@colletdev/core 0.1.3

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.
Files changed (119) hide show
  1. package/README.md +77 -0
  2. package/custom-elements.json +6037 -0
  3. package/generated/.gitattributes +2 -0
  4. package/generated/index.d.ts +120 -0
  5. package/generated/index.js +521 -0
  6. package/generated/styles.js +2845 -0
  7. package/package.json +56 -0
  8. package/src/elements/accordion.d.ts +20 -0
  9. package/src/elements/accordion.js +92 -0
  10. package/src/elements/activity_group.d.ts +19 -0
  11. package/src/elements/activity_group.js +27 -0
  12. package/src/elements/alert.d.ts +24 -0
  13. package/src/elements/alert.js +40 -0
  14. package/src/elements/autocomplete.d.ts +30 -0
  15. package/src/elements/autocomplete.js +671 -0
  16. package/src/elements/avatar.d.ts +18 -0
  17. package/src/elements/avatar.js +28 -0
  18. package/src/elements/backdrop.d.ts +14 -0
  19. package/src/elements/backdrop.js +28 -0
  20. package/src/elements/badge.d.ts +21 -0
  21. package/src/elements/badge.js +42 -0
  22. package/src/elements/breadcrumb.d.ts +17 -0
  23. package/src/elements/breadcrumb.js +41 -0
  24. package/src/elements/button.d.ts +24 -0
  25. package/src/elements/button.js +36 -0
  26. package/src/elements/card.d.ts +21 -0
  27. package/src/elements/card.js +67 -0
  28. package/src/elements/carousel.d.ts +23 -0
  29. package/src/elements/carousel.js +895 -0
  30. package/src/elements/chat_input.d.ts +22 -0
  31. package/src/elements/chat_input.js +78 -0
  32. package/src/elements/checkbox.d.ts +21 -0
  33. package/src/elements/checkbox.js +114 -0
  34. package/src/elements/code_block.d.ts +21 -0
  35. package/src/elements/code_block.js +27 -0
  36. package/src/elements/collapsible.d.ts +20 -0
  37. package/src/elements/collapsible.js +93 -0
  38. package/src/elements/date_picker.d.ts +30 -0
  39. package/src/elements/date_picker.js +528 -0
  40. package/src/elements/dialog.d.ts +20 -0
  41. package/src/elements/dialog.js +314 -0
  42. package/src/elements/drawer.d.ts +20 -0
  43. package/src/elements/drawer.js +318 -0
  44. package/src/elements/fab.d.ts +22 -0
  45. package/src/elements/fab.js +36 -0
  46. package/src/elements/file_upload.d.ts +26 -0
  47. package/src/elements/file_upload.js +59 -0
  48. package/src/elements/listbox.d.ts +19 -0
  49. package/src/elements/listbox.js +250 -0
  50. package/src/elements/menu.d.ts +20 -0
  51. package/src/elements/menu.js +224 -0
  52. package/src/elements/message_bubble.d.ts +23 -0
  53. package/src/elements/message_bubble.js +29 -0
  54. package/src/elements/message_group.d.ts +18 -0
  55. package/src/elements/message_group.js +28 -0
  56. package/src/elements/message_part.d.ts +35 -0
  57. package/src/elements/message_part.js +153 -0
  58. package/src/elements/pagination.d.ts +22 -0
  59. package/src/elements/pagination.js +36 -0
  60. package/src/elements/popover.d.ts +26 -0
  61. package/src/elements/popover.js +191 -0
  62. package/src/elements/profile_menu.d.ts +20 -0
  63. package/src/elements/profile_menu.js +213 -0
  64. package/src/elements/progress.d.ts +18 -0
  65. package/src/elements/progress.js +31 -0
  66. package/src/elements/radio_group.d.ts +22 -0
  67. package/src/elements/radio_group.js +70 -0
  68. package/src/elements/scrollbar.d.ts +19 -0
  69. package/src/elements/scrollbar.js +299 -0
  70. package/src/elements/search_bar.d.ts +27 -0
  71. package/src/elements/search_bar.js +98 -0
  72. package/src/elements/select.d.ts +26 -0
  73. package/src/elements/select.js +485 -0
  74. package/src/elements/sidebar.d.ts +21 -0
  75. package/src/elements/sidebar.js +322 -0
  76. package/src/elements/skeleton.d.ts +17 -0
  77. package/src/elements/skeleton.js +31 -0
  78. package/src/elements/slider.d.ts +28 -0
  79. package/src/elements/slider.js +93 -0
  80. package/src/elements/speed_dial.d.ts +23 -0
  81. package/src/elements/speed_dial.js +370 -0
  82. package/src/elements/spinner.d.ts +15 -0
  83. package/src/elements/spinner.js +28 -0
  84. package/src/elements/split_button.d.ts +23 -0
  85. package/src/elements/split_button.js +281 -0
  86. package/src/elements/stepper.d.ts +20 -0
  87. package/src/elements/stepper.js +31 -0
  88. package/src/elements/switch.d.ts +22 -0
  89. package/src/elements/switch.js +129 -0
  90. package/src/elements/table.d.ts +29 -0
  91. package/src/elements/table.js +371 -0
  92. package/src/elements/tabs.d.ts +19 -0
  93. package/src/elements/tabs.js +139 -0
  94. package/src/elements/text.d.ts +26 -0
  95. package/src/elements/text.js +32 -0
  96. package/src/elements/text_input.d.ts +36 -0
  97. package/src/elements/text_input.js +121 -0
  98. package/src/elements/thinking.d.ts +17 -0
  99. package/src/elements/thinking.js +28 -0
  100. package/src/elements/toast.d.ts +23 -0
  101. package/src/elements/toast.js +209 -0
  102. package/src/elements/toggle_group.d.ts +22 -0
  103. package/src/elements/toggle_group.js +176 -0
  104. package/src/elements/tooltip.d.ts +18 -0
  105. package/src/elements/tooltip.js +64 -0
  106. package/src/markdown.d.ts +24 -0
  107. package/src/markdown.js +66 -0
  108. package/src/runtime.d.ts +35 -0
  109. package/src/runtime.js +790 -0
  110. package/src/server.d.ts +69 -0
  111. package/src/server.js +176 -0
  112. package/src/streaming-markdown.js +43 -0
  113. package/src/vite-plugin.d.ts +46 -0
  114. package/src/vite-plugin.js +221 -0
  115. package/wasm/package.json +16 -0
  116. package/wasm/wasm_api.d.ts +72 -0
  117. package/wasm/wasm_api.js +593 -0
  118. package/wasm/wasm_api_bg.wasm +0 -0
  119. package/wasm/wasm_api_bg.wasm.d.ts +10 -0
