Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

perf(render): use single Fela renderer #1459

Merged
merged 12 commits into from
Jun 18, 2019
13 changes: 7 additions & 6 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
"css-shorthand-expand": "^1.2.0",
"downshift": "^3.2.10",
"fast-memoize": "^2.5.1",
"fela": "^10.2.0",
"fela-plugin-fallback-value": "^10.2.0",
"fela-plugin-placeholder-prefixer": "^10.2.0",
"fela-plugin-prefixer": "^10.2.0",
"fela-plugin-rtl": "^10.2.0",
"fela": "^10.5.0",
"fela-plugin-embedded": "^10.5.0",
"fela-plugin-fallback-value": "^10.5.0",
"fela-plugin-placeholder-prefixer": "^10.5.0",
"fela-plugin-prefixer": "^10.5.0",
"fela-plugin-rtl": "^10.5.0",
"keyboard-key": "^1.0.1",
"lodash": "^4.17.11",
"mdn-polyfills": "^5.15.0",
"popper.js": "^1.15.0",
"prop-types": "^15.6.1",
"react-fela": "^10.2.0",
"react-fela": "^10.5.0",
"react-is": "^16.6.3",
"tslib": "^1.9.3"
},
Expand Down
27 changes: 4 additions & 23 deletions packages/react/src/components/Provider/Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { IStyle } from 'fela'
import { render as felaDomRender } from 'fela-dom'
import * as _ from 'lodash'
import * as PropTypes from 'prop-types'
import * as React from 'react'
// @ts-ignore
import { RendererProvider, ThemeProvider, ThemeContext } from 'react-fela'
import * as customPropTypes from '@stardust-ui/react-proptypes'

import {
felaRenderer as felaLtrRenderer,
felaRtlRenderer,
isBrowser,
ChildrenComponentProps,
} from '../../lib'
import { felaRenderer, ChildrenComponentProps } from '../../lib'
layershifter marked this conversation as resolved.
Show resolved Hide resolved

