Skip to content

Commit

Permalink
perf: Generated classNames from props (#230)
Browse files Browse the repository at this point in the history

Co-authored-by: Guilherme Gazzo <[email protected]>
  • Loading branch information
tassoevan and ggazzo authored Jun 3, 2020
1 parent e7faeba commit 8df14cb
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 98 deletions.
1 change: 1 addition & 0 deletions packages/css-in-js/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
'newlines-between': 'always',
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']]
}],
'no-extra-parens': 'off'
},
'env': {
'jest': true,
Expand Down
86 changes: 55 additions & 31 deletions packages/css-in-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,33 @@ yarn test

#### Table of Contents

- [css](#css)
- [createSelector](#createselector)
- [Parameters](#parameters)
- [keyframes](#keyframes)
- [Parameters](#parameters-1)
- [attachRules](#attachrules)
- [Parameters](#parameters-2)
- [Parameters](#parameters-1)
- [referenceRules](#referencerules)
- [Parameters](#parameters-2)
- [css](#css)
- [Parameters](#parameters-3)
- [transpile](#transpile)
- [className](#classname)
- [Parameters](#parameters-4)
- [createSelector](#createselector)
- [keyframes](#keyframes)
- [Parameters](#parameters-5)
- [toClassName](#toclassname)
- [Parameters](#parameters-6)
- [transpile](#transpile)
- [Parameters](#parameters-7)

### css

Template string tag to declare CSS content chunks.

#### Parameters

- `slices` **TemplateStringsArray**
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**

Returns **any** a callback to render the CSS content

### keyframes
### createSelector

Template string tag to declare CSS `@keyframe` at-rules.
Creates a pair of selector and escaped selector for a CSS Modules content.

#### Parameters

- `slices` **TemplateStringsArray**
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**

Returns **any** a callback to render the CSS at-rule content
Returns **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]** a pair of selectors in the format `rcx-@<content hash>`; the second element is escaped
for use in CSS content

### attachRules

Expand All @@ -87,27 +80,58 @@ style sheet.

Returns **any** a callback to unreference the rules

### transpile
### css

Transpiles CSS Modules content to CSS rules.
Template string tag to declare CSS content chunks.

#### Parameters

- `selector` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `slices` **TemplateStringsArray**
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**

Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
Returns **cssFn** a callback to render the CSS content

### createSelector
### className

Creates a pair of selector and escaped selector for a CSS Modules content.
Template string tag to declare CSS content within a className.

#### Parameters

- `className` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**

Returns **any** a callback to render the CSS content

### keyframes

Template string tag to declare CSS `@keyframe` at-rules.

#### Parameters

- `slices` **TemplateStringsArray**
- `values` **...[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**

Returns **any** a callback to render the CSS at-rule content

### toClassName

Process a value as a className.

#### Parameters

- `value` **(cssFn | classNameFn | [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String))**

Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** a string containing a className or undefined

### transpile

Transpiles CSS Modules content to CSS rules.

#### Parameters

- `selector` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `content` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**

Returns **\[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)]** a pair of selectors in the format `rcx-@<content hash>`; the second element is escaped
for use in CSS content
Returns **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**

## Author

Expand Down
5 changes: 3 additions & 2 deletions packages/css-in-js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow

export * from './tags';
export * from './selectors';
export * from './sheet';
export * from './tags';
export * from './toClassName';
export * from './transpile';
export * from './selectors';
18 changes: 17 additions & 1 deletion packages/css-in-js/src/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ declare class TemplateStringsArray extends $ReadOnlyArray<string> {
+raw: string;
}

export type cssFn = (rules: string[]) => string;

export type classNameFn = {
(rules: string[]): string,
className: string,
};

const createReplacementsMapping = (rules = []) => (value) => {
if (value === 0) {
return '0';
Expand All @@ -27,11 +34,20 @@ const createReplacementsMapping = (rules = []) => (value) => {
*
* @return a callback to render the CSS content
*/
export const css = (slices: TemplateStringsArray, ...values: string[]) => (rules: string[] = []) => {
export const css = (slices: TemplateStringsArray, ...values: string[]): cssFn => (rules: string[] = []) => {
const replacements = values.map(createReplacementsMapping(rules));
return String.raw(slices, ...replacements);
};

/**
* Template string tag to declare CSS content within a className.
*
* @return a callback to render the CSS content
*/
export const className = (className: string) =>
(slices: TemplateStringsArray, ...values: string[]): classNameFn =>
Object.assign((css(slices, ...values): classNameFn), { className });

/**
* Template string tag to declare CSS `@keyframe` at-rules.
*
Expand Down
38 changes: 38 additions & 0 deletions packages/css-in-js/src/toClassName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @flow

import { createSelector } from './selectors';
import { referenceRules } from './sheet';
import { transpile } from './transpile';
import type { cssFn, classNameFn } from './tags';

/**
* Process a value as a className.
*
* @return a string containing a className or undefined
*/
export const toClassName = (value: cssFn | classNameFn | string): ?string => {
if (typeof value === 'function') {
const rules = [];
rules.push(value(rules));

const content = rules.filter(Boolean).join('') || undefined;

if (!content) {
return undefined;
}

const className = (value: classNameFn).className || createSelector(content)[0];
const encodedClassName = className.replace(/@|#|:/g, (char) => `\\${ char }`);

const parsedRules = transpile(`.${ encodedClassName }`, content);
referenceRules(parsedRules);

return className;
}

if (typeof value === 'string' && value) {
return value;
}

return undefined;
};
33 changes: 3 additions & 30 deletions packages/fuselage/src/components/Box/mergeProps.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSelector, transpile, referenceRules } from '@rocket.chat/css-in-js';
import { toClassName } from '@rocket.chat/css-in-js';

import { mapClassNames } from './mapClassNames';
import { mapSpaceProps } from '../../styles/props/spaces';
Expand Down Expand Up @@ -38,38 +38,11 @@ export const mergeProps = (props, contextProps, ref) => {
mapPositionProps,
].reduce((props, transform) => transform(props), initialProps);

const rules = [];

mergedProps.className = Array.from(
new Set(
mergedProps.className.map((className) => {
if (typeof className === 'function') {
rules.push(className(rules));
return undefined;
}

if (typeof className === 'string') {
return className;
}

return undefined;
}).filter(Boolean),
mergedProps.className.map(toClassName).filter(Boolean),
),
);

mergedProps.className = mergedProps.className.join(' ');

const content = rules.filter(Boolean).join('') || undefined;

if (!content) {
return mergedProps;
}

const [className, encodedClassName] = createSelector(content);
const parsedRules = transpile(`.rcx-box.${ encodedClassName }`, content);
referenceRules(parsedRules);

mergedProps.className += ` ${ className }`;
).join(' ');

return mergedProps;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { css } from '@rocket.chat/css-in-js';

import { memoize } from '../helpers/memoize';
import { memoize } from './memoize';

export const cssSupports = memoize((value) => typeof window !== 'undefined' && window.CSS && window.CSS.supports(value));

Expand Down Expand Up @@ -109,27 +109,3 @@ export const createLogicalProperties = ({
inlineEndProperty,
];
};

export const createPropType = (getValue) => {
const propType = (props, propName, componentName) => {
const propValue = props[propName];

if (propValue === undefined || getValue(propValue) !== undefined) {
return;
}

return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
};

propType.isRequired = (props, propName, componentName) => {
const propValue = props[propName];

if (propValue !== undefined && getValue(propValue) !== undefined) {
return;
}

return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
};

return propType;
};
23 changes: 23 additions & 0 deletions packages/fuselage/src/helpers/createPropType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const createPropType = (getValue) => {
const propType = (props, propName, componentName) => {
const propValue = props[propName];

if (propValue === undefined || getValue(propValue) !== undefined) {
return;
}

return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
};

propType.isRequired = (props, propName, componentName) => {
const propValue = props[propName];

if (propValue !== undefined && getValue(propValue) !== undefined) {
return;
}

return new Error(`Invalid value for prop \`${ propName }\` supplied to \`${ componentName }\`: \`${ propValue }\``);
};

return propType;
};
3 changes: 3 additions & 0 deletions packages/fuselage/src/helpers/cssSupports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { memoize } from './memoize';

export const cssSupports = memoize((value) => typeof window !== 'undefined' && window.CSS && window.CSS.supports(value));
3 changes: 2 additions & 1 deletion packages/fuselage/src/styles/props/borders.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { css } from '@rocket.chat/css-in-js';

import { createLogicalProperties } from '../../helpers/createLogicalProperties';
import { cssSupports } from '../../helpers/cssSupports';
import { memoize } from '../../helpers/memoize';
import { createLogicalProperties, cssSupports } from '../helpers';
import { getColorValue } from './colors';

export const [
Expand Down
4 changes: 2 additions & 2 deletions packages/fuselage/src/styles/props/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { css } from '@rocket.chat/css-in-js';
import tokenColors from '@rocket.chat/fuselage-tokens/colors';
import invariant from 'invariant';

import { createPropType } from '../../helpers/createPropType';
import { cssSupports } from '../../helpers/cssSupports';
import { memoize } from '../../helpers/memoize';
import { cssSupports, createPropType } from '../helpers';


const mapTypeToPrefix = {
neutral: 'n',
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage/src/styles/props/layout.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from '@rocket.chat/css-in-js';

import { createPropType } from '../../helpers/createPropType';
import { memoize } from '../../helpers/memoize';
import { createPropType } from '../helpers';

export const getSizeValue = memoize((propValue) => {
if (propValue === undefined || propValue === null) {
Expand Down
5 changes: 3 additions & 2 deletions packages/fuselage/src/styles/props/position.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { css } from '@rocket.chat/css-in-js';

import { createLogicalProperties } from '../../helpers/createLogicalProperties';
import { createPropType } from '../../helpers/createPropType';
import { cssSupports } from '../../helpers/cssSupports';
import { memoize } from '../../helpers/memoize';
import { cssSupports, createLogicalProperties, createPropType } from '../helpers';


export const getInsetValue = memoize((propValue) => {
if (propValue === undefined || propValue === null) {
Expand Down
4 changes: 2 additions & 2 deletions packages/fuselage/src/styles/props/spaces.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { css } from '@rocket.chat/css-in-js';

import { createLogicalProperties } from '../../helpers/createLogicalProperties';
import { createPropType } from '../../helpers/createPropType';
import { memoize } from '../../helpers/memoize';
import { createPropType, createLogicalProperties } from '../helpers';


export const getMarginValue = memoize((propValue) => {
if (propValue === undefined || propValue === null) {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuselage/src/styles/props/typography.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { css } from '@rocket.chat/css-in-js';
import tokenTypography from '@rocket.chat/fuselage-tokens/typography';
import PropTypes from 'prop-types';

import { cssSupports } from '../helpers';
import { cssSupports } from '../../helpers/cssSupports';
import { getSizeValue } from './layout';

export const getFontFamilyValue = (propValue) => {
Expand Down

0 comments on commit 8df14cb

Please sign in to comment.