@workday/canvas-kit-styling 10.0.0-alpha.538-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +51 -0
- package/README.md +5 -0
- package/dist/commonjs/index.d.ts +3 -0
- package/dist/commonjs/index.d.ts.map +1 -0
- package/dist/commonjs/index.js +14 -0
- package/dist/commonjs/lib/cs.d.ts +216 -0
- package/dist/commonjs/lib/cs.d.ts.map +1 -0
- package/dist/commonjs/lib/cs.js +255 -0
- package/dist/commonjs/lib/slugify.d.ts +5 -0
- package/dist/commonjs/lib/slugify.d.ts.map +1 -0
- package/dist/commonjs/lib/slugify.js +18 -0
- package/dist/commonjs/lib/uniqueId.d.ts +30 -0
- package/dist/commonjs/lib/uniqueId.d.ts.map +1 -0
- package/dist/commonjs/lib/uniqueId.js +46 -0
- package/dist/es6/index.d.ts +3 -0
- package/dist/es6/index.d.ts.map +1 -0
- package/dist/es6/index.js +2 -0
- package/dist/es6/lib/cs.d.ts +216 -0
- package/dist/es6/lib/cs.d.ts.map +1 -0
- package/dist/es6/lib/cs.js +246 -0
- package/dist/es6/lib/slugify.d.ts +5 -0
- package/dist/es6/lib/slugify.d.ts.map +1 -0
- package/dist/es6/lib/slugify.js +14 -0
- package/dist/es6/lib/uniqueId.d.ts +30 -0
- package/dist/es6/lib/uniqueId.d.ts.map +1 -0
- package/dist/es6/lib/uniqueId.js +40 -0
- package/index.ts +2 -0
- package/package.json +55 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resetUniqueIdCount = exports.setUniqueSeed = exports.generateUniqueId = void 0;
|
|
4
|
+
// Create a unique seed per import to prevent collisions from other versions of `useUniqueId`
|
|
5
|
+
let seed = Math.random()
|
|
6
|
+
.toString(36)
|
|
7
|
+
.slice(2)
|
|
8
|
+
.replace(/[0-9]*/, '')
|
|
9
|
+
.substr(0, 4);
|
|
10
|
+
let c = 0;
|
|
11
|
+
/**
|
|
12
|
+
* Generates a unique and HTML5 compliant identifier every time it is called. Internally it uses a 4
|
|
13
|
+
* character random seed starting with a letter. This seed is unique to each instance of this
|
|
14
|
+
* package meaning different versions of Canvas Kit on the page will have a different seed. Each
|
|
15
|
+
* call will use a Base 36 string (10 numbers + 26 letters) based on an incremented number. The
|
|
16
|
+
* incremented number always starts at 0 and can be reset for testing purposes using
|
|
17
|
+
* [resetUniqueIdCount](#resetuniqueidcount). [setUniqueSeed](#setuniqueseed) can also be used for
|
|
18
|
+
* testing or server side rendering to get the same results during hydration.
|
|
19
|
+
*/
|
|
20
|
+
const generateUniqueId = () => seed + (c++).toString(36);
|
|
21
|
+
exports.generateUniqueId = generateUniqueId;
|
|
22
|
+
/**
|
|
23
|
+
* Update the seed used by the id generator. This is useful for snapshot tests to help stabilize ids
|
|
24
|
+
* generated each run. This could also be used for server-side hydration - if you choose the same
|
|
25
|
+
* seed for server and set that on the client before components are rendered, the ids generated will
|
|
26
|
+
* be the same.
|
|
27
|
+
* @example
|
|
28
|
+
* // set in a script tag from the server
|
|
29
|
+
* setSeed(window.__ID_SEED); // set in a script tag from the server
|
|
30
|
+
*
|
|
31
|
+
* // jest setup
|
|
32
|
+
* before(() => {
|
|
33
|
+
* setSeed('a')
|
|
34
|
+
* })
|
|
35
|
+
*/
|
|
36
|
+
const setUniqueSeed = (s) => {
|
|
37
|
+
seed = s;
|
|
38
|
+
};
|
|
39
|
+
exports.setUniqueSeed = setUniqueSeed;
|
|
40
|
+
/**
|
|
41
|
+
* This should only be called for tests in an `beforeEach`
|
|
42
|
+
*/
|
|
43
|
+
const resetUniqueIdCount = () => {
|
|
44
|
+
c = 0;
|
|
45
|
+
};
|
|
46
|
+
exports.resetUniqueIdCount = resetUniqueIdCount;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Keyframes, SerializedStyles, CSSObject } from '@emotion/serialize';
|
|
3
|
+
/**
|
|
4
|
+
* Style properties in a JavaScript camelCase. Everything Emotion allows is also
|
|
5
|
+
* allowed here.
|
|
6
|
+
*/
|
|
7
|
+
export declare type StyleProps = undefined | boolean | number | string | Keyframes | SerializedStyles | CSSObject;
|
|
8
|
+
export declare type CS = string | Record<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* CSS variable map type. In developer/dynamic mode, we don't know what the hash is going to be. All
|
|
11
|
+
* variables will look like `--{hash}-{name}`. But the static optimizers generates the name based on
|
|
12
|
+
* the AST, so the `id` will be known. Instead of something like `--abc123-color`, the `ID` is set
|
|
13
|
+
* by the optimizer.
|
|
14
|
+
*
|
|
15
|
+
* For example:
|
|
16
|
+
* ```ts
|
|
17
|
+
* // dynamic
|
|
18
|
+
* const myVars = createVars('color') // type is `Record<'color', string>`
|
|
19
|
+
*
|
|
20
|
+
* // optimizer rewrites the code
|
|
21
|
+
* const myVars = createVars<'color', 'myVars'>('color')
|
|
22
|
+
* // type is now `{color: "--myVars-color"}`
|
|
23
|
+
*
|
|
24
|
+
* myVars.color // type is `--myVars-color`
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* This is so optimized variables can be used directly by the static parser downstream. The variable
|
|
28
|
+
* names become statically analyzable.
|
|
29
|
+
*/
|
|
30
|
+
export declare type CsVarsMap<T extends string, ID extends string | never> = [ID] extends [never] ? Record<T, string> : {
|
|
31
|
+
[K in T]: `--${ID}-${K}`;
|
|
32
|
+
};
|
|
33
|
+
export declare type CsVars<T extends string, ID extends string | never> = CsVarsMap<T, ID> & {
|
|
34
|
+
(input: Partial<Record<T, string>>): Record<T, string>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Take a CSS Variable name and return a variable property
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* const myVars = createVars('color')
|
|
41
|
+
*
|
|
42
|
+
* const myStyles = createStyles({
|
|
43
|
+
* color: cssVar(myVars.color) // color: 'var(--{hash}-color)'
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* It can also support an optional fallback. Fallbacks should only be used if it is reasonable to
|
|
48
|
+
* expect a CSS variable isn't defined.
|
|
49
|
+
* ```ts
|
|
50
|
+
* const myStyles = createStyles({
|
|
51
|
+
* color: cssVar(myVars.color, 'red') // color: 'var(--{hash}-color, red)'
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* If the project is set up for parsing with fallback files, a fallback will automatically be filled
|
|
56
|
+
* during the parsing phase. This is helpful for cases when CSS variables are expected, but not set
|
|
57
|
+
* in the environment.
|
|
58
|
+
*/
|
|
59
|
+
export declare function cssVar(input: string, fallback?: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Create temporary CSS variables to use in components. The CSS variable names will
|
|
62
|
+
* be unique at runtime to avoid collisions. The return value is a function and a
|
|
63
|
+
* Map. The function can be used to pass in values from JavaScript. The function will
|
|
64
|
+
* return a map of variable keys to CSS Variable names.
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* // creates a `color` and `background` CSS variable
|
|
68
|
+
* const myVars = createVars('color', 'background')
|
|
69
|
+
*
|
|
70
|
+
* // 'color' is a typed property. The type is `string`
|
|
71
|
+
* console.log(myVars.color) // `'var(--{hash}-color)'`
|
|
72
|
+
*
|
|
73
|
+
* // 'color' is a typed property. The type is `string?`
|
|
74
|
+
* // The returned object can be assigned to the `style` property of an element
|
|
75
|
+
* console.log(myVars({ color: 'red' })) // `{'--{hash}-color': 'red'}`
|
|
76
|
+
*
|
|
77
|
+
* const div = document.createElement('div')
|
|
78
|
+
* div.style = myVars({ color: 'red' }) // <div style="--{hash}-color: red;" />
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function createVars<T extends string, ID extends string>(input: {
|
|
82
|
+
id: ID;
|
|
83
|
+
args: T[];
|
|
84
|
+
}): CsVars<T, ID>;
|
|
85
|
+
export declare function createVars<T extends string>(...args: T[]): CsVars<T, never>;
|
|
86
|
+
declare type ModifierConfig = Record<string, Record<string, CS>>;
|
|
87
|
+
declare type ModifierValues<T extends ModifierConfig> = {
|
|
88
|
+
[P in keyof T]: keyof T[P];
|
|
89
|
+
};
|
|
90
|
+
declare type ModifierReturn<T extends ModifierConfig> = T & ((modifiers: Partial<ModifierValues<T>>) => string);
|
|
91
|
+
/**
|
|
92
|
+
* Creates a modifier function that takes in a modifier config and will return a CSS class name that
|
|
93
|
+
* matches the result. Modifiers can be thought as `if` or `switch` statements when conditionally
|
|
94
|
+
* changing the styles of a component based on props. This function can be thought of as a helper
|
|
95
|
+
* function that makes it easier to work with modifiers. Without it, you would have to implement
|
|
96
|
+
* if/switch/ternary for each option.
|
|
97
|
+
*
|
|
98
|
+
* ```tsx
|
|
99
|
+
* const myModifiers = createModifiers({
|
|
100
|
+
* // a modifier called 'size'
|
|
101
|
+
* size: {
|
|
102
|
+
* small: createStyles({ fontSize: 12 }),
|
|
103
|
+
* medium: createStyles({ fontSize: 14 })
|
|
104
|
+
* }
|
|
105
|
+
* })
|
|
106
|
+
*
|
|
107
|
+
* // with the modifier function
|
|
108
|
+
* myModifiers({ size: 'medium' }) // returns the medium class name
|
|
109
|
+
*
|
|
110
|
+
* // manually without the function
|
|
111
|
+
* size === 'small' ? myModifiers.size.small : size === 'medium' ? myModifiers.size.medium : ''
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function createModifiers<T extends ModifierConfig>(input: T): ModifierReturn<T>;
|
|
115
|
+
/**
|
|
116
|
+
* All acceptable values of the `cs` prop. It can be a CSS class name, any CSS properties, an object
|
|
117
|
+
* with a `className` and `styles`, or an array of these
|
|
118
|
+
*/
|
|
119
|
+
export declare type CSToPropsInput = undefined | CS | CsToPropsReturn | React.CSSProperties | CSToPropsInput[];
|
|
120
|
+
export declare type CsToPropsReturn = {
|
|
121
|
+
className?: string;
|
|
122
|
+
style?: React.CSSProperties;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* A function that takes in a single input, or an array. The type of the input is either:
|
|
126
|
+
*
|
|
127
|
+
* - `string` - it represents a CSS class name
|
|
128
|
+
* - `undefined` - there is no value. This is provided for convenience for developers to not have to
|
|
129
|
+
* filter out undefined
|
|
130
|
+
* - `{--{hash}-{varName}: {value}}` - a `Map` of CSS variable to values
|
|
131
|
+
* - `{style: ..., className: ...}` an object already returned by another `csToProps` function call
|
|
132
|
+
* (for nesting)
|
|
133
|
+
* - An array containing any of the above. This will recurse over each entry to produce a single,
|
|
134
|
+
* reduced `{style: ..., className: ...}` object
|
|
135
|
+
* @param input
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
export declare function csToProps(input: CSToPropsInput): CsToPropsReturn;
|
|
139
|
+
export interface CSProps {
|
|
140
|
+
/** The `cs` prop takes in a single value or an array of values. You can pass the CSS class name
|
|
141
|
+
* returned by {@link createStyles}, or the result of {@link createVars} and
|
|
142
|
+
* {@link createModifiers}. If you're extending a component already using `cs`, you can merge that
|
|
143
|
+
* prop in as well.
|
|
144
|
+
*
|
|
145
|
+
* ```tsx
|
|
146
|
+
* cs={[
|
|
147
|
+
* cs, // from the prop list
|
|
148
|
+
* myStyles,
|
|
149
|
+
* myModifiers({ size: 'medium' }),
|
|
150
|
+
* myVars({ backgroundColor: 'red' })
|
|
151
|
+
* ]}
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
cs?: CSToPropsInput;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Creates CSS styles based on object-style input. It has a side-effect of adding CSS to the page
|
|
158
|
+
* and will return a space-delimitated string of CSS class names meant to be added to an element.
|
|
159
|
+
*
|
|
160
|
+
* It can take a number of inputs of various types. The simplest is object-styles.
|
|
161
|
+
*
|
|
162
|
+
* ```ts
|
|
163
|
+
* const myStyles = createStyles({
|
|
164
|
+
* backgroundColor: 'red'
|
|
165
|
+
* })
|
|
166
|
+
* ```
|
|
167
|
+
*
|
|
168
|
+
* The `createStyles` function is curried into 2 parts. The first function could be done at build
|
|
169
|
+
* time. The returned function combines CSS class names and will remain as a small runtime.
|
|
170
|
+
*
|
|
171
|
+
* > **Note:** The order of calling `createStyles` is important. Each call will make a single CSS
|
|
172
|
+
* > class selector and will be injected into the document's
|
|
173
|
+
* > [StyleSheetList](https://developer.mozilla.org/en-US/docs/Web/API/StyleSheetList). Style
|
|
174
|
+
* > properties will be merge by the rules of [CSS
|
|
175
|
+
* > specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). If two selectors
|
|
176
|
+
* > have the same specificity, the last defined wins. Always make sure that the properties you want
|
|
177
|
+
* > to win are last in your file.
|
|
178
|
+
*/
|
|
179
|
+
export declare function createStyles(...args: (StyleProps | string)[]): string;
|
|
180
|
+
/**
|
|
181
|
+
* React utility function to apply CSS class names and dynamic CSS variables
|
|
182
|
+
*
|
|
183
|
+
* ```tsx
|
|
184
|
+
* const MyComponent = (props: any) => {
|
|
185
|
+
* return <div {...handleCsProp(props)} />
|
|
186
|
+
* }
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
* It will return an object with `className` and `style` attributes. If a `className` is provided to
|
|
190
|
+
* the component, it will merge the class names. If a `style` is provided to the component, it will
|
|
191
|
+
* merge the styles.
|
|
192
|
+
*
|
|
193
|
+
* ```tsx
|
|
194
|
+
* const vars = createVars('background')
|
|
195
|
+
* const styles = createStyles({
|
|
196
|
+
* color: vars.color
|
|
197
|
+
* })
|
|
198
|
+
* <MyComponent
|
|
199
|
+
* className="foobar"
|
|
200
|
+
* style={{ padding: 10 }}
|
|
201
|
+
* cs={styles}
|
|
202
|
+
* />
|
|
203
|
+
*
|
|
204
|
+
* // Output
|
|
205
|
+
* <div
|
|
206
|
+
* className="foobar {hashedClassName}"
|
|
207
|
+
* style="padding: 10px; --{hash}-background: red;"
|
|
208
|
+
* />
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export declare function handleCsProp<T extends CSProps & {
|
|
212
|
+
className?: string | undefined;
|
|
213
|
+
style?: React.CSSProperties | undefined;
|
|
214
|
+
}>({ cs, style, className, ...props }: T): Omit<T, "cs">;
|
|
215
|
+
export {};
|
|
216
|
+
//# sourceMappingURL=cs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs.d.ts","sourceRoot":"","sources":["../../../lib/cs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAkB,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAI3F;;;GAGG;AACH,oBAAY,UAAU,GAClB,SAAS,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,GACT,gBAAgB,GAChB,SAAS,CAAC;AA4Bd,oBAAY,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,oBAAY,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,GACrF,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAEjB;KAAE,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,EAAE;CAAC,CAAC;AAE/B,oBAAY,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,GAAG,KAAK,IAAI,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG;IACnF,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;CACxD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,UAItD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,EAAE,KAAK,EAAE;IACrE,EAAE,EAAE,EAAE,CAAC;IACP,IAAI,EAAE,CAAC,EAAE,CAAC;CACX,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAwB7E,aAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;AAEzD,aAAK,cAAc,CAAC,CAAC,SAAS,cAAc,IAAI;KAC7C,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;CAC3B,CAAC;AAEF,aAAK,cAAc,CAAC,CAAC,SAAS,cAAc,IAAI,CAAC,GAC/C,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,cAAc,EAAE,KAAK,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAcrF;AAMD;;;GAGG;AACH,oBAAY,cAAc,GACtB,SAAS,GACT,EAAE,GACF,eAAe,GACf,KAAK,CAAC,aAAa,GACnB,cAAc,EAAE,CAAC;AAErB,oBAAY,eAAe,GAAG;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;CAAC,CAAC;AAChF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA8BhE;AAED,MAAM,WAAW,OAAO;IACtB;;;;;;;;;;;;;OAaG;IACH,EAAE,CAAC,EAAE,cAAc,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,GAAG,IAAI,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,GAAG,MAAM,CA+BrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,OAAO,GAAG;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/B,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC;CACzC,EACD,EAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAC,EAAE,CAAC,iBAKpC"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { generateUniqueId } from './uniqueId';
|
|
2
|
+
// eslint-disable-next-line @emotion/no-vanilla
|
|
3
|
+
import { css } from '@emotion/css';
|
|
4
|
+
import { serializeStyles } from '@emotion/serialize';
|
|
5
|
+
import { slugify } from './slugify';
|
|
6
|
+
function convertProperty(value) {
|
|
7
|
+
// Handle the case where the value is a variable without the `var()` wrapping function. It happens
|
|
8
|
+
// enough that it makes sense to automatically wrap.
|
|
9
|
+
if (typeof value === 'string' && value.startsWith('--')) {
|
|
10
|
+
return `var(${value})`;
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Walks through all the properties and values of a style and converts properties and/or values that
|
|
16
|
+
* need special processing. An example might be using a CSS variable without a `var()` wrapping.
|
|
17
|
+
*/
|
|
18
|
+
function convertAllProperties(obj) {
|
|
19
|
+
if (typeof obj === 'object') {
|
|
20
|
+
const converted = {};
|
|
21
|
+
for (const key in obj) {
|
|
22
|
+
if (obj.hasOwnProperty(key)) {
|
|
23
|
+
converted[key] = convertAllProperties(obj[key]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return converted;
|
|
27
|
+
}
|
|
28
|
+
return convertProperty(obj);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Take a CSS Variable name and return a variable property
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* const myVars = createVars('color')
|
|
35
|
+
*
|
|
36
|
+
* const myStyles = createStyles({
|
|
37
|
+
* color: cssVar(myVars.color) // color: 'var(--{hash}-color)'
|
|
38
|
+
* })
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* It can also support an optional fallback. Fallbacks should only be used if it is reasonable to
|
|
42
|
+
* expect a CSS variable isn't defined.
|
|
43
|
+
* ```ts
|
|
44
|
+
* const myStyles = createStyles({
|
|
45
|
+
* color: cssVar(myVars.color, 'red') // color: 'var(--{hash}-color, red)'
|
|
46
|
+
* })
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* If the project is set up for parsing with fallback files, a fallback will automatically be filled
|
|
50
|
+
* during the parsing phase. This is helpful for cases when CSS variables are expected, but not set
|
|
51
|
+
* in the environment.
|
|
52
|
+
*/
|
|
53
|
+
export function cssVar(input, fallback) {
|
|
54
|
+
return fallback
|
|
55
|
+
? `var(${input}, ${fallback.startsWith('--') ? `var(${fallback})` : fallback})`
|
|
56
|
+
: `var(${input})`;
|
|
57
|
+
}
|
|
58
|
+
export function createVars(...args) {
|
|
59
|
+
const id = args[0].id || generateUniqueId(); //?
|
|
60
|
+
// eslint-disable-next-line no-param-reassign
|
|
61
|
+
args = args[0].args || args;
|
|
62
|
+
const result = (input) => {
|
|
63
|
+
const vars = {};
|
|
64
|
+
args.forEach(key => {
|
|
65
|
+
if (input[key]) {
|
|
66
|
+
// @ts-ignore TS complains about `key` not in object `{}`
|
|
67
|
+
vars[result[key]] = input[key];
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return vars;
|
|
71
|
+
};
|
|
72
|
+
args.forEach(key => {
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
result[key] = `--${id}-${slugify(key)}`;
|
|
75
|
+
}, {});
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a modifier function that takes in a modifier config and will return a CSS class name that
|
|
80
|
+
* matches the result. Modifiers can be thought as `if` or `switch` statements when conditionally
|
|
81
|
+
* changing the styles of a component based on props. This function can be thought of as a helper
|
|
82
|
+
* function that makes it easier to work with modifiers. Without it, you would have to implement
|
|
83
|
+
* if/switch/ternary for each option.
|
|
84
|
+
*
|
|
85
|
+
* ```tsx
|
|
86
|
+
* const myModifiers = createModifiers({
|
|
87
|
+
* // a modifier called 'size'
|
|
88
|
+
* size: {
|
|
89
|
+
* small: createStyles({ fontSize: 12 }),
|
|
90
|
+
* medium: createStyles({ fontSize: 14 })
|
|
91
|
+
* }
|
|
92
|
+
* })
|
|
93
|
+
*
|
|
94
|
+
* // with the modifier function
|
|
95
|
+
* myModifiers({ size: 'medium' }) // returns the medium class name
|
|
96
|
+
*
|
|
97
|
+
* // manually without the function
|
|
98
|
+
* size === 'small' ? myModifiers.size.small : size === 'medium' ? myModifiers.size.medium : ''
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export function createModifiers(input) {
|
|
102
|
+
const modifierFn = (modifiers) => {
|
|
103
|
+
return Object.keys(modifiers)
|
|
104
|
+
.filter(key => input[key] && input[key][modifiers[key]])
|
|
105
|
+
.map(key => input[key][modifiers[key]])
|
|
106
|
+
.join(' ');
|
|
107
|
+
};
|
|
108
|
+
Object.keys(input).forEach(key => {
|
|
109
|
+
// @ts-ignore TypeScript makes it a pain to deal with index types
|
|
110
|
+
modifierFn[key] = input[key];
|
|
111
|
+
});
|
|
112
|
+
return modifierFn;
|
|
113
|
+
}
|
|
114
|
+
function isCsPropsReturn(input) {
|
|
115
|
+
return input.hasOwnProperty('style') || input.hasOwnProperty('className');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A function that takes in a single input, or an array. The type of the input is either:
|
|
119
|
+
*
|
|
120
|
+
* - `string` - it represents a CSS class name
|
|
121
|
+
* - `undefined` - there is no value. This is provided for convenience for developers to not have to
|
|
122
|
+
* filter out undefined
|
|
123
|
+
* - `{--{hash}-{varName}: {value}}` - a `Map` of CSS variable to values
|
|
124
|
+
* - `{style: ..., className: ...}` an object already returned by another `csToProps` function call
|
|
125
|
+
* (for nesting)
|
|
126
|
+
* - An array containing any of the above. This will recurse over each entry to produce a single,
|
|
127
|
+
* reduced `{style: ..., className: ...}` object
|
|
128
|
+
* @param input
|
|
129
|
+
* @returns
|
|
130
|
+
*/
|
|
131
|
+
export function csToProps(input) {
|
|
132
|
+
// input is a string, so it must only be a class name
|
|
133
|
+
if (typeof input === 'string') {
|
|
134
|
+
return { className: input };
|
|
135
|
+
}
|
|
136
|
+
// input isn't defined, so we'll return an empty object
|
|
137
|
+
if (!input) {
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
// A nested `csToProps`, we'll simply return with no modification
|
|
141
|
+
if (isCsPropsReturn(input)) {
|
|
142
|
+
return input;
|
|
143
|
+
}
|
|
144
|
+
// At this point, `input` is either an array or a `Record<T, string>`. If it isn't an array, it
|
|
145
|
+
// must be a style object for setting CSS variables. So set the `style` attribute
|
|
146
|
+
if (!Array.isArray(input)) {
|
|
147
|
+
return { style: input };
|
|
148
|
+
}
|
|
149
|
+
// An array. it is the only thing left. We iterate and recurse over the array to produce a single
|
|
150
|
+
// object with `style` and `className` attributes to spread on an element
|
|
151
|
+
return input.map(csToProps).reduce((result, val) => {
|
|
152
|
+
return {
|
|
153
|
+
className: [result.className, val.className].filter(v => v).join(' '),
|
|
154
|
+
style: { ...result.style, ...val.style },
|
|
155
|
+
};
|
|
156
|
+
}, {});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Creates CSS styles based on object-style input. It has a side-effect of adding CSS to the page
|
|
160
|
+
* and will return a space-delimitated string of CSS class names meant to be added to an element.
|
|
161
|
+
*
|
|
162
|
+
* It can take a number of inputs of various types. The simplest is object-styles.
|
|
163
|
+
*
|
|
164
|
+
* ```ts
|
|
165
|
+
* const myStyles = createStyles({
|
|
166
|
+
* backgroundColor: 'red'
|
|
167
|
+
* })
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* The `createStyles` function is curried into 2 parts. The first function could be done at build
|
|
171
|
+
* time. The returned function combines CSS class names and will remain as a small runtime.
|
|
172
|
+
*
|
|
173
|
+
* > **Note:** The order of calling `createStyles` is important. Each call will make a single CSS
|
|
174
|
+
* > class selector and will be injected into the document's
|
|
175
|
+
* > [StyleSheetList](https://developer.mozilla.org/en-US/docs/Web/API/StyleSheetList). Style
|
|
176
|
+
* > properties will be merge by the rules of [CSS
|
|
177
|
+
* > specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity). If two selectors
|
|
178
|
+
* > have the same specificity, the last defined wins. Always make sure that the properties you want
|
|
179
|
+
* > to win are last in your file.
|
|
180
|
+
*/
|
|
181
|
+
export function createStyles(...args) {
|
|
182
|
+
return args
|
|
183
|
+
.map(input => {
|
|
184
|
+
if (typeof input === 'string') {
|
|
185
|
+
return input;
|
|
186
|
+
}
|
|
187
|
+
const convertedStyles = convertAllProperties(input);
|
|
188
|
+
// We want to call `serializeStyles` directly and ignore the hash generated so we can have
|
|
189
|
+
// more predictable style merging. If 2 different files define the same style properties, the
|
|
190
|
+
// hash will be the same and using the default hash functionality of Emotion will mean the
|
|
191
|
+
// merge order will be unpredictable. For example, `BaseButton` and `TertiaryButton` define
|
|
192
|
+
// different modifiers for padding. The BaseButton's `extraSmall` modifier contains the same
|
|
193
|
+
// styling as TertiaryButton's `large` modifier. Emotion would hash these two declarations as
|
|
194
|
+
// the same hash and only inject the one from `BaseButton`. If `TertiaryButton` defines
|
|
195
|
+
// `padding` in its base styles, and uses the large size modifier, the base padding will
|
|
196
|
+
// override the `TertiaryButton` `large` size modifier because `BaseButton.small` modifier was
|
|
197
|
+
// injected into the document's style sheets before `TertiaryButton.base` styles. This is due
|
|
198
|
+
// to CSS specificity. If everything has the same specificity, last defined wins. More info:
|
|
199
|
+
// https://codesandbox.io/s/stupefied-bartik-9c2jtd?file=/src/App.tsx
|
|
200
|
+
const { styles } = serializeStyles([convertedStyles]);
|
|
201
|
+
// use `css.call()` instead of `css()` to trick Emotion's babel plugin to not rewrite our code
|
|
202
|
+
// to remove our generated Id for the name:
|
|
203
|
+
// https://github.com/emotion-js/emotion/blob/f3b268f7c52103979402da919c9c0dd3f9e0e189/packages/babel-plugin/src/utils/transform-expression-with-styles.js#L81-L82
|
|
204
|
+
// Without this "fix", anyone using the Emotion babel plugin would get different results than
|
|
205
|
+
// intended when styles are merged.
|
|
206
|
+
return css.call(null, { name: generateUniqueId(), styles }); //?
|
|
207
|
+
})
|
|
208
|
+
.join(' ');
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* React utility function to apply CSS class names and dynamic CSS variables
|
|
212
|
+
*
|
|
213
|
+
* ```tsx
|
|
214
|
+
* const MyComponent = (props: any) => {
|
|
215
|
+
* return <div {...handleCsProp(props)} />
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*
|
|
219
|
+
* It will return an object with `className` and `style` attributes. If a `className` is provided to
|
|
220
|
+
* the component, it will merge the class names. If a `style` is provided to the component, it will
|
|
221
|
+
* merge the styles.
|
|
222
|
+
*
|
|
223
|
+
* ```tsx
|
|
224
|
+
* const vars = createVars('background')
|
|
225
|
+
* const styles = createStyles({
|
|
226
|
+
* color: vars.color
|
|
227
|
+
* })
|
|
228
|
+
* <MyComponent
|
|
229
|
+
* className="foobar"
|
|
230
|
+
* style={{ padding: 10 }}
|
|
231
|
+
* cs={styles}
|
|
232
|
+
* />
|
|
233
|
+
*
|
|
234
|
+
* // Output
|
|
235
|
+
* <div
|
|
236
|
+
* className="foobar {hashedClassName}"
|
|
237
|
+
* style="padding: 10px; --{hash}-background: red;"
|
|
238
|
+
* />
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export function handleCsProp({ cs, style, className, ...props }) {
|
|
242
|
+
return {
|
|
243
|
+
...csToProps([cs, className, style]),
|
|
244
|
+
...props,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slugify.d.ts","sourceRoot":"","sources":["../../../lib/slugify.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAU7C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function that takes a string and returns a "slug" which can be used in HTML
|
|
3
|
+
*/
|
|
4
|
+
export function slugify(input) {
|
|
5
|
+
return input
|
|
6
|
+
.trim()
|
|
7
|
+
.replace(/[\.\s_-]+/g, '-')
|
|
8
|
+
.replace(/[^\w\s-]/g, '')
|
|
9
|
+
.replace(/([A-Z])/g, m => {
|
|
10
|
+
return '-' + m.toLowerCase();
|
|
11
|
+
})
|
|
12
|
+
.replace(/^-+|-+$/g, '')
|
|
13
|
+
.toLowerCase();
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a unique and HTML5 compliant identifier every time it is called. Internally it uses a 4
|
|
3
|
+
* character random seed starting with a letter. This seed is unique to each instance of this
|
|
4
|
+
* package meaning different versions of Canvas Kit on the page will have a different seed. Each
|
|
5
|
+
* call will use a Base 36 string (10 numbers + 26 letters) based on an incremented number. The
|
|
6
|
+
* incremented number always starts at 0 and can be reset for testing purposes using
|
|
7
|
+
* [resetUniqueIdCount](#resetuniqueidcount). [setUniqueSeed](#setuniqueseed) can also be used for
|
|
8
|
+
* testing or server side rendering to get the same results during hydration.
|
|
9
|
+
*/
|
|
10
|
+
export declare const generateUniqueId: () => string;
|
|
11
|
+
/**
|
|
12
|
+
* Update the seed used by the id generator. This is useful for snapshot tests to help stabilize ids
|
|
13
|
+
* generated each run. This could also be used for server-side hydration - if you choose the same
|
|
14
|
+
* seed for server and set that on the client before components are rendered, the ids generated will
|
|
15
|
+
* be the same.
|
|
16
|
+
* @example
|
|
17
|
+
* // set in a script tag from the server
|
|
18
|
+
* setSeed(window.__ID_SEED); // set in a script tag from the server
|
|
19
|
+
*
|
|
20
|
+
* // jest setup
|
|
21
|
+
* before(() => {
|
|
22
|
+
* setSeed('a')
|
|
23
|
+
* })
|
|
24
|
+
*/
|
|
25
|
+
export declare const setUniqueSeed: (s: string) => void;
|
|
26
|
+
/**
|
|
27
|
+
* This should only be called for tests in an `beforeEach`
|
|
28
|
+
*/
|
|
29
|
+
export declare const resetUniqueIdCount: () => void;
|
|
30
|
+
//# sourceMappingURL=uniqueId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniqueId.d.ts","sourceRoot":"","sources":["../../../lib/uniqueId.ts"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,cAAkC,CAAC;AAEhE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa,MAAO,MAAM,SAEtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,YAE9B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Create a unique seed per import to prevent collisions from other versions of `useUniqueId`
|
|
2
|
+
let seed = Math.random()
|
|
3
|
+
.toString(36)
|
|
4
|
+
.slice(2)
|
|
5
|
+
.replace(/[0-9]*/, '')
|
|
6
|
+
.substr(0, 4);
|
|
7
|
+
let c = 0;
|
|
8
|
+
/**
|
|
9
|
+
* Generates a unique and HTML5 compliant identifier every time it is called. Internally it uses a 4
|
|
10
|
+
* character random seed starting with a letter. This seed is unique to each instance of this
|
|
11
|
+
* package meaning different versions of Canvas Kit on the page will have a different seed. Each
|
|
12
|
+
* call will use a Base 36 string (10 numbers + 26 letters) based on an incremented number. The
|
|
13
|
+
* incremented number always starts at 0 and can be reset for testing purposes using
|
|
14
|
+
* [resetUniqueIdCount](#resetuniqueidcount). [setUniqueSeed](#setuniqueseed) can also be used for
|
|
15
|
+
* testing or server side rendering to get the same results during hydration.
|
|
16
|
+
*/
|
|
17
|
+
export const generateUniqueId = () => seed + (c++).toString(36);
|
|
18
|
+
/**
|
|
19
|
+
* Update the seed used by the id generator. This is useful for snapshot tests to help stabilize ids
|
|
20
|
+
* generated each run. This could also be used for server-side hydration - if you choose the same
|
|
21
|
+
* seed for server and set that on the client before components are rendered, the ids generated will
|
|
22
|
+
* be the same.
|
|
23
|
+
* @example
|
|
24
|
+
* // set in a script tag from the server
|
|
25
|
+
* setSeed(window.__ID_SEED); // set in a script tag from the server
|
|
26
|
+
*
|
|
27
|
+
* // jest setup
|
|
28
|
+
* before(() => {
|
|
29
|
+
* setSeed('a')
|
|
30
|
+
* })
|
|
31
|
+
*/
|
|
32
|
+
export const setUniqueSeed = (s) => {
|
|
33
|
+
seed = s;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* This should only be called for tests in an `beforeEach`
|
|
37
|
+
*/
|
|
38
|
+
export const resetUniqueIdCount = () => {
|
|
39
|
+
c = 0;
|
|
40
|
+
};
|
package/index.ts
ADDED