@vanilla-bean/components 1.0.2 → 1.1.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 +5 -5
- package/Component/Component.scenarios.js +2 -2
- package/Component/Component.test.js +2 -2
- package/Component/observeElementConnection.js +1 -1
- package/README.md +1 -1
- package/components/BottomSheet/README.md +1 -1
- package/components/Button/Button.lld.md +1 -1
- package/components/Code/Code.lld.md +1 -1
- package/components/ColorPicker/ColorPicker.js +1 -1
- package/components/Dialog/Dialog.js +1 -1
- package/components/Dialog/Dialog.lld.md +1 -1
- package/components/Dialog/README.md +10 -10
- package/components/Form/Form.js +9 -9
- package/components/Form/Form.lld.md +2 -2
- package/components/Form/README.md +3 -3
- package/components/Input/README.md +7 -7
- package/components/Keyboard/Keyboard.lld.md +1 -1
- package/components/Menu/Menu.js +3 -3
- package/components/Notify/Notify.lld.md +2 -2
- package/components/Page/Page.lld.md +1 -1
- package/components/RadioButton/RadioButton.lld.md +1 -1
- package/components/Router/README.md +12 -12
- package/components/Select/README.md +5 -5
- package/components/Select/Select.js +1 -1
- package/components/Table/README.md +4 -4
- package/components/Table/Table.lld.md +1 -1
- package/components/TagList/TagList.lld.md +1 -1
- package/components/TooltipWrapper/TooltipWrapper.lld.md +2 -2
- package/components/Whiteboard/Whiteboard.lld.md +1 -1
- package/index.d.ts +4 -3
- package/package.json +1 -1
- package/theme/.test.js +20 -4
- package/theme/README.md +15 -8
- package/theme/button.js +1 -1
- package/theme/colors.js +9 -2
- package/theme/input.js +3 -3
- package/theme/page.js +6 -0
- package/theme/scrollbar.js +7 -3
- package/theme/table.js +2 -2
- package/utils/browser.js +1 -1
- package/Component/README.md +0 -455
- package/Elem/README.md +0 -373
- package/styled/README.md +0 -329
package/Component/README.md
DELETED
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
# Component
|
|
2
|
-
|
|
3
|
-
Reactive component class extending Elem with Oxject-backed reactive options, lifecycle management, and automatic cleanup.
|
|
4
|
-
|
|
5
|
-
## Key Features
|
|
6
|
-
|
|
7
|
-
- **Reactive options system** - Property changes backed by Oxject emit events and update DOM automatically
|
|
8
|
-
- **Automatic memory management** - Element-level listeners persist through DOM moves, cleaned up on destroy. External resources (timers, subscriptions) cleaned up on disconnect
|
|
9
|
-
- **Flexible render timing** - Immediate, onload, or animationFrame rendering modes
|
|
10
|
-
- **Enhanced event handling** - Input events include `.value` property, DOM connection detection
|
|
11
|
-
- **Integrated styling** - Inline objects or scoped CSS with theme system integration
|
|
12
|
-
- **Lifecycle hooks** - Built-in hover and press handlers with automatic cleanup
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
import { Component } from '@vanilla-bean/components';
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Basic Usage
|
|
19
|
-
|
|
20
|
-
### Simple Components
|
|
21
|
-
|
|
22
|
-
Every Component extends EventTarget and wraps an HTMLElement with reactive options:
|
|
23
|
-
|
|
24
|
-
```js
|
|
25
|
-
import { Component } from '@vanilla-bean/components';
|
|
26
|
-
|
|
27
|
-
const button = new Component({
|
|
28
|
-
tag: 'button',
|
|
29
|
-
textContent: 'Click me',
|
|
30
|
-
className: 'primary-btn',
|
|
31
|
-
onPointerPress: () => alert('Hello!'),
|
|
32
|
-
appendTo: document.body,
|
|
33
|
-
});
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Reactive Property Updates
|
|
37
|
-
|
|
38
|
-
Options are backed by Oxject. Property changes trigger DOM updates automatically:
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
const input = new Component({
|
|
42
|
-
tag: 'input',
|
|
43
|
-
value: 'Initial text',
|
|
44
|
-
onChange: ({ value }) => {
|
|
45
|
-
input.options.value = value; // Updates DOM reactively
|
|
46
|
-
},
|
|
47
|
-
appendTo: document.body,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Display that updates automatically
|
|
51
|
-
new Component({
|
|
52
|
-
tag: 'p',
|
|
53
|
-
textContent: input.options.subscriber('value', value => `Current: ${value}`),
|
|
54
|
-
appendTo: document.body,
|
|
55
|
-
});
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Extended Component Classes
|
|
59
|
-
|
|
60
|
-
Create reusable component classes with custom behavior:
|
|
61
|
-
|
|
62
|
-
```js
|
|
63
|
-
class Button extends Component {
|
|
64
|
-
constructor(options = {}, ...children) {
|
|
65
|
-
super(
|
|
66
|
-
{
|
|
67
|
-
tag: 'button',
|
|
68
|
-
...options,
|
|
69
|
-
styles: ({ button }) => `
|
|
70
|
-
${button}
|
|
71
|
-
${options.styles?.({ button }) || ''}
|
|
72
|
-
`,
|
|
73
|
-
},
|
|
74
|
-
...children,
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_setOption(key, value) {
|
|
79
|
-
if (key === 'mode') {
|
|
80
|
-
this.removeClass(/\bmode-\S+/g).addClass(`mode-${value}`);
|
|
81
|
-
} else {
|
|
82
|
-
super._setOption(key, value);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### With Styled Components
|
|
89
|
-
|
|
90
|
-
Components integrate seamlessly with the styled system:
|
|
91
|
-
|
|
92
|
-
```js
|
|
93
|
-
import { styled } from '@vanilla-bean/components';
|
|
94
|
-
|
|
95
|
-
const StyledComponent = styled(
|
|
96
|
-
Component,
|
|
97
|
-
({ colors }) => `
|
|
98
|
-
background: ${colors.blue};
|
|
99
|
-
color: ${colors.white};
|
|
100
|
-
padding: 16px;
|
|
101
|
-
border-radius: 8px;
|
|
102
|
-
`,
|
|
103
|
-
);
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Reactive Options System
|
|
107
|
-
|
|
108
|
-
Component options are stored in an Oxject instance (not a plain object like Elem), enabling automatic reactivity:
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
const button = new Component({ textContent: 'Click me' });
|
|
112
|
-
|
|
113
|
-
// Property changes trigger DOM updates
|
|
114
|
-
button.options.textContent = 'Updated'; // Updates DOM reactively
|
|
115
|
-
button.options.addClass = 'active'; // Adds CSS class reactively
|
|
116
|
-
|
|
117
|
-
// Full Oxject features available
|
|
118
|
-
const subscription = button.options.subscribe({
|
|
119
|
-
key: 'textContent',
|
|
120
|
-
callback: value => console.log('Text changed:', value),
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Option Processing Pipeline
|
|
125
|
-
|
|
126
|
-
Options are routed through `_setOption` based on key and value type:
|
|
127
|
-
|
|
128
|
-
| Condition | Routing | Example |
|
|
129
|
-
| ------------------------ | -------------------------- | ------------------------- |
|
|
130
|
-
| Key starts with 'on' | Event handler registration | `onPointerPress: handler` |
|
|
131
|
-
| Key in `knownAttributes` | `elem.setAttribute()` | `id: 'my-id'` |
|
|
132
|
-
| Component has method | Method call with value | `addClass: 'active'` |
|
|
133
|
-
| HTMLElement has property | Direct elem property | `textContent: 'hello'` |
|
|
134
|
-
| Value is function | Component property | `customMethod: fn` |
|
|
135
|
-
| Default | Elem property assignment | `customProp: value` |
|
|
136
|
-
|
|
137
|
-
## Event Handling
|
|
138
|
-
|
|
139
|
-
### Automatic Event Registration
|
|
140
|
-
|
|
141
|
-
Event handlers are registered automatically when option keys start with 'on':
|
|
142
|
-
|
|
143
|
-
```js
|
|
144
|
-
new Component({
|
|
145
|
-
onPointerPress: event => console.log('pressed'),
|
|
146
|
-
onChange: ({ value }) => console.log('value:', value), // Input events include .value
|
|
147
|
-
onConnected: () => console.log('added to DOM'),
|
|
148
|
-
onDisconnected: () => console.log('removed from DOM'),
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
> **Note:** `click` is not in the supported event set. Use `onPointerPress` for press interactions.
|
|
153
|
-
|
|
154
|
-
### Enhanced Input Events
|
|
155
|
-
|
|
156
|
-
Input-related events (`keydown`, `keyup`, `change`, `blur`, `input`, `search`) receive enhanced event objects:
|
|
157
|
-
|
|
158
|
-
```js
|
|
159
|
-
new Component({
|
|
160
|
-
tag: 'input',
|
|
161
|
-
type: 'text',
|
|
162
|
-
onChange: ({ value, event }) => {
|
|
163
|
-
console.log('New value:', value); // Extracted from input
|
|
164
|
-
console.log('Original event:', event);
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Connection Events
|
|
170
|
-
|
|
171
|
-
DOM connection/disconnection detected via MutationObserver:
|
|
172
|
-
|
|
173
|
-
```js
|
|
174
|
-
new Component({
|
|
175
|
-
onConnected: () => console.log('Component added to DOM'),
|
|
176
|
-
onDisconnected: () => {
|
|
177
|
-
console.log('Component removed - cleanup triggered');
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Specialized Event Handlers
|
|
183
|
-
|
|
184
|
-
#### Hover with Movement Tracking
|
|
185
|
-
|
|
186
|
-
```js
|
|
187
|
-
component.onHover(event => {
|
|
188
|
-
console.log('Mouse position:', event.clientX, event.clientY);
|
|
189
|
-
});
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
#### Press Detection
|
|
193
|
-
|
|
194
|
-
Fires on `pointerdown` for immediate, reliable response in all contexts including scroll containers and modal dialogs:
|
|
195
|
-
|
|
196
|
-
```js
|
|
197
|
-
component.onPointerPress(event => {
|
|
198
|
-
console.log('Pressed');
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Styling
|
|
203
|
-
|
|
204
|
-
### Inline Style Objects
|
|
205
|
-
|
|
206
|
-
Apply styles as JavaScript objects:
|
|
207
|
-
|
|
208
|
-
```js
|
|
209
|
-
component.styles({
|
|
210
|
-
color: 'red',
|
|
211
|
-
padding: '10px',
|
|
212
|
-
backgroundColor: '#f0f0f0',
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Or as option
|
|
216
|
-
new Component({
|
|
217
|
-
styles: { color: 'red', padding: '10px' },
|
|
218
|
-
});
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Scoped CSS with Theme Integration
|
|
222
|
-
|
|
223
|
-
Use theme functions for scoped, processed CSS:
|
|
224
|
-
|
|
225
|
-
```js
|
|
226
|
-
// Theme function with automatic scoping
|
|
227
|
-
component.styles(
|
|
228
|
-
({ colors, fonts }) => `
|
|
229
|
-
background: ${colors.blue};
|
|
230
|
-
${fonts.kodeMono}
|
|
231
|
-
padding: 16px;
|
|
232
|
-
border-radius: 8px;
|
|
233
|
-
|
|
234
|
-
&:hover {
|
|
235
|
-
background: ${colors.darker(colors.blue)};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
@media (max-width: 768px) {
|
|
239
|
-
padding: 8px;
|
|
240
|
-
}
|
|
241
|
-
`,
|
|
242
|
-
);
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
CSS is scope-wrapped and injected as a scoped <style> tag. No preprocessing.
|
|
246
|
-
|
|
247
|
-
## Lifecycle Management
|
|
248
|
-
|
|
249
|
-
### Render Lifecycle
|
|
250
|
-
|
|
251
|
-
`render()` orchestrates a fixed sequence every time it is called:
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
render()
|
|
255
|
-
├─ if (this.rendered) ← re-render only: clears children, runs descendant cleanup
|
|
256
|
-
│ empty()
|
|
257
|
-
│ this.rendered = false
|
|
258
|
-
├─ build() ← subclass structural hook, runs before options are applied
|
|
259
|
-
├─ _processOptions() ← routes every option through _setOption()
|
|
260
|
-
│ priority keys first (onConnected, textContent, content, appendTo, prependTo, value)
|
|
261
|
-
│ then all remaining keys
|
|
262
|
-
└─ this.rendered = true
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
**`build()` is the subclass structural hook.** Override `build()`, never `render()`, to create child elements and internal structure. Because `build()` runs before `_processOptions()`, all structure exists by the time `_setOption` receives values.
|
|
266
|
-
|
|
267
|
-
The preferred way to handle specific options is `static handlers` — a per-class dispatch map that composes across the constructor chain without requiring `super._setOption`:
|
|
268
|
-
|
|
269
|
-
```js
|
|
270
|
-
class Card extends Component {
|
|
271
|
-
build() {
|
|
272
|
-
this.header = new Component({ tag: 'header', appendTo: this });
|
|
273
|
-
this.body = new Component({ tag: 'section', appendTo: this });
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
static handlers = {
|
|
277
|
-
title(value) {
|
|
278
|
-
this.header.options.textContent = value;
|
|
279
|
-
},
|
|
280
|
-
variant(value) {
|
|
281
|
-
this.removeClass(/variant-\S+/).addClass(`variant-${value}`);
|
|
282
|
-
},
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
Handlers that don't call `next(value)` fully own their key. Unhandled keys fall through to standard routing automatically — no `super._setOption` required. Use `_setOption` override only for enum validation or cases that need to intercept before the routing chain.
|
|
288
|
-
|
|
289
|
-
**`empty()` only runs on re-render.** On the initial render it is skipped. On subsequent `render()` calls it removes all child elements and runs cleanup on all descendant components before the DOM is cleared.
|
|
290
|
-
|
|
291
|
-
**`async build()` is not supported.** `render()` does not await `build()`'s return value. Asynchronous initialization belongs in `onConnected`.
|
|
292
|
-
|
|
293
|
-
**Deep hierarchies must chain `super.build()` manually.** If both a parent class and its subclass define `build()`, the subclass must call `super.build()` explicitly. It is not called automatically.
|
|
294
|
-
|
|
295
|
-
### Rendering Process (summary)
|
|
296
|
-
|
|
297
|
-
```js
|
|
298
|
-
component.render(); // Re-render: empty → build → _processOptions → rendered = true
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Rendering behavior:
|
|
302
|
-
|
|
303
|
-
1. **Content clearing** - `empty()` removes existing children on re-render only (skipped on first render)
|
|
304
|
-
2. **Structure creation** - `build()` creates sub-elements before options are applied
|
|
305
|
-
3. **Priority processing** - `_processOptions()` applies `priorityOptions` keys first
|
|
306
|
-
4. **Option routing** - Each option processed via `_setOption`
|
|
307
|
-
5. **Completion flag** - Sets `rendered = true`
|
|
308
|
-
|
|
309
|
-
### Automatic Cleanup System
|
|
310
|
-
|
|
311
|
-
Components automatically clean up resources when removed from DOM:
|
|
312
|
-
|
|
313
|
-
```js
|
|
314
|
-
// Manual cleanup registration
|
|
315
|
-
component.addCleanup('unique-id', () => {
|
|
316
|
-
console.log('Custom cleanup executed');
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Manual cleanup execution (automatic on disconnect)
|
|
320
|
-
component.processCleanup();
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
**Automatic cleanup includes:**
|
|
324
|
-
|
|
325
|
-
- Event listener removal
|
|
326
|
-
- Oxject subscription cleanup
|
|
327
|
-
- Style element removal from DOM
|
|
328
|
-
- Recursive child component cleanup
|
|
329
|
-
|
|
330
|
-
### Render Timing Control
|
|
331
|
-
|
|
332
|
-
Control when components render with `autoRender` option:
|
|
333
|
-
|
|
334
|
-
```js
|
|
335
|
-
// Immediate rendering (default)
|
|
336
|
-
new Component({ autoRender: true });
|
|
337
|
-
|
|
338
|
-
// Render on window load event
|
|
339
|
-
new Component({ autoRender: 'onload' });
|
|
340
|
-
|
|
341
|
-
// Render on next animation frame
|
|
342
|
-
new Component({ autoRender: 'animationFrame' });
|
|
343
|
-
|
|
344
|
-
// Manual rendering only
|
|
345
|
-
new Component({ autoRender: false });
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
## API Reference
|
|
349
|
-
|
|
350
|
-
### Constructor
|
|
351
|
-
|
|
352
|
-
```js
|
|
353
|
-
new Component(options?, ...children)
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
**Parameters:**
|
|
357
|
-
|
|
358
|
-
- `options` (Object) - Configuration options
|
|
359
|
-
- `children` (...Component|Elem) - Child elements appended to `append` option
|
|
360
|
-
|
|
361
|
-
### Configuration Options
|
|
362
|
-
|
|
363
|
-
#### Component-Specific Options
|
|
364
|
-
|
|
365
|
-
| Option | Type | Description |
|
|
366
|
-
| ------------------ | ------------------------------------- | ------------------------------------------- |
|
|
367
|
-
| `tag` | `string` | HTML tag name (default: 'div') |
|
|
368
|
-
| `autoRender` | `boolean\|'onload'\|'animationFrame'` | Render timing control |
|
|
369
|
-
| `registeredEvents` | `Set<string>` | Custom event types for on() method |
|
|
370
|
-
| `knownAttributes` | `Set<string>` | Attribute names for elem.setAttribute() |
|
|
371
|
-
| `priorityOptions` | `Set<string>` | Option keys processed first during render |
|
|
372
|
-
| `styles` | `string\|object\|Function` | CSS string, style object, or theme function |
|
|
373
|
-
| `uniqueId` | `string` | Override auto-generated unique ID |
|
|
374
|
-
|
|
375
|
-
#### Event Handler Options
|
|
376
|
-
|
|
377
|
-
| Option | Type | Description |
|
|
378
|
-
| ---------------- | ---------- | -------------------------------------------- |
|
|
379
|
-
| `onHover` | `Function` | Hover handler with move tracking |
|
|
380
|
-
| `onPointerPress` | `Function` | Fires on `pointerdown` |
|
|
381
|
-
| ~~`onClick`~~ | — | Not supported. Use `onPointerPress` instead. |
|
|
382
|
-
| `onChange` | `Function` | Change event handler (input .value included) |
|
|
383
|
-
| `onConnected` | `Function` | DOM connection detection |
|
|
384
|
-
| `onDisconnected` | `Function` | DOM disconnection detection |
|
|
385
|
-
|
|
386
|
-
All standard Elem options work as reactive Component options.
|
|
387
|
-
|
|
388
|
-
### Methods
|
|
389
|
-
|
|
390
|
-
#### Options-Compatible Methods
|
|
391
|
-
|
|
392
|
-
Can be called directly or used as reactive options:
|
|
393
|
-
|
|
394
|
-
```js
|
|
395
|
-
component.styles(styleFunction); // Direct call
|
|
396
|
-
component.onHover(callback); // Direct call
|
|
397
|
-
component.addClass('active'); // Direct call
|
|
398
|
-
component.append(child); // Direct call
|
|
399
|
-
|
|
400
|
-
// Or as reactive options:
|
|
401
|
-
new Component({
|
|
402
|
-
styles: styleFunction,
|
|
403
|
-
onHover: callback,
|
|
404
|
-
addClass: 'active',
|
|
405
|
-
append: [child],
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
#### Lifecycle Methods
|
|
410
|
-
|
|
411
|
-
```js
|
|
412
|
-
component.build(); // Subclass structural hook — override to create child elements
|
|
413
|
-
component.render(); // Re-render: empty → build → _processOptions → rendered = true
|
|
414
|
-
component.addCleanup(id, fn); // Register cleanup function (chains with existing for same id)
|
|
415
|
-
component.replaceCleanup(id, fn); // Replace cleanup, running the previous one immediately
|
|
416
|
-
component.replaceDestroyCleanup(id, fn); // Destroy-only cleanup: survives disconnect, runs on destroy()
|
|
417
|
-
component.processCleanup(); // Execute all cleanup functions
|
|
418
|
-
component.destroy(); // Disconnect observer, run all cleanup, remove from DOM
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
#### Event Methods
|
|
422
|
-
|
|
423
|
-
```js
|
|
424
|
-
component.on({ targetEvent, callback, id? }); // Event registration
|
|
425
|
-
component.emit(eventType, detail); // Event dispatch
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
#### Utility Methods
|
|
429
|
-
|
|
430
|
-
```js
|
|
431
|
-
component.ancestry(); // Prototype chain inspection
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Properties
|
|
435
|
-
|
|
436
|
-
```js
|
|
437
|
-
component.parent; // Parent Component/Elem instance
|
|
438
|
-
component.children; // Child Component/Elem instances
|
|
439
|
-
component.uniqueId; // Frozen unique identifier
|
|
440
|
-
component.options; // Reactive Oxject instance
|
|
441
|
-
component.elem; // Underlying HTMLElement
|
|
442
|
-
component.rendered; // Boolean render status flag
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
## Memory Management
|
|
446
|
-
|
|
447
|
-
Components provide comprehensive automatic memory management:
|
|
448
|
-
|
|
449
|
-
- **Event listeners** - Automatically removed on DOM disconnect
|
|
450
|
-
- **Oxject subscriptions** - Cleaned up when component destroyed
|
|
451
|
-
- **Style elements** - Removed from DOM head on cleanup
|
|
452
|
-
- **Child components** - Recursive cleanup propagation
|
|
453
|
-
- **Custom cleanup** - Manual cleanup function registration
|
|
454
|
-
|
|
455
|
-
No manual cleanup required for typical usage - components handle resource management automatically.
|