diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd2e43cab..8a4c4bb2ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### BREAKING CHANGES - Rename `flipInRtl` Icon's `slot` to `svgFlippingInRtl` in Teams theme @mnajdova ([#1179](https://github.com/stardust-ui/react/pull/1179)) +### Performance +- Drop usages of `FelaTheme` component and use `React.Context` to get `theme` directly @layershifter ([#1163](https://github.com/stardust-ui/react/pull/1163)) + ### Features - Add `Reaction` variables to Teams dark and HOC themes @mnajdova ([#1152](https://github.com/stardust-ui/react/pull/1152)) - Move `Grid`'s and `Image`'s styles and variables from Teams to base theme @mnajdova ([#1182](https://github.com/stardust-ui/react/pull/1182)) diff --git a/packages/react/src/lib/UIComponent.tsx b/packages/react/src/lib/UIComponent.tsx index cc48d6b162..5f110d99f7 100644 --- a/packages/react/src/lib/UIComponent.tsx +++ b/packages/react/src/lib/UIComponent.tsx @@ -1,5 +1,8 @@ import * as React from 'react' import * as _ from 'lodash' +// @ts-ignore We have this export in package, but it is not present in typings +import { ThemeContext } from 'react-fela' + import renderComponent, { RenderResultConfig } from './renderComponent' import { AccessibilityActionHandlers } from './accessibility/types' import { FocusZone } from './accessibility/FocusZone' @@ -11,6 +14,7 @@ class UIComponent extends React.Component { static displayName: string static className: string + static contextType = ThemeContext static propTypes: any /** Array of props to exclude from list of handled ones. */ @@ -47,17 +51,20 @@ class UIComponent extends React.Component { } render() { - return renderComponent({ - className: this.childClass.className, - defaultProps: this.childClass.defaultProps, - displayName: this.childClass.displayName, - handledProps: this.childClass.handledProps, - props: this.props, - state: this.state, - actionHandlers: this.actionHandlers, - focusZoneRef: this.setFocusZoneRef, - render: this.renderComponent, - }) + return renderComponent( + { + className: this.childClass.className, + defaultProps: this.childClass.defaultProps, + displayName: this.childClass.displayName, + handledProps: this.childClass.handledProps, + props: this.props, + state: this.state, + actionHandlers: this.actionHandlers, + focusZoneRef: this.setFocusZoneRef, + render: this.renderComponent, + }, + this.context, + ) } private setFocusZoneRef = (focusZone: FocusZone): void => { diff --git a/packages/react/src/lib/createComponent.tsx b/packages/react/src/lib/createComponent.tsx index 3b73670264..7f02e1dcc0 100644 --- a/packages/react/src/lib/createComponent.tsx +++ b/packages/react/src/lib/createComponent.tsx @@ -1,11 +1,14 @@ import * as React from 'react' import * as _ from 'lodash' +// @ts-ignore +import { ThemeContext } from 'react-fela' import renderComponent, { RenderResultConfig } from './renderComponent' import { AccessibilityActionHandlers } from './accessibility/types' import { FocusZone } from './accessibility/FocusZone' import { createShorthandFactory } from './factories' import { ObjectOf } from '../types' +import { ThemePrepared } from '../themes/types' export interface CreateComponentConfig

