@adia-ai/web-components 0.4.4 → 0.4.6
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/README.md +63 -24
- package/USAGE.md +584 -0
- package/components/calendar-picker/calendar-picker.d.ts +27 -0
- package/components/calendar-picker/calendar-picker.js +1 -1
- package/components/check/check.d.ts +30 -0
- package/components/check/check.js +1 -1
- package/components/code/code.d.ts +39 -0
- package/components/color-picker/color-picker.d.ts +37 -0
- package/components/index.js +1 -0
- package/components/input/input.d.ts +61 -0
- package/components/input/input.js +9 -9
- package/components/option-card/option-card.d.ts +30 -0
- package/components/option-card/option-card.js +1 -1
- package/components/otp-input/otp-input.d.ts +25 -0
- package/components/otp-input/otp-input.js +3 -3
- package/components/pane/pane.css +10 -0
- package/components/pane/pane.js +28 -4
- package/components/radio/radio.d.ts +28 -0
- package/components/radio/radio.js +1 -1
- package/components/range/range.d.ts +31 -0
- package/components/range/range.js +3 -3
- package/components/rating/rating.d.ts +33 -0
- package/components/rating/rating.js +1 -1
- package/components/search/search.d.ts +35 -0
- package/components/search/search.js +2 -2
- package/components/segmented/segmented.d.ts +24 -0
- package/components/select/select.d.ts +57 -0
- package/components/select/select.js +2 -2
- package/components/slider/slider.d.ts +31 -0
- package/components/slider/slider.js +4 -4
- package/components/slider/slider.test.js +105 -0
- package/components/switch/switch.d.ts +30 -0
- package/components/switch/switch.js +1 -1
- package/components/textarea/textarea.d.ts +31 -0
- package/components/textarea/textarea.js +2 -2
- package/components/toggle-scheme/toggle-scheme.a2ui.json +197 -0
- package/components/toggle-scheme/toggle-scheme.css +20 -0
- package/components/toggle-scheme/toggle-scheme.js +277 -0
- package/components/toggle-scheme/toggle-scheme.yaml +173 -0
- package/components/upload/upload.d.ts +27 -0
- package/components/upload/upload.js +1 -1
- package/core/element.d.ts +174 -0
- package/core/form.d.ts +108 -0
- package/core/index.d.ts +11 -0
- package/core/index.js +1 -0
- package/core/register.d.ts +25 -0
- package/core/register.js +58 -0
- package/core/signals.d.ts +94 -0
- package/core/template.d.ts +70 -0
- package/index.d.ts +161 -0
- package/package.json +22 -6
- package/styles/design-tokens-export.js +554 -0
- package/traits/CATEGORIES.md +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<select-ui>` — Custom-styled select / combobox / multi-select.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/select
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface SelectOption {
|
|
10
|
+
value: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
avatar?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
action?: string;
|
|
16
|
+
divider?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SelectChangeEventDetail {
|
|
20
|
+
value: string;
|
|
21
|
+
}
|
|
22
|
+
export type SelectChangeEvent = CustomEvent<SelectChangeEventDetail>;
|
|
23
|
+
|
|
24
|
+
export interface SelectActionEventDetail {
|
|
25
|
+
action: string;
|
|
26
|
+
}
|
|
27
|
+
export type SelectActionEvent = CustomEvent<SelectActionEventDetail>;
|
|
28
|
+
|
|
29
|
+
export class UISelect extends UIFormElement {
|
|
30
|
+
placeholder: string;
|
|
31
|
+
/** Open/closed reflected attribute — toggled by trigger click / keyboard. */
|
|
32
|
+
open: boolean;
|
|
33
|
+
label: string;
|
|
34
|
+
/** Leading icon name (Phosphor registry). */
|
|
35
|
+
icon: string;
|
|
36
|
+
/** Leading avatar URL or name. */
|
|
37
|
+
avatar: string;
|
|
38
|
+
multiple: boolean;
|
|
39
|
+
searchable: boolean;
|
|
40
|
+
/** Allow values not in the option list (combobox mode). */
|
|
41
|
+
freeText: boolean;
|
|
42
|
+
divider: boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Dynamic option list. Setting `.options = [...]` stamps option elements at
|
|
46
|
+
* connection time + auto-syncs the listbox.
|
|
47
|
+
*/
|
|
48
|
+
options: readonly SelectOption[];
|
|
49
|
+
|
|
50
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
51
|
+
type: K,
|
|
52
|
+
listener: (this: UISelect, ev: HTMLElementEventMap[K]) => unknown,
|
|
53
|
+
options?: boolean | AddEventListenerOptions,
|
|
54
|
+
): void;
|
|
55
|
+
addEventListener(type: 'change', listener: (ev: SelectChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
56
|
+
addEventListener(type: 'action', listener: (ev: SelectActionEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
57
|
+
}
|
|
@@ -46,7 +46,7 @@ class UISelect extends UIFormElement {
|
|
|
46
46
|
this.open = false;
|
|
47
47
|
this.#query = '';
|
|
48
48
|
this.syncValue(opt.value);
|
|
49
|
-
this.dispatchEvent(new
|
|
49
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
connected() {
|
|
@@ -293,7 +293,7 @@ class UISelect extends UIFormElement {
|
|
|
293
293
|
this.#query = '';
|
|
294
294
|
this.open = false;
|
|
295
295
|
this.syncValue?.(q);
|
|
296
|
-
this.dispatchEvent(new
|
|
296
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
297
297
|
} else {
|
|
298
298
|
this.open = false;
|
|
299
299
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<slider-ui>` — Single-handle slider for numeric input.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/slider
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface SliderChangeEventDetail {
|
|
10
|
+
value: number;
|
|
11
|
+
}
|
|
12
|
+
export type SliderChangeEvent = CustomEvent<SliderChangeEventDetail>;
|
|
13
|
+
export type SliderInputEvent = CustomEvent<SliderChangeEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UISlider extends UIFormElement {
|
|
16
|
+
/** Numeric value — overrides UIFormElement.value (which is String). */
|
|
17
|
+
value: number;
|
|
18
|
+
min: number;
|
|
19
|
+
max: number;
|
|
20
|
+
step: number;
|
|
21
|
+
label: string;
|
|
22
|
+
suffix: string;
|
|
23
|
+
|
|
24
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
25
|
+
type: K,
|
|
26
|
+
listener: (this: UISlider, ev: HTMLElementEventMap[K]) => unknown,
|
|
27
|
+
options?: boolean | AddEventListenerOptions,
|
|
28
|
+
): void;
|
|
29
|
+
addEventListener(type: 'change', listener: (ev: SliderChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
30
|
+
addEventListener(type: 'input', listener: (ev: SliderInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
31
|
+
}
|
|
@@ -110,7 +110,7 @@ class UISlider extends UIFormElement {
|
|
|
110
110
|
#setValue(v) {
|
|
111
111
|
if (v === this.value) return;
|
|
112
112
|
this.value = v;
|
|
113
|
-
this.dispatchEvent(new
|
|
113
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
#onPointerDown = (e) => {
|
|
@@ -132,13 +132,13 @@ class UISlider extends UIFormElement {
|
|
|
132
132
|
this.#thumbEl.releasePointerCapture(e.pointerId);
|
|
133
133
|
this.#thumbEl.removeEventListener('pointermove', this.#onPointerMove);
|
|
134
134
|
this.#thumbEl.removeEventListener('pointerup', this.#onPointerUp);
|
|
135
|
-
this.dispatchEvent(new
|
|
135
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
136
136
|
};
|
|
137
137
|
|
|
138
138
|
#onTrackClick = (e) => {
|
|
139
139
|
if (this.disabled || e.target === this.#thumbEl) return;
|
|
140
140
|
this.#setValue(this.#valueFromX(e.clientX));
|
|
141
|
-
this.dispatchEvent(new
|
|
141
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
142
142
|
};
|
|
143
143
|
|
|
144
144
|
#onKey = (e) => {
|
|
@@ -155,7 +155,7 @@ class UISlider extends UIFormElement {
|
|
|
155
155
|
}
|
|
156
156
|
e.preventDefault();
|
|
157
157
|
this.#setValue(this.#snap(v));
|
|
158
|
-
this.dispatchEvent(new
|
|
158
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
disconnected() {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import '../../core/element.js';
|
|
3
|
+
import './slider.js';
|
|
4
|
+
|
|
5
|
+
const tick = () => new Promise((r) => queueMicrotask(r));
|
|
6
|
+
|
|
7
|
+
function mount(html) {
|
|
8
|
+
const wrap = document.createElement('div');
|
|
9
|
+
wrap.innerHTML = html;
|
|
10
|
+
document.body.appendChild(wrap);
|
|
11
|
+
return wrap.firstElementChild;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('slider-ui', () => {
|
|
15
|
+
beforeEach(() => { document.body.innerHTML = ''; });
|
|
16
|
+
|
|
17
|
+
it('renders thumb at correct % for initial value', async () => {
|
|
18
|
+
const s = mount('<slider-ui value="50" min="0" max="100"></slider-ui>');
|
|
19
|
+
await tick();
|
|
20
|
+
const thumb = s.querySelector('[slot="thumb"]');
|
|
21
|
+
expect(thumb.style.left).toBe('50%');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Property reactivity contract — locks in the behavior that consumer
|
|
25
|
+
// feedback (FEEDBACK-adia-packages.md §5, 2026-05-12) incorrectly
|
|
26
|
+
// claimed was broken. UIElement's signal-backed property setters
|
|
27
|
+
// (core/element.js:31-50) trigger the host's render effect on every
|
|
28
|
+
// property change; slider-ui's render() reads this.value and updates
|
|
29
|
+
// [slot="thumb"].style.left. This test catches any regression that
|
|
30
|
+
// would break undo/redo or any other programmatic-value flow.
|
|
31
|
+
it('moves thumb when .value is set programmatically', async () => {
|
|
32
|
+
const s = mount('<slider-ui value="50" min="0" max="100"></slider-ui>');
|
|
33
|
+
await tick();
|
|
34
|
+
const thumb = s.querySelector('[slot="thumb"]');
|
|
35
|
+
expect(thumb.style.left).toBe('50%');
|
|
36
|
+
|
|
37
|
+
s.value = 75;
|
|
38
|
+
await tick();
|
|
39
|
+
expect(thumb.style.left).toBe('75%');
|
|
40
|
+
|
|
41
|
+
s.value = 10;
|
|
42
|
+
await tick();
|
|
43
|
+
expect(thumb.style.left).toBe('10%');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('moves thumb when attribute is set imperatively', async () => {
|
|
47
|
+
const s = mount('<slider-ui value="50" min="0" max="100"></slider-ui>');
|
|
48
|
+
await tick();
|
|
49
|
+
const thumb = s.querySelector('[slot="thumb"]');
|
|
50
|
+
|
|
51
|
+
s.setAttribute('value', '25');
|
|
52
|
+
await tick();
|
|
53
|
+
expect(thumb.style.left).toBe('25%');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('updates [slot="value"] readout text when .value changes', async () => {
|
|
57
|
+
const s = mount('<slider-ui value="50" min="0" max="100"></slider-ui>');
|
|
58
|
+
await tick();
|
|
59
|
+
const readout = s.querySelector('[slot="value"]');
|
|
60
|
+
expect(readout.textContent).toBe('50');
|
|
61
|
+
|
|
62
|
+
s.value = 88;
|
|
63
|
+
await tick();
|
|
64
|
+
expect(readout.textContent).toBe('88');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('emits CustomEvent("change") with detail.value on keyboard step', async () => {
|
|
68
|
+
const s = mount('<slider-ui value="50" min="0" max="100" step="1"></slider-ui>');
|
|
69
|
+
await tick();
|
|
70
|
+
|
|
71
|
+
let captured = null;
|
|
72
|
+
s.addEventListener('change', (e) => { captured = e; });
|
|
73
|
+
|
|
74
|
+
s.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }));
|
|
75
|
+
await tick();
|
|
76
|
+
|
|
77
|
+
expect(captured).not.toBeNull();
|
|
78
|
+
expect(captured).toBeInstanceOf(CustomEvent);
|
|
79
|
+
expect(captured.detail).toEqual({ value: 51 });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('emits CustomEvent("input") with detail.value on internal value change', async () => {
|
|
83
|
+
const s = mount('<slider-ui value="50" min="0" max="100" step="1"></slider-ui>');
|
|
84
|
+
await tick();
|
|
85
|
+
|
|
86
|
+
let captured = null;
|
|
87
|
+
s.addEventListener('input', (e) => { captured = e; });
|
|
88
|
+
|
|
89
|
+
s.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }));
|
|
90
|
+
await tick();
|
|
91
|
+
|
|
92
|
+
expect(captured).not.toBeNull();
|
|
93
|
+
expect(captured.detail).toEqual({ value: 51 });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('reflects value to [aria-valuenow] for screen readers', async () => {
|
|
97
|
+
const s = mount('<slider-ui value="50" min="0" max="100"></slider-ui>');
|
|
98
|
+
await tick();
|
|
99
|
+
expect(s.getAttribute('aria-valuenow')).toBe('50');
|
|
100
|
+
|
|
101
|
+
s.value = 80;
|
|
102
|
+
await tick();
|
|
103
|
+
expect(s.getAttribute('aria-valuenow')).toBe('80');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<switch-ui>` — Toggle switch (boolean-semantic form control).
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/switch
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface SwitchChangeEventDetail {
|
|
10
|
+
/** Submitted value (defaults to `"on"` when checked). */
|
|
11
|
+
value: string;
|
|
12
|
+
/** Current checked state. */
|
|
13
|
+
checked: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type SwitchChangeEvent = CustomEvent<SwitchChangeEventDetail>;
|
|
16
|
+
|
|
17
|
+
export class UISwitch extends UIFormElement {
|
|
18
|
+
/** Checked state — reflected, toggles on click. */
|
|
19
|
+
checked: boolean;
|
|
20
|
+
label: string;
|
|
21
|
+
/** Size — `sm` / `md` / `lg`. */
|
|
22
|
+
size: '' | 'sm' | 'md' | 'lg';
|
|
23
|
+
|
|
24
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
25
|
+
type: K,
|
|
26
|
+
listener: (this: UISwitch, ev: HTMLElementEventMap[K]) => unknown,
|
|
27
|
+
options?: boolean | AddEventListenerOptions,
|
|
28
|
+
): void;
|
|
29
|
+
addEventListener(type: 'change', listener: (ev: SwitchChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
30
|
+
}
|
|
@@ -35,7 +35,7 @@ class UISwitch extends UIFormElement {
|
|
|
35
35
|
#toggle = () => {
|
|
36
36
|
if (this.disabled) return;
|
|
37
37
|
this.checked = !this.checked;
|
|
38
|
-
this.dispatchEvent(new
|
|
38
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value, checked: this.checked } }));
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
#onKey = (e) => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<textarea-ui>` — Multi-line text input with Enter-submit semantic.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/textarea
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface TextareaChangeEventDetail {
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
export type TextareaChangeEvent = CustomEvent<TextareaChangeEventDetail>;
|
|
13
|
+
export type TextareaInputEvent = CustomEvent<TextareaChangeEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UITextarea extends UIFormElement {
|
|
16
|
+
placeholder: string;
|
|
17
|
+
label: string;
|
|
18
|
+
rows: number;
|
|
19
|
+
/** CSS `resize` value — `none` / `vertical` (default) / `horizontal` / `both`. */
|
|
20
|
+
resize: 'none' | 'vertical' | 'horizontal' | 'both';
|
|
21
|
+
|
|
22
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
23
|
+
type: K,
|
|
24
|
+
listener: (this: UITextarea, ev: HTMLElementEventMap[K]) => unknown,
|
|
25
|
+
options?: boolean | AddEventListenerOptions,
|
|
26
|
+
): void;
|
|
27
|
+
addEventListener(type: 'change', listener: (ev: TextareaChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
28
|
+
addEventListener(type: 'input', listener: (ev: TextareaInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
29
|
+
/** Fired on Enter without Shift; user can `e.preventDefault()` to insert newline. */
|
|
30
|
+
addEventListener(type: 'submit', listener: (ev: Event) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
31
|
+
}
|
|
@@ -76,7 +76,7 @@ class UITextarea extends UIFormElement {
|
|
|
76
76
|
this.value = text;
|
|
77
77
|
this.#textEl.toggleAttribute('data-empty', !text);
|
|
78
78
|
this.syncValue(text);
|
|
79
|
-
this.dispatchEvent(new
|
|
79
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
#onKeydown = (e) => {
|
|
@@ -88,7 +88,7 @@ class UITextarea extends UIFormElement {
|
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
#onBlur = () => {
|
|
91
|
-
this.dispatchEvent(new
|
|
91
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
#onPaste = (e) => {
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/ToggleScheme.json",
|
|
4
|
+
"title": "ToggleScheme",
|
|
5
|
+
"description": "Icon-button primitive that toggles `color-scheme` between light and dark on a target element (default `:root`). Encapsulates the prefers-color-scheme initial-resolution, the inline-style write to the target, optional localStorage persistence (sharing the `adia-theme-` namespace with <theme-panel>), and the moon/sun icon swap. Replaces the hand-wired `<button-ui id=\"theme-toggle\">` + `applyScheme()` pattern previously duplicated across apps/genui, apps/construct-canvas, playgrounds/chat.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"activeScheme": {
|
|
17
|
+
"description": "Resolved scheme (\"light\" | \"dark\"). Read-only; mutate via .setScheme() or .toggle().",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": [
|
|
20
|
+
"",
|
|
21
|
+
"light",
|
|
22
|
+
"dark"
|
|
23
|
+
],
|
|
24
|
+
"default": ""
|
|
25
|
+
},
|
|
26
|
+
"color": {
|
|
27
|
+
"description": "Semantic intent forwarded to the internal <button-ui>.",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": [
|
|
30
|
+
"",
|
|
31
|
+
"default",
|
|
32
|
+
"accent",
|
|
33
|
+
"info",
|
|
34
|
+
"success",
|
|
35
|
+
"warning",
|
|
36
|
+
"danger"
|
|
37
|
+
],
|
|
38
|
+
"default": ""
|
|
39
|
+
},
|
|
40
|
+
"component": {
|
|
41
|
+
"const": "ToggleScheme"
|
|
42
|
+
},
|
|
43
|
+
"disabled": {
|
|
44
|
+
"description": "Disables the internal button.",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": false
|
|
47
|
+
},
|
|
48
|
+
"iconDark": {
|
|
49
|
+
"description": "Phosphor icon shown when active-scheme is \"dark\" (clicking switches to light).",
|
|
50
|
+
"type": "string",
|
|
51
|
+
"default": "moon"
|
|
52
|
+
},
|
|
53
|
+
"iconLight": {
|
|
54
|
+
"description": "Phosphor icon shown when active-scheme is \"light\" (clicking switches to dark).",
|
|
55
|
+
"type": "string",
|
|
56
|
+
"default": "sun"
|
|
57
|
+
},
|
|
58
|
+
"labelDark": {
|
|
59
|
+
"description": "aria-label / title applied to the internal button when active-scheme is \"dark\".",
|
|
60
|
+
"type": "string",
|
|
61
|
+
"default": "Switch to light mode"
|
|
62
|
+
},
|
|
63
|
+
"labelLight": {
|
|
64
|
+
"description": "aria-label / title applied to the internal button when active-scheme is \"light\".",
|
|
65
|
+
"type": "string",
|
|
66
|
+
"default": "Switch to dark mode"
|
|
67
|
+
},
|
|
68
|
+
"persist": {
|
|
69
|
+
"description": "Persist the user's choice to localStorage under `${storage-prefix}scheme`.",
|
|
70
|
+
"type": "boolean",
|
|
71
|
+
"default": false
|
|
72
|
+
},
|
|
73
|
+
"scheme": {
|
|
74
|
+
"description": "Initial scheme — \"auto\" follows prefers-color-scheme; \"light\" / \"dark\" force.",
|
|
75
|
+
"type": "string",
|
|
76
|
+
"enum": [
|
|
77
|
+
"auto",
|
|
78
|
+
"light",
|
|
79
|
+
"dark"
|
|
80
|
+
],
|
|
81
|
+
"default": "auto"
|
|
82
|
+
},
|
|
83
|
+
"size": {
|
|
84
|
+
"description": "Sizing scale forwarded to the internal <button-ui>.",
|
|
85
|
+
"type": "string",
|
|
86
|
+
"enum": [
|
|
87
|
+
"xs",
|
|
88
|
+
"sm",
|
|
89
|
+
"md",
|
|
90
|
+
"lg",
|
|
91
|
+
"xl"
|
|
92
|
+
],
|
|
93
|
+
"default": "md"
|
|
94
|
+
},
|
|
95
|
+
"storagePrefix": {
|
|
96
|
+
"description": "LocalStorage key prefix; default matches <theme-panel> for cross-element interop.",
|
|
97
|
+
"type": "string",
|
|
98
|
+
"default": "adia-theme-"
|
|
99
|
+
},
|
|
100
|
+
"target": {
|
|
101
|
+
"description": "CSS selector for the element receiving `color-scheme`. \":root\" → <html>.",
|
|
102
|
+
"type": "string",
|
|
103
|
+
"default": ":root"
|
|
104
|
+
},
|
|
105
|
+
"variant": {
|
|
106
|
+
"description": "Visual style forwarded to the internal <button-ui>. Defaults to \"ghost\" (matches app practice).",
|
|
107
|
+
"type": "string",
|
|
108
|
+
"enum": [
|
|
109
|
+
"default",
|
|
110
|
+
"solid",
|
|
111
|
+
"outline",
|
|
112
|
+
"ghost",
|
|
113
|
+
"primary",
|
|
114
|
+
"secondary",
|
|
115
|
+
"soft"
|
|
116
|
+
],
|
|
117
|
+
"default": "ghost"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"required": [
|
|
121
|
+
"component"
|
|
122
|
+
],
|
|
123
|
+
"unevaluatedProperties": false,
|
|
124
|
+
"x-adiaui": {
|
|
125
|
+
"anti_patterns": [],
|
|
126
|
+
"category": "control",
|
|
127
|
+
"events": {
|
|
128
|
+
"scheme-change": {
|
|
129
|
+
"description": "Fired when the active scheme changes. detail contains { scheme: \"light\" | \"dark\", source: \"press\" | \"media\" | \"programmatic\" }."
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"examples": [
|
|
133
|
+
{
|
|
134
|
+
"description": "Drop into a toolbar / app header as a single icon-button affordance.",
|
|
135
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"ToggleScheme\",\n \"persist\": true\n }\n]",
|
|
136
|
+
"name": "header-action"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"keywords": [
|
|
140
|
+
"scheme",
|
|
141
|
+
"color-scheme",
|
|
142
|
+
"dark-mode",
|
|
143
|
+
"light-mode",
|
|
144
|
+
"theme",
|
|
145
|
+
"toggle",
|
|
146
|
+
"moon",
|
|
147
|
+
"sun"
|
|
148
|
+
],
|
|
149
|
+
"name": "UIToggleScheme",
|
|
150
|
+
"related": [
|
|
151
|
+
"Button",
|
|
152
|
+
"Switch"
|
|
153
|
+
],
|
|
154
|
+
"slots": {},
|
|
155
|
+
"states": [
|
|
156
|
+
{
|
|
157
|
+
"description": "Default, ready for interaction.",
|
|
158
|
+
"name": "idle"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"description": "Active scheme is light.",
|
|
162
|
+
"attribute": "active-scheme",
|
|
163
|
+
"name": "light"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"description": "Active scheme is dark.",
|
|
167
|
+
"attribute": "active-scheme",
|
|
168
|
+
"name": "dark"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"description": "Non-interactive; the internal button is disabled.",
|
|
172
|
+
"attribute": "disabled",
|
|
173
|
+
"name": "disabled"
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"synonyms": {
|
|
177
|
+
"dark-mode": [
|
|
178
|
+
"toggle-scheme"
|
|
179
|
+
],
|
|
180
|
+
"theme-toggle": [
|
|
181
|
+
"toggle-scheme"
|
|
182
|
+
],
|
|
183
|
+
"toggle": [
|
|
184
|
+
"toggle-scheme",
|
|
185
|
+
"switch"
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
"tag": "toggle-scheme-ui",
|
|
189
|
+
"tokens": {
|
|
190
|
+
"--toggle-scheme-icon-transition": {
|
|
191
|
+
"description": "Duration + easing for icon-color transition when scheme flips."
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"traits": [],
|
|
195
|
+
"version": 1
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@scope (toggle-scheme-ui) {
|
|
2
|
+
:where(:scope) {
|
|
3
|
+
--toggle-scheme-icon-transition: var(--a-duration-fast) var(--a-easing);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
:scope {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
line-height: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:scope[disabled] {
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:scope > [part="button"] {
|
|
18
|
+
transition: color var(--toggle-scheme-icon-transition);
|
|
19
|
+
}
|
|
20
|
+
}
|