@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,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<code-ui>` — Syntax-highlighted code block (CodeMirror 6 under the hood).
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/code
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface CodeChangeEventDetail {
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
export type CodeChangeEvent = CustomEvent<CodeChangeEventDetail>;
|
|
13
|
+
export type CodeInputEvent = CustomEvent<CodeChangeEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UICode extends UIFormElement {
|
|
16
|
+
/** CodeMirror language slug — `javascript` / `typescript` / `html` / `css` / `json` / `markdown` / `python` etc. */
|
|
17
|
+
language: string;
|
|
18
|
+
/** Inline mode — drops chrome, renders as a single-line code fragment. */
|
|
19
|
+
inline: boolean;
|
|
20
|
+
/** Text content of the code block (alternative to slotted children). */
|
|
21
|
+
text: string;
|
|
22
|
+
/** Alias for `text` — set via `el.textContent` round-trips. */
|
|
23
|
+
textContent: string;
|
|
24
|
+
/** Show line numbers in the gutter. */
|
|
25
|
+
lineNumbers: boolean;
|
|
26
|
+
/** Editable mode — user can type. Form-associated only when this is true. */
|
|
27
|
+
editable: boolean;
|
|
28
|
+
/** Drop chrome (border, background) for inline composition. */
|
|
29
|
+
bare: boolean;
|
|
30
|
+
placeholder: string;
|
|
31
|
+
|
|
32
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
33
|
+
type: K,
|
|
34
|
+
listener: (this: UICode, ev: HTMLElementEventMap[K]) => unknown,
|
|
35
|
+
options?: boolean | AddEventListenerOptions,
|
|
36
|
+
): void;
|
|
37
|
+
addEventListener(type: 'change', listener: (ev: CodeChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
38
|
+
addEventListener(type: 'input', listener: (ev: CodeInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<color-picker-ui>` — Color picker with hex / rgb / hsl / oklch formats.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/color-picker
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export type ColorFormat = 'hex' | 'rgb' | 'hsl' | 'oklch';
|
|
10
|
+
|
|
11
|
+
export interface ColorPickerChangeEventDetail {
|
|
12
|
+
value: string;
|
|
13
|
+
format: ColorFormat;
|
|
14
|
+
}
|
|
15
|
+
export type ColorPickerChangeEvent = CustomEvent<ColorPickerChangeEventDetail>;
|
|
16
|
+
export type ColorPickerInputEvent = CustomEvent<ColorPickerChangeEventDetail>;
|
|
17
|
+
|
|
18
|
+
export interface ColorPickerFormatChangeEventDetail {
|
|
19
|
+
format: ColorFormat;
|
|
20
|
+
}
|
|
21
|
+
export type ColorPickerFormatChangeEvent = CustomEvent<ColorPickerFormatChangeEventDetail>;
|
|
22
|
+
|
|
23
|
+
export class UIColorPicker extends UIFormElement {
|
|
24
|
+
/** Current color as a string in the active `format`. */
|
|
25
|
+
value: string;
|
|
26
|
+
/** Output format. */
|
|
27
|
+
format: ColorFormat;
|
|
28
|
+
|
|
29
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
30
|
+
type: K,
|
|
31
|
+
listener: (this: UIColorPicker, ev: HTMLElementEventMap[K]) => unknown,
|
|
32
|
+
options?: boolean | AddEventListenerOptions,
|
|
33
|
+
): void;
|
|
34
|
+
addEventListener(type: 'change', listener: (ev: ColorPickerChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
35
|
+
addEventListener(type: 'input', listener: (ev: ColorPickerInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
36
|
+
addEventListener(type: 'format-change', listener: (ev: ColorPickerFormatChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
37
|
+
}
|
package/components/index.js
CHANGED
|
@@ -87,6 +87,7 @@ export { UIEmbed } from './embed/embed.js';
|
|
|
87
87
|
export { UIBlock } from './block/block.js';
|
|
88
88
|
export { UIText } from './text/text.js';
|
|
89
89
|
export { UIToggleGroup, UIToggleOption } from './toggle-group/toggle-group.js';
|
|
90
|
+
export { UIToggleScheme } from './toggle-scheme/toggle-scheme.js';
|
|
90
91
|
export { UIDemoToggle } from './demo-toggle/demo-toggle.js';
|
|
91
92
|
export { UIRichText } from './richtext/richtext.js';
|
|
92
93
|
export { UIStream } from './stream/stream.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<input-ui>` — Text + email + password + number + tel + url + search input.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/input
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
/** Native HTML input types this primitive supports. */
|
|
10
|
+
export type UIInputType =
|
|
11
|
+
| 'text'
|
|
12
|
+
| 'email'
|
|
13
|
+
| 'password'
|
|
14
|
+
| 'number'
|
|
15
|
+
| 'tel'
|
|
16
|
+
| 'url'
|
|
17
|
+
| 'search';
|
|
18
|
+
|
|
19
|
+
export interface InputChangeEventDetail {
|
|
20
|
+
value: string;
|
|
21
|
+
}
|
|
22
|
+
export type InputChangeEvent = CustomEvent<InputChangeEventDetail>;
|
|
23
|
+
export type InputInputEvent = CustomEvent<InputChangeEventDetail>;
|
|
24
|
+
|
|
25
|
+
export class UIInput extends UIFormElement {
|
|
26
|
+
placeholder: string;
|
|
27
|
+
type: UIInputType;
|
|
28
|
+
label: string;
|
|
29
|
+
prefix: string;
|
|
30
|
+
suffix: string;
|
|
31
|
+
/** Raw mode — drops chrome (border, padding) for inline composition. */
|
|
32
|
+
raw: boolean;
|
|
33
|
+
|
|
34
|
+
// ── Number-mode properties (active when type="number") ──
|
|
35
|
+
/** Minimum value. `null` to disable. */
|
|
36
|
+
min: number | null;
|
|
37
|
+
/** Maximum value. `null` to disable. */
|
|
38
|
+
max: number | null;
|
|
39
|
+
/** Step size for stepper buttons + arrow keys. */
|
|
40
|
+
step: number;
|
|
41
|
+
/** Decimal precision (places); `null` to derive from step. */
|
|
42
|
+
precision: number | null;
|
|
43
|
+
/**
|
|
44
|
+
* BCP-47 locale tag for number formatting — e.g. `"de-DE"` / `"fr-FR"` /
|
|
45
|
+
* `"en-IN"`. Default empty = en-US (`.` decimal separator, no thousands
|
|
46
|
+
* grouping). `.value` stays canonical (`Number(v)` round-trips).
|
|
47
|
+
*/
|
|
48
|
+
locale: string;
|
|
49
|
+
|
|
50
|
+
// ── Number-mode accessor ──
|
|
51
|
+
/** Parsed numeric value. Returns `NaN` if value isn't a number. */
|
|
52
|
+
readonly valueAsNumber: number;
|
|
53
|
+
|
|
54
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
55
|
+
type: K,
|
|
56
|
+
listener: (this: UIInput, ev: HTMLElementEventMap[K]) => unknown,
|
|
57
|
+
options?: boolean | AddEventListenerOptions,
|
|
58
|
+
): void;
|
|
59
|
+
addEventListener(type: 'change', listener: (ev: InputChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
60
|
+
addEventListener(type: 'input', listener: (ev: InputInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
61
|
+
}
|
|
@@ -462,8 +462,8 @@ class UIInput extends UIFormElement {
|
|
|
462
462
|
if (next === this.valueAsNumber) return;
|
|
463
463
|
this.value = this.#format(next);
|
|
464
464
|
this.syncValue(this.value);
|
|
465
|
-
this.dispatchEvent(new
|
|
466
|
-
this.dispatchEvent(new
|
|
465
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
466
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
// ── Event handlers ──
|
|
@@ -488,7 +488,7 @@ class UIInput extends UIFormElement {
|
|
|
488
488
|
this.value = text;
|
|
489
489
|
if (!this.#isNativePassword) this.#textEl.toggleAttribute('data-empty', !text);
|
|
490
490
|
this.syncValue(text);
|
|
491
|
-
this.dispatchEvent(new
|
|
491
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
492
492
|
};
|
|
493
493
|
|
|
494
494
|
#onBeforeInput = (e) => {
|
|
@@ -600,7 +600,7 @@ class UIInput extends UIFormElement {
|
|
|
600
600
|
e.preventDefault();
|
|
601
601
|
// Commit normalized value before firing form events.
|
|
602
602
|
this.#commitOnBlur();
|
|
603
|
-
this.dispatchEvent(new
|
|
603
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
604
604
|
this.dispatchEvent(new Event('submit', { bubbles: true }));
|
|
605
605
|
return;
|
|
606
606
|
}
|
|
@@ -608,7 +608,7 @@ class UIInput extends UIFormElement {
|
|
|
608
608
|
}
|
|
609
609
|
if (e.key === 'Enter') {
|
|
610
610
|
e.preventDefault();
|
|
611
|
-
this.dispatchEvent(new
|
|
611
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
612
612
|
this.dispatchEvent(new Event('submit', { bubbles: true }));
|
|
613
613
|
}
|
|
614
614
|
};
|
|
@@ -631,7 +631,7 @@ class UIInput extends UIFormElement {
|
|
|
631
631
|
|
|
632
632
|
#onBlur = () => {
|
|
633
633
|
if (this.#isNumberMode) this.#commitOnBlur();
|
|
634
|
-
this.dispatchEvent(new
|
|
634
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
635
635
|
};
|
|
636
636
|
|
|
637
637
|
#commitOnBlur() {
|
|
@@ -650,7 +650,7 @@ class UIInput extends UIFormElement {
|
|
|
650
650
|
if (this.value !== stored) {
|
|
651
651
|
this.value = stored;
|
|
652
652
|
this.syncValue(stored);
|
|
653
|
-
this.dispatchEvent(new
|
|
653
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
654
654
|
}
|
|
655
655
|
if (this.#textEl.textContent !== displayed) {
|
|
656
656
|
this.#textEl.textContent = displayed;
|
|
@@ -665,8 +665,8 @@ class UIInput extends UIFormElement {
|
|
|
665
665
|
this.syncValue(this.value);
|
|
666
666
|
this.#textEl.textContent = this.value;
|
|
667
667
|
this.#textEl.toggleAttribute('data-empty', !this.value);
|
|
668
|
-
this.dispatchEvent(new
|
|
669
|
-
this.dispatchEvent(new
|
|
668
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
669
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
670
670
|
}
|
|
671
671
|
|
|
672
672
|
#onPaste = (e) => {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<option-card-ui>` — Radio-style card with heading + description + icon.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/option-card
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface OptionCardChangeEventDetail {
|
|
10
|
+
value: string;
|
|
11
|
+
checked: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type OptionCardChangeEvent = CustomEvent<OptionCardChangeEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UIOptionCard extends UIFormElement {
|
|
16
|
+
/** Selected state — reflected; setting `checked=true` deselects siblings in the group. */
|
|
17
|
+
checked: boolean;
|
|
18
|
+
heading: string;
|
|
19
|
+
description: string;
|
|
20
|
+
icon: string;
|
|
21
|
+
/** Layout — `default` (vertical) / `horizontal` / `compact`. */
|
|
22
|
+
layout: 'default' | 'horizontal' | 'compact';
|
|
23
|
+
|
|
24
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
25
|
+
type: K,
|
|
26
|
+
listener: (this: UIOptionCard, ev: HTMLElementEventMap[K]) => unknown,
|
|
27
|
+
options?: boolean | AddEventListenerOptions,
|
|
28
|
+
): void;
|
|
29
|
+
addEventListener(type: 'change', listener: (ev: OptionCardChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
30
|
+
}
|
|
@@ -116,7 +116,7 @@ class UIOptionCard extends UIFormElement {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
this.checked = true;
|
|
119
|
-
this.dispatchEvent(new
|
|
119
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value, checked: this.checked } }));
|
|
120
120
|
};
|
|
121
121
|
|
|
122
122
|
#onKey = (e) => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<otp-input-ui>` — Auto-focus-advancing one-time-passcode input (6 digits by default).
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/otp-input
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface OtpInputEventDetail {
|
|
10
|
+
/** Combined digits as a string (e.g. `"123456"`). */
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
13
|
+
export type OtpInputEvent = CustomEvent<OtpInputEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UIOtpInput extends UIFormElement {
|
|
16
|
+
/** Number of digit slots. */
|
|
17
|
+
length: number;
|
|
18
|
+
|
|
19
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
20
|
+
type: K,
|
|
21
|
+
listener: (this: UIOtpInput, ev: HTMLElementEventMap[K]) => unknown,
|
|
22
|
+
options?: boolean | AddEventListenerOptions,
|
|
23
|
+
): void;
|
|
24
|
+
addEventListener(type: 'input', listener: (ev: OtpInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
25
|
+
}
|
|
@@ -93,7 +93,7 @@ class UIOtpInput extends UIFormElement {
|
|
|
93
93
|
input.value = input.value.replace(/\D/g, '').slice(0, 1);
|
|
94
94
|
|
|
95
95
|
this.#syncCombined();
|
|
96
|
-
this.dispatchEvent(new
|
|
96
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
97
97
|
|
|
98
98
|
if (input.value && index < this.#inputs.length - 1) {
|
|
99
99
|
this.#inputs[index + 1].focus();
|
|
@@ -107,7 +107,7 @@ class UIOtpInput extends UIFormElement {
|
|
|
107
107
|
this.#inputs[index - 1].focus();
|
|
108
108
|
this.#inputs[index - 1].value = '';
|
|
109
109
|
this.#syncCombined();
|
|
110
|
-
this.dispatchEvent(new
|
|
110
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -118,7 +118,7 @@ class UIOtpInput extends UIFormElement {
|
|
|
118
118
|
this.#inputs[i].value = text[i] || '';
|
|
119
119
|
}
|
|
120
120
|
this.#syncCombined();
|
|
121
|
-
this.dispatchEvent(new
|
|
121
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
122
122
|
|
|
123
123
|
// Focus last filled or first empty
|
|
124
124
|
const firstEmpty = this.#inputs.findIndex(inp => !inp.value);
|
package/components/pane/pane.css
CHANGED
|
@@ -54,6 +54,15 @@
|
|
|
54
54
|
overflow: hidden;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/* During an active drag-resize: kill all transitions so pointer-driven
|
|
58
|
+
width updates track 1:1 instead of interpolating through a consumer-
|
|
59
|
+
declared width transition (e.g. editor-shell's collapse animation).
|
|
60
|
+
Mirrors the admin-sidebar pattern. */
|
|
61
|
+
:scope[data-resizing] {
|
|
62
|
+
user-select: none;
|
|
63
|
+
transition: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
/* Side-aware border treatment.
|
|
58
67
|
`leading` — pane sits at the leading edge of a horizontal row, so
|
|
59
68
|
the only visual separator it needs is on its trailing
|
|
@@ -187,6 +196,7 @@
|
|
|
187
196
|
cursor: col-resize;
|
|
188
197
|
background: transparent;
|
|
189
198
|
transition: background var(--pane-duration) var(--pane-easing);
|
|
199
|
+
touch-action: none;
|
|
190
200
|
z-index: 1;
|
|
191
201
|
}
|
|
192
202
|
|
package/components/pane/pane.js
CHANGED
|
@@ -48,6 +48,8 @@ class UIPane extends UIElement {
|
|
|
48
48
|
#dragging = false;
|
|
49
49
|
#startX = 0;
|
|
50
50
|
#startW = 0;
|
|
51
|
+
#pointerId = null;
|
|
52
|
+
#prevDocCursor = '';
|
|
51
53
|
#bound = false;
|
|
52
54
|
|
|
53
55
|
connected() {
|
|
@@ -110,15 +112,30 @@ class UIPane extends UIElement {
|
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
// ── Resize ──
|
|
115
|
+
//
|
|
116
|
+
// Pointer capture + handle-level listeners (not document-level): keeps
|
|
117
|
+
// pointer events flowing to the grabber even when the cursor races past
|
|
118
|
+
// it, and works under touch. Cursor is locked at the documentElement
|
|
119
|
+
// level so it stays `col-resize` across the whole viewport during drag,
|
|
120
|
+
// not just over the 4px handle. CSS bypasses any width transition while
|
|
121
|
+
// `[data-resizing]` is set, so resizes track the pointer 1:1.
|
|
113
122
|
|
|
114
123
|
#onResizeDown = (e) => {
|
|
124
|
+
if (!e.isPrimary) return;
|
|
115
125
|
e.preventDefault();
|
|
116
126
|
this.#dragging = true;
|
|
117
127
|
this.#startX = e.clientX;
|
|
118
128
|
this.#startW = this.getBoundingClientRect().width;
|
|
129
|
+
this.#pointerId = e.pointerId;
|
|
119
130
|
this.setAttribute('data-resizing', '');
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
|
|
132
|
+
try { this.#resizeEl.setPointerCapture(e.pointerId); } catch {}
|
|
133
|
+
this.#resizeEl.addEventListener('pointermove', this.#onResizeMove);
|
|
134
|
+
this.#resizeEl.addEventListener('pointerup', this.#onResizeUp, { once: true });
|
|
135
|
+
this.#resizeEl.addEventListener('pointercancel', this.#onResizeUp, { once: true });
|
|
136
|
+
|
|
137
|
+
this.#prevDocCursor = document.documentElement.style.cursor;
|
|
138
|
+
document.documentElement.style.cursor = 'col-resize';
|
|
122
139
|
};
|
|
123
140
|
|
|
124
141
|
#onResizeMove = (e) => {
|
|
@@ -134,16 +151,23 @@ class UIPane extends UIElement {
|
|
|
134
151
|
};
|
|
135
152
|
|
|
136
153
|
#onResizeUp = () => {
|
|
154
|
+
if (!this.#dragging) return;
|
|
137
155
|
this.#dragging = false;
|
|
138
156
|
this.removeAttribute('data-resizing');
|
|
139
|
-
|
|
157
|
+
try { this.#resizeEl?.releasePointerCapture(this.#pointerId); } catch {}
|
|
158
|
+
this.#pointerId = null;
|
|
159
|
+
this.#resizeEl?.removeEventListener('pointermove', this.#onResizeMove);
|
|
160
|
+
document.documentElement.style.cursor = this.#prevDocCursor;
|
|
140
161
|
};
|
|
141
162
|
|
|
142
163
|
disconnected() {
|
|
143
164
|
this.removeEventListener('click', this.#onClick);
|
|
144
165
|
this.removeEventListener('keydown', this.#onKey);
|
|
145
166
|
this.#resizeEl?.removeEventListener('pointerdown', this.#onResizeDown);
|
|
146
|
-
|
|
167
|
+
this.#resizeEl?.removeEventListener('pointermove', this.#onResizeMove);
|
|
168
|
+
if (this.#dragging) {
|
|
169
|
+
document.documentElement.style.cursor = this.#prevDocCursor;
|
|
170
|
+
}
|
|
147
171
|
this.#bound = false;
|
|
148
172
|
}
|
|
149
173
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<radio-ui>` — Radio button (boolean-semantic form control; group via shared name).
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/radio
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface RadioChangeEventDetail {
|
|
10
|
+
/** Submitted value (the radio's `value` attribute). */
|
|
11
|
+
value: string;
|
|
12
|
+
/** Current checked state. */
|
|
13
|
+
checked: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type RadioChangeEvent = CustomEvent<RadioChangeEventDetail>;
|
|
16
|
+
|
|
17
|
+
export class UIRadio extends UIFormElement {
|
|
18
|
+
/** Checked state — reflected; setting `checked=true` deselects siblings in the group. */
|
|
19
|
+
checked: boolean;
|
|
20
|
+
label: string;
|
|
21
|
+
|
|
22
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
23
|
+
type: K,
|
|
24
|
+
listener: (this: UIRadio, ev: HTMLElementEventMap[K]) => unknown,
|
|
25
|
+
options?: boolean | AddEventListenerOptions,
|
|
26
|
+
): void;
|
|
27
|
+
addEventListener(type: 'change', listener: (ev: RadioChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
28
|
+
}
|
|
@@ -38,7 +38,7 @@ class UIRadio extends UIFormElement {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
this.checked = true;
|
|
41
|
-
this.dispatchEvent(new
|
|
41
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value, checked: this.checked } }));
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
#onKey = (e) => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<range-ui>` — Range track with click-to-jump + drag.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/range
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface RangeChangeEventDetail {
|
|
10
|
+
value: number;
|
|
11
|
+
}
|
|
12
|
+
export type RangeChangeEvent = CustomEvent<RangeChangeEventDetail>;
|
|
13
|
+
export type RangeInputEvent = CustomEvent<RangeChangeEventDetail>;
|
|
14
|
+
|
|
15
|
+
export class UIRange 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: UIRange, ev: HTMLElementEventMap[K]) => unknown,
|
|
27
|
+
options?: boolean | AddEventListenerOptions,
|
|
28
|
+
): void;
|
|
29
|
+
addEventListener(type: 'change', listener: (ev: RangeChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
30
|
+
addEventListener(type: 'input', listener: (ev: RangeInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
31
|
+
}
|
|
@@ -103,7 +103,7 @@ class UIRange extends UIFormElement {
|
|
|
103
103
|
const snapped = this.#snap(v);
|
|
104
104
|
if (snapped === this.value) return;
|
|
105
105
|
this.value = snapped;
|
|
106
|
-
this.dispatchEvent(new
|
|
106
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// ── Pointer drag ──
|
|
@@ -147,7 +147,7 @@ class UIRange extends UIFormElement {
|
|
|
147
147
|
this.#fieldEl.releasePointerCapture(e.pointerId);
|
|
148
148
|
this.#fieldEl.removeEventListener('pointermove', this.#onPointerMove);
|
|
149
149
|
this.#fieldEl.removeEventListener('pointerup', this.#onPointerUp);
|
|
150
|
-
this.dispatchEvent(new
|
|
150
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
151
151
|
};
|
|
152
152
|
|
|
153
153
|
// ── Keyboard ──
|
|
@@ -166,7 +166,7 @@ class UIRange extends UIFormElement {
|
|
|
166
166
|
}
|
|
167
167
|
e.preventDefault();
|
|
168
168
|
this.#setValue(v);
|
|
169
|
-
this.dispatchEvent(new
|
|
169
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
170
170
|
};
|
|
171
171
|
|
|
172
172
|
disconnected() {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<rating-ui>` — Star rating input (or other icon set).
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/rating
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface RatingChangeEventDetail {
|
|
10
|
+
value: number;
|
|
11
|
+
}
|
|
12
|
+
export type RatingChangeEvent = CustomEvent<RatingChangeEventDetail>;
|
|
13
|
+
|
|
14
|
+
export class UIRating extends UIFormElement {
|
|
15
|
+
/** Numeric rating value — overrides UIFormElement.value (which is String). */
|
|
16
|
+
value: number;
|
|
17
|
+
/** Maximum rating — number of icons rendered. */
|
|
18
|
+
max: number;
|
|
19
|
+
/** Phosphor icon name (default: `star`). */
|
|
20
|
+
icon: string;
|
|
21
|
+
/** Read-only mode (display only; no interaction). */
|
|
22
|
+
readonly: boolean;
|
|
23
|
+
/** Allow half-step values (0.5, 1.5, …). */
|
|
24
|
+
allowHalf: boolean;
|
|
25
|
+
size: 'sm' | 'md' | 'lg';
|
|
26
|
+
|
|
27
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
28
|
+
type: K,
|
|
29
|
+
listener: (this: UIRating, ev: HTMLElementEventMap[K]) => unknown,
|
|
30
|
+
options?: boolean | AddEventListenerOptions,
|
|
31
|
+
): void;
|
|
32
|
+
addEventListener(type: 'change', listener: (ev: RatingChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
33
|
+
}
|
|
@@ -121,7 +121,7 @@ class UIRating extends UIFormElement {
|
|
|
121
121
|
this.value = v;
|
|
122
122
|
this.#hoverValue = null;
|
|
123
123
|
this.syncValue(String(v));
|
|
124
|
-
this.dispatchEvent(new
|
|
124
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { value: this.value } }));
|
|
125
125
|
this.render();
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<search-ui>` — Search input with debounce + clear button + Enter-to-search.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/search
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface SearchInputEventDetail {
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
export type SearchInputEvent = CustomEvent<SearchInputEventDetail>;
|
|
13
|
+
|
|
14
|
+
export interface SearchEventDetail {
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
/** Fired after debounce OR on Enter — your "do the search" trigger. */
|
|
18
|
+
export type SearchEvent = CustomEvent<SearchEventDetail>;
|
|
19
|
+
|
|
20
|
+
export class UISearch extends UIFormElement {
|
|
21
|
+
placeholder: string;
|
|
22
|
+
/** Debounce delay in ms before `search` event fires. */
|
|
23
|
+
debounce: number;
|
|
24
|
+
|
|
25
|
+
/** Programmatically focus the inner input. */
|
|
26
|
+
focus(): void;
|
|
27
|
+
|
|
28
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
29
|
+
type: K,
|
|
30
|
+
listener: (this: UISearch, ev: HTMLElementEventMap[K]) => unknown,
|
|
31
|
+
options?: boolean | AddEventListenerOptions,
|
|
32
|
+
): void;
|
|
33
|
+
addEventListener(type: 'input', listener: (ev: SearchInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
34
|
+
addEventListener(type: 'search', listener: (ev: SearchEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
35
|
+
}
|
|
@@ -62,7 +62,7 @@ class UISearch extends UIFormElement {
|
|
|
62
62
|
#onInput = () => {
|
|
63
63
|
this.value = this.#inputEl.value;
|
|
64
64
|
this.syncValue(this.value);
|
|
65
|
-
this.dispatchEvent(new
|
|
65
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
66
66
|
|
|
67
67
|
clearTimeout(this.#timer);
|
|
68
68
|
this.#timer = setTimeout(() => {
|
|
@@ -92,7 +92,7 @@ class UISearch extends UIFormElement {
|
|
|
92
92
|
if (this.#inputEl) this.#inputEl.value = '';
|
|
93
93
|
this.syncValue('');
|
|
94
94
|
this.setAttribute('value', '');
|
|
95
|
-
this.dispatchEvent(new
|
|
95
|
+
this.dispatchEvent(new CustomEvent('input', { bubbles: true, detail: { value: this.value } }));
|
|
96
96
|
this.dispatchEvent(new CustomEvent('search', {
|
|
97
97
|
bubbles: true,
|
|
98
98
|
detail: { value: '' },
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<segmented-ui>` — Segmented control for 2-5 mutually-exclusive options.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/segmented
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { UIFormElement } from '../../core/form.js';
|
|
8
|
+
|
|
9
|
+
export interface SegmentedChangeEventDetail {
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
export type SegmentedChangeEvent = CustomEvent<SegmentedChangeEventDetail>;
|
|
13
|
+
|
|
14
|
+
export class UISegmented extends UIFormElement {
|
|
15
|
+
/** Selected segment's value. */
|
|
16
|
+
value: string;
|
|
17
|
+
|
|
18
|
+
addEventListener<K extends keyof HTMLElementEventMap>(
|
|
19
|
+
type: K,
|
|
20
|
+
listener: (this: UISegmented, ev: HTMLElementEventMap[K]) => unknown,
|
|
21
|
+
options?: boolean | AddEventListenerOptions,
|
|
22
|
+
): void;
|
|
23
|
+
addEventListener(type: 'change', listener: (ev: SegmentedChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
|
|
24
|
+
}
|