import {
ThemePrepared,
Expand Down Expand Up @@ -87,15 +81,6 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {

staticStylesRendered: boolean = false

static _topLevelFelaRenderer = undefined

get topLevelFelaRenderer() {
if (!Provider._topLevelFelaRenderer) {
Provider._topLevelFelaRenderer = this.props.rtl ? felaRtlRenderer : felaLtrRenderer
}
return Provider._topLevelFelaRenderer
}

renderStaticStyles = (mergedTheme: ThemePrepared) => {
// RTL WARNING
// This function sets static styles which are global and renderer agnostic.
layershifter marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -110,13 +95,13 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {

const renderObject = (object: StaticStyleObject) => {
_.forEach(object, (style, selector) => {
this.topLevelFelaRenderer.renderStatic(style as IStyle, selector)
felaRenderer.renderStatic(style as IStyle, selector)
})
}

staticStyles.forEach((staticStyle: StaticStyle) => {
if (typeof staticStyle === 'string') {
this.topLevelFelaRenderer.renderStatic(staticStyle)
felaRenderer.renderStatic(staticStyle)
} else if (_.isPlainObject(staticStyle)) {
renderObject(staticStyle as StaticStyleObject)
} else if (_.isFunction(staticStyle)) {
Expand Down Expand Up @@ -145,7 +130,7 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {
if (!_.isPlainObject(font)) {
throw new Error(`fontFaces must be objects, got: ${typeof font}`)
}
this.topLevelFelaRenderer.renderFont(font.name, font.paths, font.style)
felaRenderer.renderFont(font.name, font.paths, font.style)
}

fontFaces.forEach((font: FontFace) => {
Expand Down Expand Up @@ -178,10 +163,6 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {
// https://github.com/rofrischmann/fela/blob/master/docs/api/fela-dom/rehydrate.md
const outgoingContext: ProviderContextPrepared = mergeContexts(this.context, inputContext)

// Heads up!
// We should call render() to ensure that a subscription for DOM updates was created
// https://github.com/stardust-ui/react/issues/581
if (isBrowser()) felaDomRender(outgoingContext.renderer)
this.renderStaticStylesOnce(outgoingContext.theme)

const rtlProps: { dir?: 'rtl' | 'ltr' } = {}
Expand Down
40 changes: 40 additions & 0 deletions packages/react/src/lib/felaInvokeKeyframesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import callable from './callable'

/**
* Fela plugin for invoking keyframes with params. The keyframes, defined in the animationName prop,
* are called with the params object, if defined in the animationName prop.
*
* Caution! Infinite recursion is possible in case if style object has links to self in the props
* tree.
*/
export default () => {
const invokeKeyframes = (styles: Object, type?, renderer?, props?) => {
layershifter marked this conversation as resolved.
Show resolved Hide resolved
return Object.keys(styles).reduce((acc, cssPropertyName) => {
const cssPropertyValue = styles[cssPropertyName]

if (cssPropertyName === 'animationName' && typeof cssPropertyValue === 'object') {
if (cssPropertyValue.keyframe) {
styles[cssPropertyName] = callable(cssPropertyValue.keyframe)(
cssPropertyValue.params || {},
)
}

return {
...acc,
[cssPropertyName]: styles[cssPropertyName],
}
}

if (typeof cssPropertyValue === 'object') {
return {
...acc,
[cssPropertyName]: invokeKeyframes(cssPropertyValue, type, renderer, props),
}
}

return { ...acc, [cssPropertyName]: styles[cssPropertyName] }
}, {})
}

return invokeKeyframes
}
49 changes: 0 additions & 49 deletions packages/react/src/lib/felaRenderKeyframesPlugin.ts

This file was deleted.

37 changes: 17 additions & 20 deletions packages/react/src/lib/felaRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { createRenderer } from 'fela'
import felaSanitizeCss from './felaSanitizeCssPlugin'
import felaExpandCssShorthandsPlugin from './felaExpandCssShorthandsPlugin'
import felaPluginEmbedded from 'fela-plugin-embedded'
import felaPluginFallbackValue from 'fela-plugin-fallback-value'
import felaPluginPlaceholderPrefixer from 'fela-plugin-placeholder-prefixer'
import felaPluginPrefixer from 'fela-plugin-prefixer'
import felaDisableAnimationsPlugin from './felaDisableAnimationsPlugin'
import rtl from 'fela-plugin-rtl'
import felaPluginRtl from 'fela-plugin-rtl'

import { Renderer } from '../themes/types'
import felaRenderKeyframesPlugin from './felaRenderKeyframesPlugin'
import felaDisableAnimationsPlugin from './felaDisableAnimationsPlugin'
import felaExpandCssShorthandsPlugin from './felaExpandCssShorthandsPlugin'
import felaInvokeKeyframesPlugin from './felaInvokeKeyframesPlugin'
import felaSanitizeCss from './felaSanitizeCssPlugin'

let felaDevMode = false

Expand Down Expand Up @@ -46,8 +47,10 @@ const blacklistedClassNames = ['fa', 'fas', 'far', 'fal', 'fab']
const filterClassName = (className: string): boolean =>
className.indexOf('ad') === -1 && blacklistedClassNames.indexOf(className) === -1

const createRendererConfig = (options: any = {}) => ({
const rendererConfig = {
devMode: felaDevMode,
filterClassName,
enhancers: [],
plugins: [
// is necessary to prevent accidental style typos
// from breaking ALL the styles on the page
Expand All @@ -61,22 +64,16 @@ const createRendererConfig = (options: any = {}) => ({
// Heads up!
// This is required after fela-plugin-prefixer to resolve the array of fallback values prefixer produces.
felaPluginFallbackValue(),

layershifter marked this conversation as resolved.
Show resolved Hide resolved
felaExpandCssShorthandsPlugin(),
felaDisableAnimationsPlugin(),
felaRenderKeyframesPlugin(),
...(options.isRtl ? [rtl()] : []),
felaInvokeKeyframesPlugin(),
felaPluginEmbedded(),

felaPluginRtl(),
],
filterClassName,
enhancers: [],
...(options.isRtl ? { selectorPrefix: 'rtl_' } : {}),
...(options.rendererId ? { rendererId: options.rendererId } : {}),
})
}

const felaRenderer: Renderer = createRenderer(rendererConfig)

// TODO: { rendererId: 'default' } is a temporary workaround for https://github.com/stardust-ui/react/issues/948#issuecomment-466404630
export const felaRenderer: Renderer = createRenderer(
createRendererConfig({ rendererId: 'default' }),
)
export const felaRtlRenderer: Renderer = createRenderer(
createRendererConfig({ isRtl: true, rendererId: 'rtl' }),
)
export default felaRenderer
10 changes: 9 additions & 1 deletion packages/react/src/lib/getClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ const getClasses = (
// root, icon, etc.
const componentParts: string[] = Object.keys(componentStyles)

// Fela plugins rely on `direction` param in `theme` prop instead of RTL
// Our API should be aligned with it
const direction = styleParam.rtl ? 'rtl' : 'ltr'
const mergedStyleParam = {
...styleParam,
theme: { ...styleParam.theme, direction },
}

return componentParts.reduce((classes, partName) => {
classes[partName] = renderer.renderRule(callable(componentStyles[partName]), styleParam)
classes[partName] = renderer.renderRule(callable(componentStyles[partName]), mergedStyleParam)

return classes
}, {})
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export { default as applyAccessibilityKeyHandlers } from './applyAccessibilityKe
export { default as AutoControlledComponent } from './AutoControlledComponent'
export { default as childrenExist } from './childrenExist'
export { default as UIComponent } from './UIComponent'
export { felaRenderer, felaRtlRenderer } from './felaRenderer'
export { default as felaRenderer } from './felaRenderer'
export { default as toCompactArray } from './toCompactArray'
export { default as rtlTextContainer } from './rtlTextContainer'
export { default as stringLiteralsArray } from './stringLiteralsArray'
Expand Down
8 changes: 4 additions & 4 deletions packages/react/src/lib/mergeProviderContexts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { felaRenderer, felaRtlRenderer } from './felaRenderer'
import felaRenderer from './felaRenderer'
import { ProviderContextPrepared, ProviderContextInput } from '../types'
import mergeThemes from './mergeThemes'

Expand All @@ -19,7 +19,7 @@ const mergeProviderContexts = (...contexts: ProviderContextInput[]): ProviderCon
icons: {},
animations: {},
},
renderer: {},
renderer: felaRenderer,
rtl: false,
disableAnimations: false,
} as ProviderContextPrepared
Expand All @@ -36,8 +36,8 @@ const mergeProviderContexts = (...contexts: ProviderContextInput[]): ProviderCon
acc.rtl = mergedRTL
}

// Use the correct renderer for RTL
acc.renderer = acc.rtl ? felaRtlRenderer : felaRenderer
// Use provided renderer if it is defined
acc.renderer = next.renderer || acc.renderer

// Latest disableAnimations value wins
const mergedDisableAnimations = mergeBooleanValues(
Expand Down
21 changes: 10 additions & 11 deletions packages/react/src/themes/base/components/Loader/loaderStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FlexDirectionProperty } from 'csstype'

import { pxToRem } from '../../../../lib'
import { LoaderProps } from '../../../../components/Loader/Loader'
import { ComponentStyleFunctionParam, ICSSInJSStyle } from '../../../types'
import { ComponentStyleFunctionParam, ICSSInJSStyle, StardustAnimationName } from '../../../types'
import { ObjectOf } from '../../../../types'
import { LoaderVariables } from './loaderVariables'

Expand All @@ -27,16 +27,15 @@ export default {
theme: t,
variables: v,
}: ComponentStyleFunctionParam<LoaderProps, LoaderVariables>): ICSSInJSStyle => {
const animationName = {
keyframe: ({ from, to }) =>
({
from: {
transform: `rotate(${from})`,
},
to: {
transform: `rotate(${to}})`,
},
} as any),
const animationName: StardustAnimationName = {
keyframe: ({ from, to }) => ({
from: {
transform: `rotate(${from})`,
},
to: {
transform: `rotate(${to})`,
},
}),
params: {
from: '0deg',
to: '360deg',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ export default {
}: ComponentStyleFunctionParam<LoaderProps, LoaderVariables>) => {
const outerAnimation: ICSSInJSStyle = {
animationName: {
keyframe: () =>
({
to: {
opacity: 1,
},
} as any),
to: {
opacity: 1,
},
},
animationDelay: '1.5s',
animationDirection: 'normal',
Expand All @@ -48,12 +45,9 @@ export default {
}
const svgAnimation: ICSSInJSStyle = {
animationName: {
keyframe: () =>
({
to: {
transform: `translate3d(0, ${v.svgTranslatePosition[p.size]}, 0)`,
},
} as any),
to: {
layershifter marked this conversation as resolved.
Show resolved Hide resolved
transform: `translate3d(0, ${v.svgTranslatePosition[p.size]}, 0)`,
},
},
animationDelay: '0s',
animationDirection: 'normal',
Expand Down
10 changes: 6 additions & 4 deletions packages/react/src/themes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,17 @@ export interface ICSSPseudoElementStyle extends ICSSInJSStyle {
content?: string
}

export interface StardustAnimationName {
keyframe?: any
params?: object
type AnimationKeyFrame = Record<'from' | 'to' | string, ICSSInJSStyle>

export interface StardustAnimationName<P = Record<string, any>> {
keyframe?: AnimationKeyFrame | ((params: P) => AnimationKeyFrame)
params?: P
}

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

export type CSSProperties = Omit<React.CSSProperties, 'animationName'> & {
animationName?: StardustAnimationName | string | 'none'
animationName?: StardustAnimationName | AnimationKeyFrame | string | 'none'
}

export interface ICSSInJSStyle extends CSSProperties {
Expand Down
Loading