@@ -0,0 +1,281 @@
1
+ // Custom behavior for <cx-split-button> — primary click, dropdown toggle, keyboard nav.
2
+ //
3
+ // The Rust component renders:
4
+ // <div role="group" aria-label="..."> — button group
5
+ // <button part="primary"> — primary action
6
+ // <button part="trigger" aria-haspopup="menu" aria-expanded="false"
7
+ // data-floating-trigger data-floating-target="{menu_id}"> — dropdown toggle
8
+ // <div id="{menu_id}" role="menu" data-menu data-floating> — dropdown menu
9
+ // <button role="menuitem" data-action-id="{id}"> — each entry
10
+ // <div role="separator"> — dividers
11
+ //
12
+ // This Custom Element wires up:
13
+ // - Click primary → cx-click event
14
+ // - Click trigger → toggle dropdown menu
15
+ // - Click menu item → cx-action event + close
16
+ // - Click outside / focus exit → close
17
+ // - Keyboard: Enter/Space on trigger, ArrowDown opens, arrow nav in menu, Escape closes
18
+ //
19
+ // Source: crates/wasm-api/src/split_button.rs
20
+
21
+ let _sheet;
22
+ function getSheet() {
23
+ if (!_sheet) {
24
+ _sheet = new CSSStyleSheet();
25
+ _sheet.replaceSync([
26
+ ':host { display: inline-flex; }',
27
+ // Dropdown uses position:fixed (set by JS) to escape scroll container clipping.
28
+ '[data-floating] {',
29
+ ' z-index: 50;',
30
+ '}',
31
+ // Hover effect for menu items
32
+ '[role="menuitem"]:not([aria-disabled="true"]):hover {',
33
+ ' background-color: var(--color-secondary);',
34
+ ' cursor: pointer;',
35
+ '}',
36
+ // Focus indicator
37
+ '[role="menuitem"]:focus-visible {',
38
+ ' background-color: var(--color-secondary);',
39
+ ' outline: none;',
40
+ '}',
41
+ ].join('\n'));
42
+ }
43
+ return _sheet;
44
+ }
45
+
46
+ export function defineCxSplitButton(wasmFn, baseClass) {
47
+ class CxSplitButton extends baseClass {
48
+ static observedAttributes = ['id', 'label', 'entries', 'variant', 'intent', 'shape', 'size', 'icon-leading', 'disabled', 'primary-disabled', 'trigger-disabled', 'trigger-label'];
49
+ static _booleanAttrs = new Set(['disabled', 'primary-disabled', 'trigger-disabled']);
50
+
51
+ #outsideClick = null;
52
+
53
+ connectedCallback() {
54
+ if (!this._isInitialized) {
55
+ this._markInitialized();
56
+ const shadow = this._shadow;
57
+
58
+ const sheet = getSheet();
59
+ if (!shadow.adoptedStyleSheets.includes(sheet)) {
60
+ shadow.adoptedStyleSheets = [...shadow.adoptedStyleSheets, sheet];
61
+ }
62
+
63
+ // ── Click handler ──
64
+ shadow.addEventListener('click', (e) => {
65
+ // Trigger button → toggle dropdown
66
+ const trigger = e.target.closest('[data-floating-trigger]');
67
+ if (trigger && !trigger.disabled && !trigger.hasAttribute('aria-disabled')) {
68
+ e.preventDefault();
69
+ this.#toggle();
70
+ return;
71
+ }
72
+
73
+ // Primary button → emit cx-click
74
+ const primary = e.target.closest('[part="primary"]');
75
+ if (primary && !primary.disabled) {
76
+ this._emit('cx-click', { originalEvent: e });
77
+ return;
78
+ }
79
+
80
+ // Menu item → emit cx-action, close
81
+ const menuitem = e.target.closest('[role="menuitem"]');
82
+ if (menuitem && !menuitem.hasAttribute('aria-disabled')) {
83
+ this._emit('cx-action', {
84
+ id: menuitem.getAttribute('data-action-id') || menuitem.id || '',
85
+ label: menuitem.textContent.trim()
86
+ });
87
+ this.#close();
88
+ }
89
+ });
90
+
91
+ // ── Keyboard handler ──
92
+ shadow.addEventListener('keydown', (e) => this.#handleKey(e));
93
+
94
+ // ── Click outside → close ──
95
+ this.#outsideClick = (e) => {
96
+ if (this.#isOpen() && !this.contains(e.target) && !this._shadow.contains(e.target)) {
97
+ this.#close();
98
+ }
99
+ };
100
+ document.addEventListener('mousedown', this.#outsideClick);
101
+
102
+ // ── Focus exit → close ──
103
+ // Must check both light DOM and shadow DOM — Node.contains() doesn't cross shadow boundaries.
104
+ shadow.addEventListener('focusout', () => {
105
+ setTimeout(() => {
106
+ if (this.#isOpen()) {
107
+ const active = this._shadow.activeElement || document.activeElement;
108
+ if (!this.contains(active) && !this._shadow.contains(active) && active !== this) {
109
+ this.#close();
110
+ }
111
+ }
112
+ }, 0);
113
+ });
114
+ } // end _isInitialized guard
115
+ super.connectedCallback();
116
+ }
117
+
118
+ disconnectedCallback() {
119
+ if (this.#outsideClick) {
120
+ document.removeEventListener('mousedown', this.#outsideClick);
121
+ this.#outsideClick = null;
122
+ }
123
+ super.disconnectedCallback();
124
+ }
125
+
126
+ // ── DOM accessors ──
127
+
128
+ #getTrigger() {
129
+ return this._shadow.querySelector('[data-floating-trigger]');
130
+ }
131
+
132
+ #getMenu() {
133
+ return this._shadow.querySelector('[role="menu"]');
134
+ }
135
+
136
+ #getItems() {
137
+ return Array.from(
138
+ this._shadow.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')
139
+ );
140
+ }
141
+
142
+ // ── Menu state ──
143
+
144
+ #isOpen() {
145
+ const menu = this.#getMenu();
146
+ return menu ? menu.hasAttribute('data-open') : false;
147
+ }
148
+
149
+ #open() {
150
+ const trigger = this.#getTrigger();
151
+ const menu = this.#getMenu();
152
+ if (!trigger || !menu || this.#isOpen()) return;
153
+
154
+ // Position with fixed coordinates — escapes overflow:auto/scroll clipping.
155
+ this._positionFloatingFixed(trigger, menu, { matchWidth: true });
156
+
157
+ trigger.setAttribute('aria-expanded', 'true');
158
+ menu.setAttribute('data-open', '');
159
+ menu.classList.remove('hidden');
160
+ menu.style.display = 'block';
161
+ menu.style.pointerEvents = 'auto';
162
+ menu.style.opacity = '1';
163
+
164
+ // Focus first menu item
165
+ requestAnimationFrame(() => {
166
+ const items = this.#getItems();
167
+ if (items.length) items[0].focus();
168
+ });
169
+ }
170
+
171
+ #close() {
172
+ const trigger = this.#getTrigger();
173
+ const menu = this.#getMenu();
174
+ if (!trigger || !menu) return;
175
+
176
+ trigger.setAttribute('aria-expanded', 'false');
177
+ menu.removeAttribute('data-open');
178
+ menu.classList.add('hidden');
179
+ menu.style.display = '';
180
+ menu.style.pointerEvents = '';
181
+ menu.style.opacity = '';
182
+ this._resetFloatingFixed(menu);
183
+ trigger.focus();
184
+ }
185
+
186
+ #toggle() {
187
+ if (this.#isOpen()) this.#close();
188
+ else this.#open();
189
+ }
190
+
191
+ // ── Keyboard navigation ──
192
+
193
+ #handleKey(e) {
194
+ // Trigger: Enter/Space toggle
195
+ const trigger = e.target.closest('[data-floating-trigger]');
196
+ if (trigger) {
197
+ if (e.key === 'Enter' || e.key === ' ') {
198
+ e.preventDefault();
199
+ this.#toggle();
200
+ return;
201
+ }
202
+ // ArrowDown on trigger opens menu
203
+ if (e.key === 'ArrowDown') {
204
+ e.preventDefault();
205
+ if (!this.#isOpen()) this.#open();
206
+ return;
207
+ }
208
+ }
209
+
210
+ // Menu item navigation
211
+ const menuitem = e.target.closest('[role="menuitem"]');
212
+ if (!menuitem) return;
213
+
214
+ const items = this.#getItems();
215
+ const idx = items.indexOf(menuitem);
216
+ if (idx === -1) return;
217
+
218
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
219
+ e.preventDefault();
220
+ const ni = e.key === 'ArrowDown'
221
+ ? (idx + 1) % items.length
222
+ : (idx - 1 + items.length) % items.length;
223
+ items[ni].focus();
224
+ return;
225
+ }
226
+
227
+ if (e.key === 'Home') {
228
+ e.preventDefault();
229
+ if (items.length) items[0].focus();
230
+ return;
231
+ }
232
+
233
+ if (e.key === 'End') {
234
+ e.preventDefault();
235
+ if (items.length) items[items.length - 1].focus();
236
+ return;
237
+ }
238
+
239
+ if (e.key === 'Enter' || e.key === ' ') {
240
+ e.preventDefault();
241
+ if (!menuitem.hasAttribute('aria-disabled')) menuitem.click();
242
+ return;
243
+ }
244
+
245
+ if (e.key === 'Escape') {
246
+ e.preventDefault();
247
+ this.#close();
248
+ return;
249
+ }
250
+
251
+ if (e.key === 'Tab') {
252
+ this.#close();
253
+ }
254
+ }
255
+
256
+ // ── Public imperative API ──
257
+ open() { this.#open(); }
258
+ close() { this.#close(); }
259
+
260
+ _doRender() {
261
+ try {
262
+ const wasOpen = this.#isOpen();
263
+
264
+ const result = wasmFn(this._props);
265
+ this._injectHtml(result);
266
+
267
+ const sheet = getSheet();
268
+ if (!this._shadow.adoptedStyleSheets.includes(sheet)) {
269
+ this._shadow.adoptedStyleSheets = [...this._shadow.adoptedStyleSheets, sheet];
270
+ }
271
+
272
+ if (wasOpen) this.#open();
273
+ } catch (e) {
274
+ console.error('[cx-split-button]', e);
275
+ }
276
+ }
277
+ }
278
+
279
+ customElements.define('cx-split-button', CxSplitButton);
280
+ return CxSplitButton;
281
+ }
@@ -0,0 +1,20 @@
1
+ // Auto-generated by scripts/generate-elements.mjs — DO NOT EDIT
2
+ // Source: crates/wasm-api/src/stepper.rs
3
+
4
+ export interface CxStepperAttributes {
5
+ id?: string;
6
+ label?: string;
7
+ steps?: string;
8
+ current?: number;
9
+ variant?: 'outline' | 'filled' | 'ghost';
10
+ shape?: 'sharp' | 'rounded';
11
+ orientation?: 'horizontal' | 'vertical';
12
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
13
+ linear?: boolean;
14
+ }
15
+
16
+ declare global {
17
+ interface HTMLElementTagNameMap {
18
+ 'cx-stepper': HTMLElement & CxStepperAttributes;
19
+ }
20
+ }
@@ -0,0 +1,31 @@
1
+ // Auto-generated by scripts/generate-elements.mjs — DO NOT EDIT
2
+ // Source: crates/wasm-api/src/stepper.rs
3
+
4
+ export function defineCxStepper(wasmFn, baseClass) {
5
+ class CxStepper extends baseClass {
6
+ static observedAttributes = ['id', 'label', 'steps', 'current', 'variant', 'shape', 'orientation', 'size', 'linear'];
7
+ static _booleanAttrs = new Set(['linear']);
8
+ static _numericAttrs = new Set(['current']);
9
+ static _focusable = false;
10
+ static _hostDisplay = 'block';
11
+
12
+ set current(v) { this._setProp('current', v); }
13
+ get current() { return this._props.current; }
14
+
15
+ connectedCallback() {
16
+ super.connectedCallback();
17
+ }
18
+
19
+ _doRender() {
20
+ try {
21
+ const result = wasmFn(this._props);
22
+ this._injectHtml(result);
23
+ } catch (e) {
24
+ console.error('[cx-stepper]', e);
25
+ }
26
+ }
27
+ }
28
+
29
+ customElements.define('cx-stepper', CxStepper);
30
+ return CxStepper;
31
+ }
@@ -0,0 +1,22 @@
1
+ // Auto-generated by scripts/generate-elements.mjs — DO NOT EDIT
2
+ // Source: crates/wasm-api/src/switch.rs
3
+
4
+ export interface CxSwitchAttributes {
5
+ id?: string;
6
+ label?: string;
7
+ shape?: 'sharp' | 'rounded' | 'pill';
8
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
+ checked?: boolean;
10
+ disabled?: boolean;
11
+ required?: boolean;
12
+ helperText?: string;
13
+ error?: string;
14
+ description?: string;
15
+ unlabeled?: boolean;
16
+ }
17
+
18
+ declare global {
19
+ interface HTMLElementTagNameMap {
20
+ 'cx-switch': HTMLElement & CxSwitchAttributes;
21
+ }
22
+ }
@@ -0,0 +1,129 @@
1
+ // Custom behavior for <cx-switch> — toggle with CSS transition animation.
2
+ // Source: crates/wasm-api/src/switch.rs
3
+
4
+ // Size → thumb translate distance mapping (mirrors Rust switch/styles.rs).
5
+ // Values match Tailwind's spacing scale: translate-x-N = N * 0.25rem.
6
+ const THUMB_TRANSLATE = {
7
+ xs: '0.75rem', // translate-x-3
8
+ sm: '0.875rem', // translate-x-3.5
9
+ md: '1rem', // translate-x-4
10
+ lg: '1.25rem', // translate-x-5
11
+ xl: '1.25rem', // translate-x-5
12
+ };
13
+
14
+ export function defineCxSwitch(wasmFn, baseClass) {
15
+ class CxSwitch extends baseClass {
16
+ static observedAttributes = ['id', 'label', 'shape', 'size', 'checked', 'disabled', 'required', 'helper-text', 'error', 'description', 'unlabeled'];
17
+ static _booleanAttrs = new Set(['checked', 'disabled', 'required', 'unlabeled']);
18
+ static _hostDisplay = 'inline-flex';
19
+
20
+ // Thumb translate distance for current size.
21
+ #thumbDistance = THUMB_TRANSLATE.md;
22
+
23
+ connectedCallback() {
24
+ if (!this._isInitialized) {
25
+ this._markInitialized();
26
+ const shadow = this._shadow;
27
+
28
+ // Switch toggle: manipulate DOM directly (not re-render) so CSS
29
+ // transitions can animate the thumb slide and track color change.
30
+ shadow.addEventListener('click', (e) => {
31
+ const btn = e.target.closest('button[role="switch"]');
32
+ if (!btn || this._props.disabled) return;
33
+
34
+ const isChecked = btn.getAttribute('aria-checked') === 'true';
35
+ const newChecked = !isChecked;
36
+
37
+ // Toggle ARIA state
38
+ btn.setAttribute('aria-checked', String(newChecked));
39
+
40
+ // Toggle track background color
41
+ btn.style.backgroundColor = newChecked
42
+ ? 'var(--color-primary)' : 'var(--color-secondary)';
43
+
44
+ // Toggle thumb position via inline translate.
45
+ // Using style.translate instead of class toggle ensures both states
46
+ // have explicit values, so the CSS transition always animates.
47
+ const thumb = btn.querySelector('span[aria-hidden="true"]');
48
+ if (thumb) {
49
+ thumb.style.translate = newChecked ? `${this.#thumbDistance} 0` : '0 0';
50
+ }
51
+
52
+ // Toggle hidden input
53
+ const input = shadow.querySelector('input[type="checkbox"]');
54
+ if (input) input.checked = newChecked;
55
+
56
+ // Sync props (for next full re-render from attribute change)
57
+ this._props.checked = newChecked;
58
+
59
+ // Form participation
60
+ this._setFormValue(newChecked ? 'on' : '');
61
+ this._emit('cx-input', { checked: newChecked });
62
+ this._emit('cx-change', { checked: newChecked });
63
+ });
64
+
65
+ // Forward focus events from inner interactive elements
66
+ shadow.addEventListener('focusin', (e) => {
67
+ this._emit('cx-focus', { relatedTarget: e.relatedTarget });
68
+ });
69
+ shadow.addEventListener('focusout', (e) => {
70
+ this._emit('cx-blur', { relatedTarget: e.relatedTarget });
71
+ });
72
+
73
+ // Forward keyboard events from inner interactive elements
74
+ shadow.addEventListener('keydown', (e) => {
75
+ this._emit('cx-keydown', { key: e.key, code: e.code, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, altKey: e.altKey, metaKey: e.metaKey });
76
+ });
77
+ shadow.addEventListener('keyup', (e) => {
78
+ this._emit('cx-keyup', { key: e.key, code: e.code, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, altKey: e.altKey, metaKey: e.metaKey });
79
+ });
80
+ } // end _isInitialized guard
81
+ super.connectedCallback();
82
+ }
83
+
84
+ // Override: update thumb distance when size changes.
85
+ attributeChangedCallback(name, old, value) {
86
+ if (name === 'size') {
87
+ this.#thumbDistance = THUMB_TRANSLATE[value] || THUMB_TRANSLATE.md;
88
+ }
89
+ super.attributeChangedCallback(name, old, value);
90
+ }
91
+
92
+ // ── Public imperative API ──
93
+ focus() { const el = this._shadow.querySelector('button[role="switch"]'); if (el) el.focus(); else super.focus(); }
94
+
95
+ _doRender() {
96
+ try {
97
+ const result = wasmFn(this._props);
98
+ this._injectHtml(result);
99
+
100
+ // Set initial thumb translate so CSS transition has explicit
101
+ // start/end values. WASM renders checked state via Tailwind
102
+ // classes, but we need inline styles for the transition to work.
103
+ const thumb = this._shadow.querySelector(
104
+ 'button[role="switch"] > span[aria-hidden="true"]'
105
+ );
106
+ if (thumb) {
107
+ const isChecked = this._props.checked;
108
+ // Remove any Tailwind translate class (it would conflict with inline style)
109
+ for (const cls of [...thumb.classList]) {
110
+ if (cls.startsWith('translate-x-')) {
111
+ thumb.classList.remove(cls);
112
+ }
113
+ }
114
+ // Set explicit translate for transition animation
115
+ thumb.style.translate = isChecked ? `${this.#thumbDistance} 0` : '0 0';
116
+ }
117
+
118
+ // Sync form value
119
+ const input = this._shadow.querySelector('input[type="checkbox"]');
120
+ if (input) this._setFormValue(input.checked ? 'on' : '');
121
+ } catch (e) {
122
+ console.error('[cx-switch]', e);
123
+ }
124
+ }
125
+ }
126
+
127
+ customElements.define('cx-switch', CxSwitch);
128
+ return CxSwitch;
129
+ }
@@ -0,0 +1,29 @@
1
+ // Auto-generated by scripts/generate-elements.mjs — DO NOT EDIT
2
+ // Source: crates/wasm-api/src/table.rs
3
+
4
+ export interface CxTableAttributes {
5
+ id?: string;
6
+ caption: string;
7
+ columns?: string;
8
+ rows?: string;
9
+ variant?: 'plain' | 'striped' | 'bordered';
10
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+ selectable?: string;
12
+ selected?: string;
13
+ sorts?: string;
14
+ pagination?: string;
15
+ hoverable?: boolean;
16
+ stickyHeader?: boolean;
17
+ footer?: string;
18
+ emptyState?: string;
19
+ columnOrder?: string;
20
+ disabled?: boolean;
21
+ loading?: number;
22
+ error?: string;
23
+ }
24
+
25
+ declare global {
26
+ interface HTMLElementTagNameMap {
27
+ 'cx-table': HTMLElement & CxTableAttributes;
28
+ }
29
+ }