@haiyue/ui 0.1.0

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -0
  3. package/dist/checkbox.d.ts +30 -0
  4. package/dist/checkbox.js +204 -0
  5. package/dist/context-menu.d.ts +41 -0
  6. package/dist/context-menu.js +225 -0
  7. package/dist/dialog.d.ts +30 -0
  8. package/dist/dialog.js +183 -0
  9. package/dist/dropdown.d.ts +45 -0
  10. package/dist/dropdown.js +228 -0
  11. package/dist/index.d.ts +32 -0
  12. package/dist/index.js +110 -0
  13. package/dist/input.d.ts +30 -0
  14. package/dist/input.js +131 -0
  15. package/dist/menu-shared.d.ts +14 -0
  16. package/dist/menu-shared.js +54 -0
  17. package/dist/radio.d.ts +30 -0
  18. package/dist/radio.js +205 -0
  19. package/dist/select.d.ts +40 -0
  20. package/dist/select.js +253 -0
  21. package/dist/split.d.ts +46 -0
  22. package/dist/split.js +277 -0
  23. package/dist/tabs.d.ts +28 -0
  24. package/dist/tabs.js +161 -0
  25. package/dist/tooltip.d.ts +37 -0
  26. package/dist/tooltip.js +286 -0
  27. package/dist/tree-model.d.ts +35 -0
  28. package/dist/tree-model.js +281 -0
  29. package/dist/tree-node.d.ts +11 -0
  30. package/dist/tree-node.js +63 -0
  31. package/dist/tree-types.d.ts +41 -0
  32. package/dist/tree-types.js +2 -0
  33. package/dist/tree.d.ts +102 -0
  34. package/dist/tree.js +942 -0
  35. package/package.json +46 -0
  36. package/src/checkbox.ts +215 -0
  37. package/src/context-menu.ts +248 -0
  38. package/src/dialog.ts +195 -0
  39. package/src/dropdown.ts +249 -0
  40. package/src/index.ts +192 -0
  41. package/src/input.ts +150 -0
  42. package/src/menu-shared.ts +65 -0
  43. package/src/radio.ts +216 -0
  44. package/src/select.ts +271 -0
  45. package/src/split.ts +306 -0
  46. package/src/tabs.ts +182 -0
  47. package/src/tooltip.ts +321 -0
  48. package/src/tree-model.ts +305 -0
  49. package/src/tree-node.ts +67 -0
  50. package/src/tree-types.ts +47 -0
  51. package/src/tree.ts +1002 -0
