Unverified Commit 53731c06 authored by Tasso Evangelista's avatar Tasso Evangelista Committed by GitHub
Browse files

feat!: CSS-in-JS for modifiers of Box (#164)

* Add prototype of useCss

* Apply useCss on Flex modifiers

* Apply useCss on Margins

* Apply useCss on Scrollable and trigger onScrollContent sparsely

* Apply useCss on AnimatedVisibility

* Refactor AnimatedVisibility

* Apply useCss on Position

* Ignore empty rules

* Rollback insertRule mechanism

* Wrap CSSStylSheet.insertRule rules into @media all

* Refactor useCss

* Delay some initializations

* Add Size modifier

* Remove unused imports

* Accept arbitrary values in Size props

* Add @rocket.chat/css-in-js

* Apply useCss into Throbber
parent da88341f
const path = require('path');
module.exports = {
extends: ['@rocket.chat/eslint-config'],
parser: 'babel-eslint',
rules: {
indent: ['error', 2],
'import/order': ['error', {
'newlines-between': 'always',
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']]
}],
},
'env': {
'jest': true,
},
};
[ignore]
[include]
[libs]
[lints]
[options]
[strict]
{
"src/**/*.js": [
"eslint --fix",
"git add"
]
}
# Welcome to @rocket.chat/css-in-js 👋
[![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://github.com/RocketChat/Rocket.Chat.Fuselage#readme)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/RocketChat/Rocket.Chat.Fuselage/graphs/commit-activity)
[![License: MIT](https://img.shields.io/github/license/RocketChat/@rocket.chat/css-in-js)](https://github.com/RocketChat/Rocket.Chat.Fuselage/blob/master/LICENSE)
[![Twitter: RocketChat](https://img.shields.io/twitter/follow/RocketChat.svg?style=social)](https://twitter.com/RocketChat)
> React Hooks for Fuselage, Rocket.Chat's design system
### 🏠 [Homepage](https://rocket.chat/Rocket.Chat.Fuselage)
## Install
```sh
yarn add @rocket.chat/css-in-js
```
## Run tests
```sh
yarn test
```
## API Reference
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
#### Table of Contents
- [css](#css)
- [Parameters](#parameters)
- [keyframes](#keyframes)
- [Parameters](#parameters-1)
- [attachRules](#attachrules)
- [Parameters](#parameters-2)
- [referenceRules](#referencerules)
- [Parameters](#parameters-3)
- [transpile](#transpile)
- [Parameters](#parameters-4)
- [createSelector](#createselector)
- [Parameters](#parameters-5)
### 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
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
### attachRules
Imediately attaches CSS rules into the style sheet.
#### Parameters
- `rules` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
Returns **any** a callback to detach the rules
### referenceRules
References CSS rules into the style sheet.
Each time this function is called with the same rules a internal reference counter for it
is incremented; when the unreference callback returned by this function is called, the reference
counter is decremented. If the counter reaches zero references, the rules are detached from
style sheet.
#### Parameters
- `rules` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
Returns **any** a callback to unreference the rules
### 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)**
### createSelector
Creates a pair of selector and escaped selector for a CSS Modules content.
#### Parameters
- `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
## Author
👤 **Rocket.Chat**
- Twitter: [@RocketChat](https://twitter.com/RocketChat)
- Github: [@RocketChat](https://github.com/RocketChat)
## 🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/RocketChat/Rocket.Chat.Fuselage/issues).
## Show your support
Give a ⭐️ if this project helped you!
* * *
_This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-flow',
],
plugins: [
'@babel/plugin-proposal-class-properties',
],
};
// flow-typed signature: e4dbf9b60e4ed33dbe6fb35b1ae31fa1
// flow-typed version: <<STUB>>/@emotion/stylis_v0.8.5/flow_v0.110.1
/**
* This is an autogenerated libdef stub for:
*
* '@emotion/stylis'
*
* Fill this stub out by replacing all the `any` types.
*
* Once filled out, we encourage you to share your work with the
* community by sending a pull request to:
* https://github.com/flowtype/flow-typed
*/
declare module '@emotion/stylis' {
declare module.exports: any;
}
/**
* We include stubs for each file inside this npm package in case you need to
* require those files directly. Feel free to delete any files that aren't
* needed.
*/
declare module '@emotion/stylis/dist/stylis.browser.cjs' {
declare module.exports: any;
}
declare module '@emotion/stylis/dist/stylis.browser.esm' {
declare module.exports: any;
}
declare module '@emotion/stylis/dist/stylis.cjs.dev' {
declare module.exports: any;
}
declare module '@emotion/stylis/dist/stylis.cjs' {
declare module.exports: any;
}
declare module '@emotion/stylis/dist/stylis.cjs.prod' {
declare module.exports: any;
}
declare module '@emotion/stylis/dist/stylis.esm' {
declare module.exports: any;
}
declare module '@emotion/stylis/src' {
declare module.exports: any;
}
declare module '@emotion/stylis/src/stylis.min' {
declare module.exports: any;
}
// Filename aliases
declare module '@emotion/stylis/dist/stylis.browser.cjs.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.browser.cjs'>;
}
declare module '@emotion/stylis/dist/stylis.browser.esm.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.browser.esm'>;
}
declare module '@emotion/stylis/dist/stylis.cjs.dev.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.cjs.dev'>;
}
declare module '@emotion/stylis/dist/stylis.cjs.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.cjs'>;
}
declare module '@emotion/stylis/dist/stylis.cjs.prod.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.cjs.prod'>;
}
declare module '@emotion/stylis/dist/stylis.esm.js' {
declare module.exports: $Exports<'@emotion/stylis/dist/stylis.esm'>;
}
declare module '@emotion/stylis/src/index' {
declare module.exports: $Exports<'@emotion/stylis/src'>;
}
declare module '@emotion/stylis/src/index.js' {
declare module.exports: $Exports<'@emotion/stylis/src'>;
}
declare module '@emotion/stylis/src/stylis.min.js' {
declare module.exports: $Exports<'@emotion/stylis/src/stylis.min'>;
}
{
"name": "@rocket.chat/css-in-js",
"version": "0.3.0",
"description": "Toolset to transpile and use CSS on runtime",
"homepage": "https://rocket.chat/Rocket.Chat.Fuselage",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/RocketChat/Rocket.Chat.Fuselage.git"
},
"bugs": {
"url": "https://github.com/RocketChat/Rocket.Chat.Fuselage/issues"
},
"keywords": [
"rocketchat",
"css-in-js"
],
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"start": "rollup -c -w",
"build": "rollup -c",
"test": "jest",
"lint": "eslint src",
"lint-staged": "lint-staged",
"docs": "documentation readme src/index.js --section='API Reference' --readme-file README.md"
},
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.6",
"@babel/preset-flow": "^7.0.0",
"@rocket.chat/eslint-config": "^0.4.0",
"babel-eslint": "^10.0.3",
"documentation": "^12.1.2",
"eslint": "^6.5.1",
"eslint-plugin-import": "^2.19.1",
"flow-bin": "^0.110.1",
"jest": "^24.9.0",
"lint-staged": "^9.4.2",
"rollup": "^1.27.9",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-peer-deps-external": "^2.2.0"
},
"peerDependencies": {},
"publishConfig": {
"access": "public"
},
"dependencies": {
"@emotion/hash": "^0.8.0",
"@emotion/stylis": "^0.8.5"
}
}
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import resolve from 'rollup-plugin-node-resolve';
import pkg from './package.json';
export default {
input: 'src/index.js',
output: [
{
file: pkg.main,
format: 'cjs',
sourcemap: true,
},
],
plugins: [
external(),
babel(),
resolve(),
commonjs(),
],
};
// @flow
export * from './tags';
export * from './sheet';
export * from './transpile';
export * from './selectors';
// @flow
import hash from '@emotion/hash';
/**
* Creates a pair of selector and escaped selector for a CSS Modules content.
*
* @returns a pair of selectors in the format `rcx-@<content hash>`; the second element is escaped
* for use in CSS content
*/
export const createSelector = (content: string): [string, string] => {
const contentHash = hash(content);
return [`rcx-@${ contentHash }`, `rcx-\\@${ contentHash }`];
};
import { createSelector } from './selectors';
describe('createSelector', () => {
it('returns a pair of selector and escaped selector', () => {
const content = 'h1 { color: red; }';
const [selector, escapedSelector] = createSelector(content);
expect(selector).toMatch(/^rcx-@.+$/);
expect(escapedSelector).toMatch(/^rcx-\\@.+$/);
expect(selector.slice(5)).toBe(escapedSelector.slice(6));
});
});
// @flow
const styleTagId = 'rcx-styles';
let styleTag;
const getStyleTag = (): HTMLStyleElement => {
if (!styleTag) {
styleTag = document.getElementById(styleTagId) || document.createElement('style');
styleTag.id = styleTagId;
styleTag.appendChild(document.createTextNode(''));
if (document.head) {
document.head.appendChild(styleTag);
}
}
/* ::
if (!(styleTag instanceof HTMLStyleElement)) {
throw new Error();
}
*/
return styleTag;
};
let styleSheet;
const getStyleSheet = (): CSSStyleSheet => {
if (!styleSheet) {
const styleTag = getStyleTag();
styleSheet = styleTag.sheet || Array.from(document.styleSheets).find(({ ownerNode }) => ownerNode === styleTag);
}
/* ::
if (!(styleSheet instanceof CSSStyleSheet)) {
throw new Error();
}
*/
return styleSheet;
};
const attachRulesIntoTag = (rules) => {
const styleTag = getStyleTag();
const textNode = document.createTextNode(rules);
styleTag.appendChild(textNode);
return () => textNode.remove();
};
const attachRulesIntoSheet = (rules) => {
const styleSheet = getStyleSheet();
const index = styleSheet.insertRule(`@media all{${ rules }}`, styleSheet.cssRules.length);
const insertedRule = styleSheet.cssRules[index];
const findPredicate = (cssRule) => cssRule === insertedRule;
return () => {
const index = Array.prototype.findIndex.call(styleSheet.cssRules, findPredicate);
styleSheet.deleteRule(index);
};
};
/**
* Imediately attaches CSS rules into the style sheet.
*
* @return a callback to detach the rules
*/
export const attachRules = (rules: string) => {
if (process.env.NODE_ENV === 'production' && !!CSSStyleSheet.prototype.insertRule) {
return attachRulesIntoSheet(rules);
}
return attachRulesIntoTag(rules);
};
const references = {};
/**
* References CSS rules into the style sheet.
*
* Each time this function is called with the same rules a internal reference counter for it
* is incremented; when the unreference callback returned by this function is called, the reference
* counter is decremented. If the counter reaches zero references, the rules are detached from
* style sheet.
*
* @return a callback to unreference the rules
*/
export const referenceRules = (rules: string) => {
if (!rules) {
return () => {};
}
const reference = { count: 0, ...references[rules] };
if (reference.count === 0) {
reference.detachRules = attachRules(rules);
}
++reference.count;
return () => {
--reference.count;
if (reference.count === 0) {
(0, reference.detachRules)();
}
};
};
// @flow
import { createSelector } from './selectors';
declare class TemplateStringsArray extends $ReadOnlyArray<string> {
+raw: string;
}
const createReplacementsMapping = (rules = []) => (value) => {
if (value === 0) {
return '0';
}
if (!value) {
return '';
}
if (typeof value === 'function') {
return value(rules);
}
return String(value);
};
/**
* Template string tag to declare CSS content chunks.
*
* @return a callback to render the CSS content
*/
export const css = (slices: TemplateStringsArray, ...values: string[]) => (rules: string[] = []) => {
const replacements = values.map(createReplacementsMapping(rules));
return String.raw(slices, ...replacements);
};
/**
* Template string tag to declare CSS `@keyframe` at-rules.
*
* @return a callback to render the CSS at-rule content
*/
export const keyframes = (slices: TemplateStringsArray, ...values: string[]) => (rules: string[] = []) => {
const replacements = values.map(createReplacementsMapping(rules));
const content = String.raw(slices, ...replacements);
const [, encodedAnimationName] = createSelector(content);
rules.push(`@keyframes ${ encodedAnimationName }{${ content }}`);
return encodedAnimationName;
};
// @flow
import Stylis from '@emotion/stylis';
let stylisInstance;
/**
* Transpiles CSS Modules content to CSS rules.
*/
export const transpile = (selector: string, content: string): string => {
if (!stylisInstance) {
stylisInstance = new Stylis();
}
return stylisInstance(selector, content);
};
import { transpile } from './transpile';
describe('transpile', () => {
it('returns CSS transpiled rules', () => {
const rules = transpile('h1', 'color: red;');
expect(rules).toBe('h1{color:red;}');
});
});
export default Object.assign({
none: 0,
auto: 'auto',
}, ...[
1,
2,
...Array.from({ length: 9 }, (_, i) => 4 * (i + 1)),
...Array.from({ length: 40 }, (_, i) => 40 * (i + 1)),
].map((value) => ({
[`x${ value }`]: `${ value / 16 }rem`,
[`neg-x${ value }`]: `${ -value / 16 }rem`,
})));
export default Object.assign({
none: '0',
full: '100%',
sw: '100vw',
sh: '100vh',
}, ...[
2,
...Array.from({ length: 9 }, (_, i) => 4 * (i + 1)),
...Array.from({ length: 40 }, (_, i) => 40 * (i + 1)),
].map((value) => ({
[`x${ value }`]: `${ value / 16 }rem`,
})));
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment