@vanilla-bean/components 1.1.1 → 2.0.1
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/Component/Component.js +222 -36
- package/Component/Component.test.js +425 -38
- package/Component/README.md +480 -0
- package/Elem/README.md +373 -0
- package/README.md +1 -1
- package/components/BottomSheet/BottomSheet.js +8 -16
- package/components/Button/Button.js +12 -18
- package/components/Calendar/Calendar.js +139 -59
- package/components/Calendar/CalendarEvent.js +9 -4
- package/components/Calendar/Toolbar.js +28 -20
- package/components/Calendar/index.js +1 -0
- package/components/Code/Code.js +26 -30
- package/components/ColorPicker/ColorPicker.js +57 -57
- package/components/Dialog/Dialog.js +78 -72
- package/components/Form/Form.js +12 -8
- package/components/Icon/Icon.js +19 -11
- package/components/Input/Input.js +85 -67
- package/components/Input/README.md +0 -2
- package/components/Keyboard/Key.js +7 -10
- package/components/Keyboard/Keyboard.js +38 -46
- package/components/Label/Label.js +52 -50
- package/components/Link/Link.js +14 -20
- package/components/List/List.js +28 -30
- package/components/Menu/Menu.js +19 -10
- package/components/Menu/Menu.lld.md +2 -2
- package/components/Notify/Notify.js +29 -19
- package/components/Popover/Popover.js +49 -44
- package/components/RadioButton/RadioButton.js +33 -29
- package/components/Router/Router.js +19 -21
- package/components/Select/Select.js +24 -30
- package/components/Table/Table.js +55 -36
- package/components/TagList/Tag.js +16 -14
- package/components/TagList/TagList.js +15 -8
- package/components/Tooltip/Tooltip.js +12 -25
- package/components/TooltipWrapper/TooltipWrapper.js +55 -49
- package/components/Whiteboard/Whiteboard.js +45 -43
- package/devTools/build.js +43 -0
- package/devTools/buildTypes.js +322 -0
- package/devTools/createComponent.js +155 -0
- package/devTools/extractJSDoc.js +395 -0
- package/devTools/processTemplate.js +500 -0
- package/devTools/updateComponentIndex.js +16 -0
- package/devTools/updateDemoViewIndex.js +90 -0
- package/eslint.config.cjs +6 -3
- package/index.d.ts +117 -59
- package/package.json +67 -22
- package/spellcheck.config.cjs +4 -0
- package/styled/README.md +329 -0
- package/theme/colors.js +15 -12
- package/theme/colors.test.js +28 -0
- package/utils/element.js +2 -2
- package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
package/styled/README.md
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
# styled
|
|
2
|
+
|
|
3
|
+
Create Component subclasses with scoped CSS: each `styled()` call generates a unique class, processes the theme, and injects a `<style>` element into `<head>`.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
### Template Literal Syntax
|
|
8
|
+
|
|
9
|
+
Create styled components using template literals with theme integration:
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const StyledIcon = styled.Icon`
|
|
13
|
+
background-color: ${({ colors }) => colors.black};
|
|
14
|
+
width: 24px;
|
|
15
|
+
height: 24px;
|
|
16
|
+
|
|
17
|
+
&:before {
|
|
18
|
+
${({ fonts }) => fonts.fontAwesomeSolid}
|
|
19
|
+
content: "\f015";
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Function Syntax with Configuration
|
|
25
|
+
|
|
26
|
+
Use function syntax when you need to pass component configuration options:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const ConfiguredComponent = styled(
|
|
30
|
+
Component,
|
|
31
|
+
({ colors }) => `
|
|
32
|
+
background-color: ${colors.black};
|
|
33
|
+
color: ${colors.lighter(colors.blue)};
|
|
34
|
+
padding: 12px;
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
background-color: ${colors.dark(colors.blue)};
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
{
|
|
41
|
+
tag: 'section',
|
|
42
|
+
role: 'banner',
|
|
43
|
+
textContent: 'Default Text',
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Named Component Shortcuts
|
|
49
|
+
|
|
50
|
+
All top-level components are available as shorthand methods:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const StyledButton = styled.Button`
|
|
54
|
+
background-color: ${({ colors }) => colors.green};
|
|
55
|
+
border-radius: 8px;
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const StyledInput = styled.Input`
|
|
59
|
+
${({ fonts }) => fonts.kodeMono}
|
|
60
|
+
border: 2px solid ${({ colors }) => colors.blue};
|
|
61
|
+
`;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Note**: Template literal syntax creates components with empty configuration. Use function syntax to pass component options.
|
|
65
|
+
|
|
66
|
+
## Theme Integration
|
|
67
|
+
|
|
68
|
+
Style functions receive the complete theme object containing colors, fonts, and component styles:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const ThemedComponent = styled.Component`
|
|
72
|
+
${({ button }) => button} /* Apply base button styles */
|
|
73
|
+
${({ fonts }) => fonts.kodeMono}
|
|
74
|
+
background: ${({ colors }) => colors.darker(colors.blue)};
|
|
75
|
+
color: ${({ colors }) => colors.mostReadable(colors.blue, [colors.white, colors.black])};
|
|
76
|
+
`;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Runtime Style Override
|
|
80
|
+
|
|
81
|
+
Override or extend styles when creating component instances:
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const instance = new StyledComponent({
|
|
85
|
+
styles: ({ colors }) => ({
|
|
86
|
+
backgroundColor: colors.red,
|
|
87
|
+
border: `2px solid ${colors.darker(colors.red)}`,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Object-based styles apply as inline styles. Function-based styles generate scoped CSS.
|
|
93
|
+
|
|
94
|
+
## Component Inheritance
|
|
95
|
+
|
|
96
|
+
### Extending Styled Components
|
|
97
|
+
|
|
98
|
+
Build component hierarchies by extending existing styled components:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
const BaseButton = styled.Button`
|
|
102
|
+
padding: 8px 16px;
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const PrimaryButton = styled(BaseButton)`
|
|
107
|
+
background: ${({ colors }) => colors.blue};
|
|
108
|
+
color: ${({ colors }) => colors.white};
|
|
109
|
+
`;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Template Literals with Any Styled Component
|
|
113
|
+
|
|
114
|
+
Template literal syntax works with any styled component, including those created with function syntax:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
const BaseComponent = styled(Component, () => 'color: red;');
|
|
118
|
+
|
|
119
|
+
// Extend any styled component with template literals
|
|
120
|
+
const ExtendedComponent = styled(BaseComponent)`
|
|
121
|
+
background: ${({ colors }) => colors.blue};
|
|
122
|
+
padding: 16px;
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
// Use in class definitions for custom methods
|
|
126
|
+
class MyComponent extends (styled(BaseComponent)`
|
|
127
|
+
font-weight: bold;
|
|
128
|
+
border-radius: 4px;
|
|
129
|
+
`) {
|
|
130
|
+
// Add custom methods here
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Component Functionality Inheritance
|
|
135
|
+
|
|
136
|
+
Styled components inherit all functionality from their base component:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const StyledInput = styled.Input`
|
|
140
|
+
border: 2px solid ${({ colors }) => colors.blue};
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
const instance = new StyledInput({
|
|
144
|
+
value: 'initial value',
|
|
145
|
+
onChange: event => console.log(event.value),
|
|
146
|
+
placeholder: 'Enter text...',
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## CSS Processing
|
|
151
|
+
|
|
152
|
+
### Native CSS Nesting
|
|
153
|
+
|
|
154
|
+
Styles are injected as written, no transformation, no runtime compilation. VBC requires Chrome 112+, Firefox 117+, and Safari 16.5+, all of which support the `&` nesting syntax natively.
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
const NestedComponent = styled.Component`
|
|
158
|
+
padding: 16px;
|
|
159
|
+
|
|
160
|
+
& .child {
|
|
161
|
+
margin: 8px;
|
|
162
|
+
|
|
163
|
+
&:hover {
|
|
164
|
+
background: ${({ colors }) => colors.blue};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@media (max-width: 768px) {
|
|
169
|
+
padding: 8px;
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Automatic Scoping
|
|
175
|
+
|
|
176
|
+
Each styled component receives a unique class identifier to prevent CSS conflicts:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const StyledDiv = styled(Component, () => `color: red;`);
|
|
180
|
+
const instance = new StyledDiv();
|
|
181
|
+
|
|
182
|
+
// Generated CSS: .a1b2c3d4 { color: red; }
|
|
183
|
+
// Component class: "a1b2c3d4"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Conditional Styles
|
|
187
|
+
|
|
188
|
+
Apply conditional styles using CSS classes and selectors:
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
const ConditionalComponent = styled.Component`
|
|
192
|
+
padding: 12px;
|
|
193
|
+
background: ${({ colors }) => colors.white};
|
|
194
|
+
|
|
195
|
+
&.active {
|
|
196
|
+
background: ${({ colors }) => colors.blue};
|
|
197
|
+
color: ${({ colors }) => colors.white};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
&.disabled {
|
|
201
|
+
opacity: 0.5;
|
|
202
|
+
pointer-events: none;
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
const instance = new ConditionalComponent({
|
|
207
|
+
addClass: 'active',
|
|
208
|
+
textContent: 'Active Button',
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Performance
|
|
213
|
+
|
|
214
|
+
### Processing Pipeline
|
|
215
|
+
|
|
216
|
+
The styled system processes styles through this pipeline:
|
|
217
|
+
|
|
218
|
+
1. **Component Creation** - Generates unique class identifier via `classSafeNanoid()`
|
|
219
|
+
2. **Style Processing** - Converts template literals into theme functions
|
|
220
|
+
3. **Theme Application** - Injects complete theme object into style functions
|
|
221
|
+
4. **CSS Processing** - Processes styles through `shimCSS()` pipeline
|
|
222
|
+
5. **DOM Injection** - Injects final CSS via `appendStyles()`
|
|
223
|
+
|
|
224
|
+
### Load-Time Optimization
|
|
225
|
+
|
|
226
|
+
Style processing timing depends on document state:
|
|
227
|
+
|
|
228
|
+
| Document State | Behavior |
|
|
229
|
+
| ------------------- | ------------------------------------------ |
|
|
230
|
+
| Complete | Processes and injects immediately |
|
|
231
|
+
| Loading | Queues for batch processing on window load |
|
|
232
|
+
| Multiple Components | Batches together for efficiency |
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
// Document loaded - processes immediately
|
|
236
|
+
const StyledComponent = styled(Component, () => 'color: red;');
|
|
237
|
+
|
|
238
|
+
// Document loading - queued for batch processing
|
|
239
|
+
const AnotherStyled = styled(Component, () => 'color: blue;');
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## API Reference
|
|
243
|
+
|
|
244
|
+
### styled(BaseComponent, styles?, options?)
|
|
245
|
+
|
|
246
|
+
Creates a styled component class.
|
|
247
|
+
|
|
248
|
+
**Parameters:**
|
|
249
|
+
|
|
250
|
+
- `BaseComponent` (Function) - Component class to extend
|
|
251
|
+
- `styles` (Function|String) - Style function or CSS string
|
|
252
|
+
- `options` (Object) - Component configuration options
|
|
253
|
+
|
|
254
|
+
**Returns:** Extended component class with scoped styling
|
|
255
|
+
|
|
256
|
+
### configured(BaseComponent, options)
|
|
257
|
+
|
|
258
|
+
Creates a component with configuration but no styles:
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
const ConfiguredComponent = configured(Component, {
|
|
262
|
+
tag: 'article',
|
|
263
|
+
role: 'main',
|
|
264
|
+
textContent: 'Default Content',
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Utility Functions
|
|
269
|
+
|
|
270
|
+
#### appendStyles(css, id?)
|
|
271
|
+
|
|
272
|
+
Inject CSS directly into the page:
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
appendStyles(
|
|
276
|
+
`
|
|
277
|
+
.my-global-class {
|
|
278
|
+
font-weight: bold;
|
|
279
|
+
color: red;
|
|
280
|
+
}
|
|
281
|
+
`,
|
|
282
|
+
'my-global-styles',
|
|
283
|
+
);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### themeStyles({ styles, scope })
|
|
287
|
+
|
|
288
|
+
Generate themed CSS with optional scoping:
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
const themedCSS = themeStyles({
|
|
292
|
+
styles: ({ colors }) => `color: ${colors.white}; background: ${colors.black};`,
|
|
293
|
+
scope: '.my-component',
|
|
294
|
+
});
|
|
295
|
+
// Returns: ".my-component { color: hsl(0, 0%, 90%); background: hsl(0, 0%, 10%); }"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
#### shimCSS(styleConfig)
|
|
299
|
+
|
|
300
|
+
Complete style processing pipeline:
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
shimCSS({
|
|
304
|
+
styles: ({ colors }) => `
|
|
305
|
+
display: flex;
|
|
306
|
+
background: ${colors.blue};
|
|
307
|
+
& .item { padding: 8px; }
|
|
308
|
+
`,
|
|
309
|
+
scope: '.my-scoped-component',
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Development Features
|
|
314
|
+
|
|
315
|
+
### Debug Class Names
|
|
316
|
+
|
|
317
|
+
Development mode adds inheritance-based class names for easier debugging:
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
// Development classes: "a1b2c3 MyCustomComponent Component Elem"
|
|
321
|
+
class MyCustomComponent extends Component {}
|
|
322
|
+
const StyledCustom = styled(MyCustomComponent, () => 'color: blue;');
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Memory Management
|
|
326
|
+
|
|
327
|
+
Class-level styles injected by `styled()` persist for the page lifetime. They are scoped to a unique class and do not interfere with other components, but are not removed when instances disconnect.
|
|
328
|
+
|
|
329
|
+
Per-instance styles set via the `styles` option on a component instance are cleaned up when that component disconnects from the DOM.
|
package/theme/colors.js
CHANGED
|
@@ -8,18 +8,21 @@ const colors = {
|
|
|
8
8
|
isReadable,
|
|
9
9
|
mostReadable,
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
// Shared instances for speed (cloning per access proved too slow) - frozen so an
|
|
12
|
+
// accidental mutation (e.g. setAlpha) throws instead of silently recoloring the app.
|
|
13
|
+
// Use colors.alpha(color, alpha) for transparency.
|
|
14
|
+
orange: Object.freeze(new TinyColor('hsl(29, 55%, 45%)')),
|
|
15
|
+
gray: Object.freeze(new TinyColor('hsl(0, 0%, 45%)')),
|
|
16
|
+
yellow: Object.freeze(new TinyColor('hsl(44, 55%, 45%)')),
|
|
17
|
+
green: Object.freeze(new TinyColor('hsl(74, 55%, 45%)')),
|
|
18
|
+
teal: Object.freeze(new TinyColor('hsl(164, 55%, 45%)')),
|
|
19
|
+
blue: Object.freeze(new TinyColor('hsl(209, 55%, 45%)')),
|
|
20
|
+
purple: Object.freeze(new TinyColor('hsl(254, 55%, 45%)')),
|
|
21
|
+
pink: Object.freeze(new TinyColor('hsl(314, 55%, 45%)')),
|
|
22
|
+
red: Object.freeze(new TinyColor('hsl(359, 55%, 45%)')),
|
|
23
|
+
transparent: Object.freeze(new TinyColor('rgba(255, 255, 255, 0)')),
|
|
24
|
+
superWhite: Object.freeze(new TinyColor('hsl(0, 100%, 100%)')),
|
|
25
|
+
vantablack: Object.freeze(new TinyColor('hsl(0, 0%, 0%)')),
|
|
23
26
|
|
|
24
27
|
get white() {
|
|
25
28
|
return colors.whiteish();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import colors from './colors';
|
|
2
|
+
|
|
3
|
+
describe('theme colors', () => {
|
|
4
|
+
test('named hues are frozen shared instances', () => {
|
|
5
|
+
expect(Object.isFrozen(colors.yellow)).toBe(true);
|
|
6
|
+
expect(Object.isFrozen(colors.selected)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('mutating a shared hue throws instead of recoloring the app', () => {
|
|
10
|
+
expect(() => colors.selected.setAlpha(0.5)).toThrow();
|
|
11
|
+
expect(colors.yellow.a).toBe(1);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('alpha() derives transparency without touching the source', () => {
|
|
15
|
+
const translucent = colors.alpha(colors.blue, 0.4);
|
|
16
|
+
|
|
17
|
+
expect(translucent.a).toBe(0.4);
|
|
18
|
+
expect(colors.blue.a).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('ramp helpers return fresh instances safe to modify', () => {
|
|
22
|
+
const light = colors.light(colors.teal);
|
|
23
|
+
|
|
24
|
+
expect(Object.isFrozen(light)).toBe(false);
|
|
25
|
+
light.setAlpha(0.5);
|
|
26
|
+
expect(colors.teal.a).toBe(1);
|
|
27
|
+
});
|
|
28
|
+
});
|
package/utils/element.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @returns {number} Zero-based index position within parent element
|
|
6
6
|
*/
|
|
7
7
|
export const getElementIndex = (element, index = 0) => {
|
|
8
|
-
if (element.previousElementSibling) return getElementIndex(element.previousElementSibling,
|
|
8
|
+
if (element.previousElementSibling) return getElementIndex(element.previousElementSibling, index + 1);
|
|
9
9
|
|
|
10
10
|
return index;
|
|
11
11
|
};
|
|
@@ -44,7 +44,7 @@ export const getElementsContainingText = (text, options = {}) => {
|
|
|
44
44
|
const xPath = `.//${xPathElement}[contains(${caseSensitive ? `text(),${esc(text)}` : `translate(.,${esc(text.toUpperCase())},${esc(text.toLowerCase())}),${esc(text.toLowerCase())}`})]`;
|
|
45
45
|
const result = document.evaluate(xPath, scope, null, XPathResult.ANY_TYPE, null);
|
|
46
46
|
|
|
47
|
-
let node
|
|
47
|
+
let node;
|
|
48
48
|
const nodes = [];
|
|
49
49
|
while ((node = result.iterateNext())) {
|
|
50
50
|
if (nodes.length > 0 && nodes.at(-1).contains(node)) nodes[nodes.length - 1] = node;
|
|
Binary file
|