package/dist/input.js ADDED
@@ -0,0 +1,131 @@
1
+ export class GEInput extends HTMLElement {
2
+ _input = document.createElement('input');
3
+ static get observedAttributes() {
4
+ return [
5
+ 'type', 'value', 'min', 'max', 'step', 'placeholder', 'disabled', 'readonly',
6
+ 'required', 'name', 'autocomplete', 'aria-label', 'title',
7
+ ];
8
+ }
9
+ constructor() {
10
+ super();
11
+ const root = this.attachShadow({ mode: 'open' });
12
+ const style = document.createElement('style');
13
+ style.textContent = `
14
+ :host {
15
+ display: block;
16
+ width: 100%;
17
+ min-width: 0;
18
+ }
19
+ input {
20
+ box-sizing: border-box;
21
+ width: 100%;
22
+ height: var(--ge-input-height, 28px);
23
+ min-width: 0;
24
+ padding: 0 8px;
25
+ border: 1px solid var(--ge-border-color, #303746);
26
+ border-radius: 4px;
27
+ outline: none;
28
+ color: var(--ge-text-color, #d8e2f2);
29
+ background: var(--ge-input-bg-color, var(--ge-surface-color, #121822));
30
+ font: var(--ge-input-font, 12px system-ui, sans-serif);
31
+ color-scheme: dark;
32
+ }
33
+ input:hover:not(:disabled):not(:read-only) {
34
+ border-color: var(--ge-hover-border-color, #435268);
35
+ }
36
+ input:focus {
37
+ border-color: var(--ge-focus-border-color, #3d6fa8);
38
+ box-shadow: 0 0 0 2px var(--ge-focus-ring-color, rgba(61, 111, 168, 0.24));
39
+ }
40
+ input:invalid {
41
+ border-color: var(--ge-invalid-border-color, #b64f62);
42
+ }
43
+ input:disabled,
44
+ input:read-only {
45
+ opacity: 0.55;
46
+ cursor: default;
47
+ }
48
+ input[type="color"] {
49
+ padding: 3px;
50
+ cursor: pointer;
51
+ }
52
+ input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
53
+ input[type="color"]::-webkit-color-swatch {
54
+ border: 0;
55
+ border-radius: 2px;
56
+ }
57
+ `;
58
+ root.append(style, this._input);
59
+ }
60
+ connectedCallback() {
61
+ this._input.addEventListener('input', this._onInput);
62
+ this._input.addEventListener('change', this._onChange);
63
+ this._sync();
64
+ }
65
+ disconnectedCallback() {
66
+ this._input.removeEventListener('input', this._onInput);
67
+ this._input.removeEventListener('change', this._onChange);
68
+ }
69
+ attributeChangedCallback() {
70
+ this._sync();
71
+ }
72
+ get type() { return normalizedType(this.getAttribute('type')); }
73
+ set type(value) { this.setAttribute('type', normalizedType(value)); }
74
+ get value() { return this.getAttribute('value') ?? ''; }
75
+ set value(value) {
76
+ this.setAttribute('value', String(value));
77
+ this._input.value = String(value);
78
+ }
79
+ get valueAsNumber() {
80
+ return this._input.valueAsNumber;
81
+ }
82
+ get disabled() { return this.hasAttribute('disabled'); }
83
+ set disabled(value) { this.toggleAttribute('disabled', value); }
84
+ get readOnly() { return this.hasAttribute('readonly'); }
85
+ set readOnly(value) { this.toggleAttribute('readonly', value); }
86
+ get valid() { return this._input.validity.valid; }
87
+ focus(options) { this._input.focus(options); }
88
+ select() { this._input.select(); }
89
+ _sync() {
90
+ const type = this.type;
91
+ if (this._input.type !== type)
92
+ this._input.type = type;
93
+ const value = this.value;
94
+ if (this._input.value !== value)
95
+ this._input.value = type === 'color' && !/^#[0-9a-f]{6}$/iu.test(value)
96
+ ? '#000000'
97
+ : value;
98
+ for (const name of ['min', 'max', 'step', 'placeholder', 'name', 'autocomplete', 'aria-label', 'title']) {
99
+ const attribute = this.getAttribute(name);
100
+ if (attribute === null)
101
+ this._input.removeAttribute(name);
102
+ else
103
+ this._input.setAttribute(name, attribute);
104
+ }
105
+ this._input.disabled = this.disabled;
106
+ this._input.readOnly = this.readOnly;
107
+ this._input.required = this.hasAttribute('required');
108
+ }
109
+ _onInput = () => {
110
+ this.setAttribute('value', this._input.value);
111
+ };
112
+ _onChange = () => {
113
+ this.setAttribute('value', this._input.value);
114
+ const number = this.type === 'number' && Number.isFinite(this._input.valueAsNumber)
115
+ ? this._input.valueAsNumber
116
+ : null;
117
+ this.dispatchEvent(new CustomEvent('value-change', {
118
+ detail: { value: this._input.value, valueAsNumber: number, valid: this.valid },
119
+ bubbles: true,
120
+ composed: true,
121
+ }));
122
+ };
123
+ }
124
+ export function defineInputComponents() {
125
+ if (!customElements.get('ge-input'))
126
+ customElements.define('ge-input', GEInput);
127
+ }
128
+ function normalizedType(value) {
129
+ return value === 'number' || value === 'color' ? value : 'text';
130
+ }
131
+ //# sourceMappingURL=input.js.map
@@ -0,0 +1,14 @@
1
+ export interface GEMenuItemLike {
2
+ label?: string;
3
+ value?: string;
4
+ disabled?: boolean;
5
+ separator?: boolean;
6
+ }
7
+ export interface GEMenuButtonOptions {
8
+ index?: number;
9
+ role?: string;
10
+ tabIndex?: number;
11
+ }
12
+ export declare const GE_MENU_ITEM_STYLES = "\n button {\n box-sizing: border-box;\n width: 100%;\n height: 26px;\n display: flex;\n align-items: center;\n padding: 0 8px;\n border: 0;\n border-radius: 3px;\n color: var(--ge-text-color, #d8e2f2);\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n }\n button:hover,\n button:focus,\n button.active {\n background: var(--ge-menu-item-hover-bg-color, var(--ge-accent-strong-color, #255a91));\n outline: none;\n }\n button:disabled {\n color: var(--ge-muted-color, #66758b);\n cursor: default;\n }\n button:disabled:hover {\n background: transparent;\n }\n .separator {\n height: 1px;\n margin: 4px 2px;\n background: var(--ge-border-color, #303746);\n }\n";
13
+ export declare function createMenuSeparator(): HTMLDivElement;
14
+ export declare function createMenuItemButton(item: GEMenuItemLike, options?: GEMenuButtonOptions): HTMLButtonElement;
@@ -0,0 +1,54 @@
1
+ export const GE_MENU_ITEM_STYLES = `
2
+ button {
3
+ box-sizing: border-box;
4
+ width: 100%;
5
+ height: 26px;
6
+ display: flex;
7
+ align-items: center;
8
+ padding: 0 8px;
9
+ border: 0;
10
+ border-radius: 3px;
11
+ color: var(--ge-text-color, #d8e2f2);
12
+ background: transparent;
13
+ font: inherit;
14
+ text-align: left;
15
+ cursor: pointer;
16
+ }
17
+ button:hover,
18
+ button:focus,
19
+ button.active {
20
+ background: var(--ge-menu-item-hover-bg-color, var(--ge-accent-strong-color, #255a91));
21
+ outline: none;
22
+ }
23
+ button:disabled {
24
+ color: var(--ge-muted-color, #66758b);
25
+ cursor: default;
26
+ }
27
+ button:disabled:hover {
28
+ background: transparent;
29
+ }
30
+ .separator {
31
+ height: 1px;
32
+ margin: 4px 2px;
33
+ background: var(--ge-border-color, #303746);
34
+ }
35
+ `;
36
+ export function createMenuSeparator() {
37
+ const separator = document.createElement('div');
38
+ separator.className = 'separator';
39
+ return separator;
40
+ }
41
+ export function createMenuItemButton(item, options = {}) {
42
+ const button = document.createElement('button');
43
+ button.type = 'button';
44
+ button.textContent = item.label ?? item.value ?? '';
45
+ button.disabled = Boolean(item.disabled);
46
+ if (options.index !== undefined)
47
+ button.dataset.index = String(options.index);
48
+ if (options.role)
49
+ button.setAttribute('role', options.role);
50
+ if (options.tabIndex !== undefined)
51
+ button.tabIndex = options.tabIndex;
52
+ return button;
53
+ }
54
+ //# sourceMappingURL=menu-shared.js.map
@@ -0,0 +1,30 @@
1
+ export interface GERadioChangeDetail {
2
+ checked: boolean;
3
+ value: string;
4
+ name: string;
5
+ }
6
+ export declare class GERadio extends HTMLElement {
7
+ private readonly _input;
8
+ private readonly _marker;
9
+ private readonly _label;
10
+ private readonly _slot;
11
+ static get observedAttributes(): string[];
12
+ constructor();
13
+ connectedCallback(): void;
14
+ disconnectedCallback(): void;
15
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
16
+ get checked(): boolean;
17
+ set checked(value: boolean);
18
+ get disabled(): boolean;
19
+ set disabled(value: boolean);
20
+ get label(): string;
21
+ set label(value: string);
22
+ get value(): string;
23
+ set value(value: string);
24
+ get name(): string;
25
+ set name(value: string);
26
+ private _sync;
27
+ private _onChange;
28
+ private _uncheckNamedPeers;
29
+ }
30
+ export declare function defineRadioComponents(): void;
package/dist/radio.js ADDED
@@ -0,0 +1,205 @@
1
+ export class GERadio extends HTMLElement {
2
+ _input = document.createElement('input');
3
+ _marker = document.createElement('span');
4
+ _label = document.createElement('span');
5
+ _slot = document.createElement('slot');
6
+ static get observedAttributes() {
7
+ return ['checked', 'disabled', 'label', 'value', 'name', 'aria-label'];
8
+ }
9
+ constructor() {
10
+ super();
11
+ const root = this.attachShadow({ mode: 'open' });
12
+ const style = document.createElement('style');
13
+ style.textContent = `
14
+ :host {
15
+ display: inline-block;
16
+ width: max-content;
17
+ min-width: 0;
18
+ color: var(--ge-radio-text-color, var(--ge-text-color, #d8e2f2));
19
+ font: 12px system-ui, sans-serif;
20
+ }
21
+ :host([disabled]) {
22
+ opacity: 0.55;
23
+ cursor: default;
24
+ }
25
+ label {
26
+ display: inline-flex;
27
+ align-items: center;
28
+ gap: 8px;
29
+ min-width: 0;
30
+ cursor: pointer;
31
+ user-select: none;
32
+ }
33
+ :host([disabled]) label {
34
+ cursor: default;
35
+ }
36
+ input {
37
+ position: absolute;
38
+ width: 1px;
39
+ height: 1px;
40
+ margin: -1px;
41
+ padding: 0;
42
+ overflow: hidden;
43
+ clip: rect(0 0 0 0);
44
+ white-space: nowrap;
45
+ border: 0;
46
+ }
47
+ .marker {
48
+ box-sizing: border-box;
49
+ width: 14px;
50
+ height: 14px;
51
+ flex: 0 0 14px;
52
+ display: inline-grid;
53
+ place-items: center;
54
+ border: 1px solid var(--ge-radio-border-color, var(--ge-border-color, #303746));
55
+ border-radius: 50%;
56
+ background: var(--ge-radio-bg-color, var(--ge-input-bg-color, #121822));
57
+ color: var(--ge-radio-dot-color, var(--ge-selected-text-color, #ffffff));
58
+ transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
59
+ }
60
+ input:focus-visible + .marker {
61
+ border-color: var(--ge-focus-border-color, #3d6fa8);
62
+ box-shadow: 0 0 0 2px var(--ge-focus-ring-color, rgba(61, 111, 168, 0.28));
63
+ }
64
+ input:checked + .marker {
65
+ border-color: var(--ge-radio-checked-border-color, var(--ge-accent-color, #3d6fa8));
66
+ background: var(--ge-radio-checked-bg-color, var(--ge-accent-strong-color, #255a91));
67
+ }
68
+ .marker::after {
69
+ content: "";
70
+ width: 6px;
71
+ height: 6px;
72
+ border-radius: 50%;
73
+ background: currentColor;
74
+ transform: scale(0);
75
+ transition: transform 0.12s ease;
76
+ }
77
+ input:checked + .marker::after {
78
+ transform: scale(1);
79
+ }
80
+ .label {
81
+ min-width: 0;
82
+ overflow: hidden;
83
+ text-overflow: ellipsis;
84
+ white-space: nowrap;
85
+ }
86
+ `;
87
+ const label = document.createElement('label');
88
+ this._input.type = 'radio';
89
+ this._marker.className = 'marker';
90
+ this._marker.setAttribute('aria-hidden', 'true');
91
+ this._label.className = 'label';
92
+ this._label.append(this._slot);
93
+ label.append(this._input, this._marker, this._label);
94
+ root.append(style, label);
95
+ }
96
+ connectedCallback() {
97
+ this._input.addEventListener('change', this._onChange);
98
+ this._sync();
99
+ if (this.checked)
100
+ this._uncheckNamedPeers();
101
+ }
102
+ disconnectedCallback() {
103
+ this._input.removeEventListener('change', this._onChange);
104
+ }
105
+ attributeChangedCallback(name, oldValue, newValue) {
106
+ this._sync();
107
+ if (name === 'checked' && oldValue !== newValue && newValue !== null) {
108
+ this._uncheckNamedPeers();
109
+ }
110
+ }
111
+ get checked() {
112
+ return this.hasAttribute('checked');
113
+ }
114
+ set checked(value) {
115
+ if (value)
116
+ this.setAttribute('checked', '');
117
+ else
118
+ this.removeAttribute('checked');
119
+ }
120
+ get disabled() {
121
+ return this.hasAttribute('disabled');
122
+ }
123
+ set disabled(value) {
124
+ if (value)
125
+ this.setAttribute('disabled', '');
126
+ else
127
+ this.removeAttribute('disabled');
128
+ }
129
+ get label() {
130
+ return this.getAttribute('label') ?? '';
131
+ }
132
+ set label(value) {
133
+ if (value)
134
+ this.setAttribute('label', value);
135
+ else
136
+ this.removeAttribute('label');
137
+ }
138
+ get value() {
139
+ return this.getAttribute('value') ?? 'on';
140
+ }
141
+ set value(value) {
142
+ this.setAttribute('value', value);
143
+ }
144
+ get name() {
145
+ return this.getAttribute('name') ?? '';
146
+ }
147
+ set name(value) {
148
+ if (value)
149
+ this.setAttribute('name', value);
150
+ else
151
+ this.removeAttribute('name');
152
+ }
153
+ _sync() {
154
+ this._input.checked = this.checked;
155
+ this._input.disabled = this.disabled;
156
+ this._input.value = this.value;
157
+ this._input.name = this.name;
158
+ const label = this.getAttribute('label');
159
+ if (label !== null)
160
+ this._slot.textContent = label;
161
+ else if (this._slot.textContent !== '')
162
+ this._slot.textContent = '';
163
+ const ariaLabel = this.getAttribute('aria-label') ?? label ?? '';
164
+ if (ariaLabel)
165
+ this._input.setAttribute('aria-label', ariaLabel);
166
+ else
167
+ this._input.removeAttribute('aria-label');
168
+ }
169
+ _onChange = () => {
170
+ if (!this._input.checked)
171
+ return;
172
+ this.setAttribute('checked', '');
173
+ this._uncheckNamedPeers();
174
+ this.dispatchEvent(new CustomEvent('checked-change', {
175
+ detail: { checked: true, value: this.value, name: this.name },
176
+ bubbles: true,
177
+ composed: true,
178
+ }));
179
+ this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
180
+ };
181
+ _uncheckNamedPeers() {
182
+ if (!this.name)
183
+ return;
184
+ const root = this.getRootNode();
185
+ const peers = root instanceof Document || root instanceof ShadowRoot
186
+ ? root.querySelectorAll(`ge-radio[name="${cssEscape(this.name)}"]`)
187
+ : [];
188
+ for (const peer of peers) {
189
+ if (peer === this || peer.disabled)
190
+ continue;
191
+ peer.removeAttribute('checked');
192
+ }
193
+ }
194
+ }
195
+ function cssEscape(value) {
196
+ if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function')
197
+ return CSS.escape(value);
198
+ return value.replace(/["\\]/g, '\\$&');
199
+ }
200
+ export function defineRadioComponents() {
201
+ if (!customElements.get('ge-radio')) {
202
+ customElements.define('ge-radio', GERadio);
203
+ }
204
+ }
205
+ //# sourceMappingURL=radio.js.map
@@ -0,0 +1,40 @@
1
+ export interface GESelectOption {
2
+ label: string;
3
+ value: string;
4
+ disabled?: boolean;
5
+ }
6
+ export interface GESelectChangeDetail {
7
+ value: string;
8
+ option: GESelectOption | null;
9
+ }
10
+ export declare class GESelect extends HTMLElement {
11
+ private readonly _wrap;
12
+ private readonly _search;
13
+ private readonly _select;
14
+ private _options;
15
+ private _filterText;
16
+ static get observedAttributes(): string[];
17
+ constructor();
18
+ connectedCallback(): void;
19
+ disconnectedCallback(): void;
20
+ attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
21
+ get options(): GESelectOption[];
22
+ set options(value: GESelectOption[]);
23
+ get value(): string;
24
+ set value(value: string);
25
+ get disabled(): boolean;
26
+ set disabled(value: boolean);
27
+ get searchable(): boolean;
28
+ set searchable(value: boolean);
29
+ get label(): string;
30
+ set label(value: string);
31
+ private _syncFromAttributes;
32
+ private _readOptionsAttribute;
33
+ private _render;
34
+ private _syncDisabled;
35
+ private _syncAriaLabel;
36
+ private _matchesFilter;
37
+ private _onChange;
38
+ private _onSearchInput;
39
+ }
40
+ export declare function defineSelectComponents(): void;