{ displayName: string @@ -40,17 +43,22 @@ const createComponent =

= any>({ } const StardustComponent: CreateComponentReturnType

= (props): React.ReactElement

=> { - return renderComponent({ - className, - defaultProps, - displayName, - handledProps: _.keys(propTypes).concat(handledProps), - props, - state: {}, - actionHandlers, - focusZoneRef, - render: config => render(config, props), - }) + const theme: ThemePrepared = React.useContext(ThemeContext) + + return renderComponent( + { + className, + defaultProps, + displayName, + handledProps: _.keys(propTypes).concat(handledProps), + props, + state: {}, + actionHandlers, + focusZoneRef, + render: config => render(config, props), + }, + theme, + ) } StardustComponent.create = createShorthandFactory({ diff --git a/packages/react/src/lib/renderComponent.tsx b/packages/react/src/lib/renderComponent.tsx index bf9b229546..5265e3a3ad 100644 --- a/packages/react/src/lib/renderComponent.tsx +++ b/packages/react/src/lib/renderComponent.tsx @@ -1,7 +1,6 @@ import cx from 'classnames' import * as React from 'react' import * as _ from 'lodash' -import { FelaTheme } from 'react-fela' import callable from './callable' import felaRenderer from './felaRenderer' @@ -131,7 +130,10 @@ const renderWithFocusZone =

( return render(config) } -const renderComponent =

(config: RenderConfig

): React.ReactElement

=> { +const renderComponent =

( + config: RenderConfig

, + theme: ThemePrepared, +): React.ReactElement

=> { const { className, defaultProps, @@ -144,98 +146,86 @@ const renderComponent =

(config: RenderConfig

): React.ReactElem render, } = config - return ( - - {(theme: ThemePrepared) => { - if (_.isEmpty(theme)) { - logProviderMissingWarning() - } - - const { - siteVariables = { - colorScheme: {}, - colors: {}, - contextualColors: {}, - emphasisColors: {}, - naturalColors: {}, - fontSizes: {}, - }, - componentVariables = {}, - componentStyles = {}, - rtl = false, - renderer = felaRenderer, - } = theme || {} - const ElementType = getElementType({ defaultProps }, props) as React.ReactType

- - const stateAndProps = { ...state, ...props } - - // Resolve variables for this component, allow props.variables to override - const resolvedVariables: ComponentVariablesObject = mergeComponentVariables( - componentVariables[displayName], - props.variables, - )(siteVariables) - - const animationCSSProp = props.animation - ? createAnimationStyles(props.animation, theme) - : {} - - // Resolve styles using resolved variables, merge results, allow props.styles to override - const mergedStyles: ComponentSlotStylesPrepared = mergeComponentStyles( - componentStyles[displayName], - { - root: props.styles, - }, - ) - - const accessibility: AccessibilityBehavior = getAccessibility( - stateAndProps, - actionHandlers, - rtl, - ) - - const unhandledProps = getUnhandledProps({ handledProps }, props) - - const colors = generateColorScheme(stateAndProps.color, resolvedVariables.colorScheme) - - const styleParam: ComponentStyleFunctionParam = { - props: stateAndProps, - variables: resolvedVariables, - theme, - colors, - } - - mergedStyles.root = { - ...callable(mergedStyles.root)(styleParam), - ...animationCSSProp, - } - - const resolvedStyles: ComponentSlotStylesPrepared = Object.keys(mergedStyles).reduce( - (acc, next) => ({ ...acc, [next]: callable(mergedStyles[next])(styleParam) }), - {}, - ) - - const classes: ComponentSlotClasses = getClasses(renderer, mergedStyles, styleParam) - classes.root = cx(className, classes.root, props.className) - - const config: RenderResultConfig

= { - ElementType, - unhandledProps, - classes, - variables: resolvedVariables, - styles: resolvedStyles, - accessibility, - rtl, - theme, - } - - if (accessibility.focusZone) { - return renderWithFocusZone(render, accessibility.focusZone, config, focusZoneRef) - } - - return render(config) - }} - + if (_.isEmpty(theme)) { + logProviderMissingWarning() + } + + const { + siteVariables = { + colorScheme: {}, + colors: {}, + contextualColors: {}, + emphasisColors: {}, + naturalColors: {}, + fontSizes: {}, + }, + componentVariables = {}, + componentStyles = {}, + rtl = false, + renderer = felaRenderer, + } = theme || {} + const ElementType = getElementType({ defaultProps }, props) as React.ReactType

+ + const stateAndProps = { ...state, ...props } + + // Resolve variables for this component, allow props.variables to override + const resolvedVariables: ComponentVariablesObject = mergeComponentVariables( + componentVariables[displayName], + props.variables, + )(siteVariables) + + const animationCSSProp = props.animation ? createAnimationStyles(props.animation, theme) : {} + + // Resolve styles using resolved variables, merge results, allow props.styles to override + const mergedStyles: ComponentSlotStylesPrepared = mergeComponentStyles( + componentStyles[displayName], + { + root: props.styles, + }, + ) + + const accessibility: AccessibilityBehavior = getAccessibility(stateAndProps, actionHandlers, rtl) + + const unhandledProps = getUnhandledProps({ handledProps }, props) + + const colors = generateColorScheme(stateAndProps.color, resolvedVariables.colorScheme) + + const styleParam: ComponentStyleFunctionParam = { + props: stateAndProps, + variables: resolvedVariables, + theme, + colors, + } + + mergedStyles.root = { + ...callable(mergedStyles.root)(styleParam), + ...animationCSSProp, + } + + const resolvedStyles: ComponentSlotStylesPrepared = Object.keys(mergedStyles).reduce( + (acc, next) => ({ ...acc, [next]: callable(mergedStyles[next])(styleParam) }), + {}, ) + + const classes: ComponentSlotClasses = getClasses(renderer, mergedStyles, styleParam) + classes.root = cx(className, classes.root, props.className) + + const resolvedConfig: RenderResultConfig

= { + ElementType, + unhandledProps, + classes, + variables: resolvedVariables, + styles: resolvedStyles, + accessibility, + rtl, + theme, + } + + if (accessibility.focusZone) { + return renderWithFocusZone(render, accessibility.focusZone, resolvedConfig, focusZoneRef) + } + + return render(resolvedConfig) } export default renderComponent