@haiyue/ui 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -1
- package/README.md +444 -1
- package/dist/adapters/web/controls.d.ts +19 -0
- package/dist/adapters/web/controls.js +284 -0
- package/dist/adapters/web/dom.d.ts +10 -0
- package/dist/adapters/web/dom.js +98 -0
- package/dist/adapters/web/index.d.ts +3 -0
- package/dist/adapters/web/index.js +3 -0
- package/dist/adapters/web/renderer.d.ts +18 -0
- package/dist/adapters/web/renderer.js +545 -0
- package/dist/adapters/web/styles.d.ts +3 -0
- package/dist/adapters/web/styles.js +62 -0
- package/dist/adapters/web/types.d.ts +46 -0
- package/dist/adapters/web/types.js +2 -0
- package/dist/affix.d.ts +35 -0
- package/dist/affix.js +137 -0
- package/dist/badge.d.ts +45 -0
- package/dist/badge.js +168 -0
- package/dist/breadcrumb.d.ts +37 -0
- package/dist/breadcrumb.js +210 -0
- package/dist/button-group.d.ts +12 -0
- package/dist/button-group.js +72 -0
- package/dist/button.d.ts +15 -1
- package/dist/button.js +101 -12
- package/dist/cascader.d.ts +65 -0
- package/dist/cascader.js +479 -0
- package/dist/collapse.d.ts +55 -0
- package/dist/collapse.js +261 -0
- package/dist/core/actions.d.ts +32 -0
- package/dist/core/actions.js +145 -0
- package/dist/core/data.d.ts +4 -0
- package/dist/core/data.js +27 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.js +6 -0
- package/dist/core/platform-view.d.ts +17 -0
- package/dist/core/platform-view.js +84 -0
- package/dist/core/selection.d.ts +14 -0
- package/dist/core/selection.js +93 -0
- package/dist/core/text.d.ts +31 -0
- package/dist/core/text.js +154 -0
- package/dist/core/types.d.ts +250 -0
- package/dist/core/types.js +2 -0
- package/dist/core/validation.d.ts +24 -0
- package/dist/core/validation.js +238 -0
- package/dist/date-picker.d.ts +60 -0
- package/dist/date-picker.js +355 -0
- package/dist/dialog.d.ts +4 -0
- package/dist/dialog.js +23 -5
- package/dist/divider.d.ts +33 -0
- package/dist/divider.js +113 -0
- package/dist/draggable.d.ts +78 -0
- package/dist/draggable.js +255 -0
- package/dist/index.d.ts +45 -1
- package/dist/index.js +66 -0
- package/dist/input.d.ts +30 -1
- package/dist/input.js +166 -54
- package/dist/portable/index.d.ts +4 -0
- package/dist/portable/index.js +3 -0
- package/dist/portable/page.d.ts +38 -0
- package/dist/portable/page.js +361 -0
- package/dist/portable/theme.d.ts +19 -0
- package/dist/portable/theme.js +40 -0
- package/dist/portable/tree.d.ts +10 -0
- package/dist/portable/tree.js +96 -0
- package/dist/range.d.ts +3 -0
- package/dist/range.js +16 -5
- package/dist/rate.d.ts +46 -0
- package/dist/rate.js +193 -0
- package/dist/resizable.d.ts +97 -0
- package/dist/resizable.js +426 -0
- package/dist/skeleton.d.ts +41 -0
- package/dist/skeleton.js +141 -0
- package/dist/steps.d.ts +45 -0
- package/dist/steps.js +203 -0
- package/dist/table.d.ts +106 -0
- package/dist/table.js +689 -0
- package/dist/tabs.d.ts +3 -0
- package/dist/tabs.js +11 -1
- package/dist/tag.d.ts +55 -0
- package/dist/tag.js +225 -0
- package/dist/time-picker.d.ts +62 -0
- package/dist/time-picker.js +298 -0
- package/dist/timeline.d.ts +48 -0
- package/dist/timeline.js +201 -0
- package/dist/toast.d.ts +55 -0
- package/dist/toast.js +224 -0
- package/dist/tour.d.ts +89 -0
- package/dist/tour.js +306 -0
- package/dist/virtual-keyboard.d.ts +95 -0
- package/dist/virtual-keyboard.js +432 -0
- package/dist/virtual-mouse.d.ts +82 -0
- package/dist/virtual-mouse.js +358 -0
- package/dist/watermark.d.ts +44 -0
- package/dist/watermark.js +148 -0
- package/package.json +115 -4
package/dist/cascader.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
const PANEL_ID_PREFIX = 'hy-cascader-panel';
|
|
2
|
+
function sameValue(left, right) {
|
|
3
|
+
return left === right;
|
|
4
|
+
}
|
|
5
|
+
function normalizeOptions(value) {
|
|
6
|
+
if (!Array.isArray(value))
|
|
7
|
+
return [];
|
|
8
|
+
return value.flatMap(item => {
|
|
9
|
+
if (!item || typeof item !== 'object')
|
|
10
|
+
return [];
|
|
11
|
+
const record = item;
|
|
12
|
+
if (record.value == null || (typeof record.value !== 'string' && typeof record.value !== 'number'))
|
|
13
|
+
return [];
|
|
14
|
+
const children = normalizeOptions(record.children);
|
|
15
|
+
return [{
|
|
16
|
+
label: String(record.label ?? record.value),
|
|
17
|
+
value: record.value,
|
|
18
|
+
disabled: Boolean(record.disabled),
|
|
19
|
+
...(children.length ? { children } : {}),
|
|
20
|
+
}];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** Tree-backed cascading dropdown that commits a complete value path. */
|
|
24
|
+
export class HYCascader extends HTMLElement {
|
|
25
|
+
_control = document.createElement('div');
|
|
26
|
+
_trigger = document.createElement('button');
|
|
27
|
+
_label = document.createElement('span');
|
|
28
|
+
_arrow = document.createElement('span');
|
|
29
|
+
_clear = document.createElement('button');
|
|
30
|
+
_panel = document.createElement('div');
|
|
31
|
+
_panelId = `${PANEL_ID_PREFIX}-${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
|
|
32
|
+
_options = [];
|
|
33
|
+
_value = [];
|
|
34
|
+
_activePath = [];
|
|
35
|
+
_reflecting = false;
|
|
36
|
+
_outsideAbort = null;
|
|
37
|
+
static get observedAttributes() {
|
|
38
|
+
return ['options', 'value', 'placeholder', 'display-separator', 'disabled', 'clearable', 'change-on-select', 'open', 'aria-label'];
|
|
39
|
+
}
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
const root = this.attachShadow({ mode: 'open' });
|
|
43
|
+
const style = document.createElement('style');
|
|
44
|
+
style.textContent = `
|
|
45
|
+
:host {
|
|
46
|
+
position: relative;
|
|
47
|
+
display: inline-block;
|
|
48
|
+
min-width: 180px;
|
|
49
|
+
color: var(--hy-text-color, #eef3ff);
|
|
50
|
+
font: 12px/1.4 system-ui, sans-serif;
|
|
51
|
+
}
|
|
52
|
+
.control {
|
|
53
|
+
display: grid;
|
|
54
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
height: var(--hy-cascader-height, 34px);
|
|
57
|
+
box-sizing: border-box;
|
|
58
|
+
border: 1px solid var(--hy-border-color, #3d4654);
|
|
59
|
+
border-radius: var(--hy-cascader-radius, 6px);
|
|
60
|
+
background: var(--hy-input-bg-color, #111722);
|
|
61
|
+
}
|
|
62
|
+
:host(:not([disabled])) .control:hover { border-color: var(--hy-hover-border-color, #596779); }
|
|
63
|
+
:host(:focus-within) .control, :host([open]) .control {
|
|
64
|
+
border-color: var(--hy-focus-border-color, #79a8ff);
|
|
65
|
+
box-shadow: 0 0 0 2px var(--hy-focus-ring-color, rgba(121, 168, 255, 0.22));
|
|
66
|
+
}
|
|
67
|
+
.trigger {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
min-width: 0;
|
|
71
|
+
gap: 8px;
|
|
72
|
+
padding: 0 8px 0 10px;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
border: 0;
|
|
75
|
+
color: inherit;
|
|
76
|
+
background: transparent;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
font: inherit;
|
|
79
|
+
text-align: left;
|
|
80
|
+
}
|
|
81
|
+
.label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
82
|
+
.label.placeholder { color: var(--hy-muted-color, #788697); }
|
|
83
|
+
.arrow { flex: 0 0 auto; color: var(--hy-muted-color, #788697); transition: transform 140ms ease; }
|
|
84
|
+
:host([open]) .arrow { transform: rotate(180deg); }
|
|
85
|
+
.clear {
|
|
86
|
+
width: 28px;
|
|
87
|
+
padding: 0;
|
|
88
|
+
border: 0;
|
|
89
|
+
color: var(--hy-muted-color, #788697);
|
|
90
|
+
background: transparent;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
font: 15px/1 system-ui, sans-serif;
|
|
93
|
+
}
|
|
94
|
+
.clear:hover { color: var(--hy-text-color, #eef3ff); }
|
|
95
|
+
.clear[hidden] { display: none; }
|
|
96
|
+
.trigger:focus-visible, .clear:focus-visible { outline: none; }
|
|
97
|
+
:host([disabled]) { opacity: 0.52; }
|
|
98
|
+
:host([disabled]) .trigger, :host([disabled]) .clear { cursor: not-allowed; }
|
|
99
|
+
.panel {
|
|
100
|
+
position: absolute;
|
|
101
|
+
z-index: var(--hy-cascader-z-index, 50);
|
|
102
|
+
top: calc(100% + 6px);
|
|
103
|
+
left: 0;
|
|
104
|
+
display: flex;
|
|
105
|
+
max-width: min(720px, 90vw);
|
|
106
|
+
overflow: auto;
|
|
107
|
+
border: 1px solid var(--hy-border-color, #3d4654);
|
|
108
|
+
border-radius: var(--hy-cascader-panel-radius, 7px);
|
|
109
|
+
background: var(--hy-menu-bg-color, var(--hy-surface-elevated-color, #171d27));
|
|
110
|
+
box-shadow: 0 12px 32px var(--hy-menu-shadow-color, rgba(0, 0, 0, 0.36));
|
|
111
|
+
}
|
|
112
|
+
.panel[hidden] { display: none; }
|
|
113
|
+
.column {
|
|
114
|
+
flex: 0 0 var(--hy-cascader-column-width, 184px);
|
|
115
|
+
min-width: 130px;
|
|
116
|
+
max-height: var(--hy-cascader-panel-height, 260px);
|
|
117
|
+
box-sizing: border-box;
|
|
118
|
+
margin: 0;
|
|
119
|
+
padding: 5px;
|
|
120
|
+
overflow-y: auto;
|
|
121
|
+
list-style: none;
|
|
122
|
+
scrollbar-color: var(--hy-border-strong-color, #596779) transparent;
|
|
123
|
+
scrollbar-width: thin;
|
|
124
|
+
}
|
|
125
|
+
.column + .column { border-left: 1px solid var(--hy-border-color, #3d4654); }
|
|
126
|
+
.option {
|
|
127
|
+
display: flex;
|
|
128
|
+
width: 100%;
|
|
129
|
+
min-height: 32px;
|
|
130
|
+
align-items: center;
|
|
131
|
+
gap: 8px;
|
|
132
|
+
padding: 6px 8px;
|
|
133
|
+
overflow: hidden;
|
|
134
|
+
border: 0;
|
|
135
|
+
border-radius: 4px;
|
|
136
|
+
color: var(--hy-secondary-text-color, #aebbd0);
|
|
137
|
+
background: transparent;
|
|
138
|
+
cursor: pointer;
|
|
139
|
+
font: inherit;
|
|
140
|
+
text-align: left;
|
|
141
|
+
}
|
|
142
|
+
.option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
143
|
+
.option-arrow { flex: 0 0 auto; color: var(--hy-muted-color, #788697); }
|
|
144
|
+
.option:hover:not(:disabled), .option:focus-visible, .option[aria-selected="true"] {
|
|
145
|
+
outline: none;
|
|
146
|
+
color: var(--hy-text-color, #eef3ff);
|
|
147
|
+
background: var(--hy-hover-bg-color, #273241);
|
|
148
|
+
}
|
|
149
|
+
.option[aria-selected="true"] { color: var(--hy-accent-color, #79a8ff); }
|
|
150
|
+
.option:disabled { cursor: not-allowed; opacity: 0.46; }
|
|
151
|
+
.empty { min-width: 184px; padding: 28px 18px; color: var(--hy-muted-color, #788697); text-align: center; }
|
|
152
|
+
`;
|
|
153
|
+
this._control.className = 'control';
|
|
154
|
+
this._control.part.add('control');
|
|
155
|
+
this._trigger.type = 'button';
|
|
156
|
+
this._trigger.className = 'trigger';
|
|
157
|
+
this._trigger.part.add('trigger');
|
|
158
|
+
this._trigger.setAttribute('aria-haspopup', 'listbox');
|
|
159
|
+
this._trigger.setAttribute('aria-controls', this._panelId);
|
|
160
|
+
this._label.className = 'label';
|
|
161
|
+
this._label.part.add('label');
|
|
162
|
+
this._arrow.className = 'arrow';
|
|
163
|
+
this._arrow.part.add('arrow');
|
|
164
|
+
this._arrow.setAttribute('aria-hidden', 'true');
|
|
165
|
+
this._arrow.textContent = '⌄';
|
|
166
|
+
this._trigger.append(this._label, this._arrow);
|
|
167
|
+
this._clear.type = 'button';
|
|
168
|
+
this._clear.className = 'clear';
|
|
169
|
+
this._clear.part.add('clear');
|
|
170
|
+
this._clear.setAttribute('aria-label', 'Clear selection');
|
|
171
|
+
this._clear.textContent = '×';
|
|
172
|
+
this._panel.id = this._panelId;
|
|
173
|
+
this._panel.className = 'panel';
|
|
174
|
+
this._panel.part.add('panel');
|
|
175
|
+
this._control.append(this._trigger, this._clear);
|
|
176
|
+
root.append(style, this._control, this._panel);
|
|
177
|
+
this._trigger.addEventListener('click', () => { if (!this.disabled)
|
|
178
|
+
this.open = !this.open; });
|
|
179
|
+
this._trigger.addEventListener('keydown', event => {
|
|
180
|
+
if (event.key === 'ArrowDown' && !this.disabled) {
|
|
181
|
+
event.preventDefault();
|
|
182
|
+
this.open = true;
|
|
183
|
+
queueMicrotask(() => this._panel.querySelector('.option:not(:disabled)')?.focus());
|
|
184
|
+
}
|
|
185
|
+
else if (event.key === 'Escape' && this.open) {
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
this.open = false;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
this._clear.addEventListener('click', event => {
|
|
191
|
+
event.stopPropagation();
|
|
192
|
+
if (!this.disabled)
|
|
193
|
+
this._commit([], true);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
connectedCallback() {
|
|
197
|
+
this._syncAttributes();
|
|
198
|
+
this._render();
|
|
199
|
+
this._syncOutsideListener();
|
|
200
|
+
}
|
|
201
|
+
disconnectedCallback() {
|
|
202
|
+
this._outsideAbort?.abort();
|
|
203
|
+
this._outsideAbort = null;
|
|
204
|
+
}
|
|
205
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
206
|
+
if (oldValue === newValue || this._reflecting)
|
|
207
|
+
return;
|
|
208
|
+
if (name === 'options')
|
|
209
|
+
this._readOptionsAttribute();
|
|
210
|
+
if (name === 'value')
|
|
211
|
+
this._readValueAttribute();
|
|
212
|
+
if (name === 'open' && this.open)
|
|
213
|
+
this._activePath = [...this._value];
|
|
214
|
+
this._render();
|
|
215
|
+
if (name === 'open') {
|
|
216
|
+
this._syncOutsideListener();
|
|
217
|
+
this._dispatchOpenChange();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
get options() { return this._options; }
|
|
221
|
+
set options(value) {
|
|
222
|
+
this._options = normalizeOptions(value);
|
|
223
|
+
this._value = this._normalizePath(this._value);
|
|
224
|
+
this._activePath = this.open ? [...this._value] : [];
|
|
225
|
+
this._reflectValue();
|
|
226
|
+
this._render();
|
|
227
|
+
}
|
|
228
|
+
get value() { return [...this._value]; }
|
|
229
|
+
set value(value) {
|
|
230
|
+
this._value = this._normalizePath(Array.isArray(value) ? value : []);
|
|
231
|
+
this._activePath = this.open ? [...this._value] : [];
|
|
232
|
+
this._reflectValue();
|
|
233
|
+
this._render();
|
|
234
|
+
}
|
|
235
|
+
get open() { return this.hasAttribute('open'); }
|
|
236
|
+
set open(value) { this.toggleAttribute('open', Boolean(value)); }
|
|
237
|
+
get disabled() { return this.hasAttribute('disabled'); }
|
|
238
|
+
set disabled(value) { this.toggleAttribute('disabled', Boolean(value)); }
|
|
239
|
+
get clearable() { return this.getAttribute('clearable') !== 'false'; }
|
|
240
|
+
set clearable(value) { this.setAttribute('clearable', String(Boolean(value))); }
|
|
241
|
+
get changeOnSelect() { return this.hasAttribute('change-on-select') && this.getAttribute('change-on-select') !== 'false'; }
|
|
242
|
+
set changeOnSelect(value) { this.toggleAttribute('change-on-select', Boolean(value)); }
|
|
243
|
+
get placeholder() { return this.getAttribute('placeholder') ?? 'Please select'; }
|
|
244
|
+
set placeholder(value) { this.setAttribute('placeholder', String(value)); }
|
|
245
|
+
get displaySeparator() { return this.getAttribute('display-separator') ?? ' / '; }
|
|
246
|
+
set displaySeparator(value) { this.setAttribute('display-separator', String(value)); }
|
|
247
|
+
_syncAttributes() {
|
|
248
|
+
if (this.hasAttribute('options'))
|
|
249
|
+
this._readOptionsAttribute();
|
|
250
|
+
if (this.hasAttribute('value'))
|
|
251
|
+
this._readValueAttribute();
|
|
252
|
+
if (this.open)
|
|
253
|
+
this._activePath = [...this._value];
|
|
254
|
+
}
|
|
255
|
+
_readOptionsAttribute() {
|
|
256
|
+
const raw = this.getAttribute('options');
|
|
257
|
+
if (!raw) {
|
|
258
|
+
this._options = [];
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
this._options = normalizeOptions(JSON.parse(raw));
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
console.warn('Invalid hy-cascader options attribute.', error);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
_readValueAttribute() {
|
|
269
|
+
const raw = this.getAttribute('value');
|
|
270
|
+
if (!raw) {
|
|
271
|
+
this._value = [];
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
this._value = this._normalizePath(JSON.parse(raw));
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
console.warn('Invalid hy-cascader value attribute.', error);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
_reflectValue() {
|
|
282
|
+
const serialized = JSON.stringify(this._value);
|
|
283
|
+
if (this.getAttribute('value') === serialized)
|
|
284
|
+
return;
|
|
285
|
+
this._reflecting = true;
|
|
286
|
+
if (this._value.length)
|
|
287
|
+
this.setAttribute('value', serialized);
|
|
288
|
+
else
|
|
289
|
+
this.removeAttribute('value');
|
|
290
|
+
this._reflecting = false;
|
|
291
|
+
}
|
|
292
|
+
_normalizePath(value) {
|
|
293
|
+
const normalized = [];
|
|
294
|
+
let options = this._options;
|
|
295
|
+
for (const segment of value) {
|
|
296
|
+
const option = options.find(item => sameValue(item.value, segment));
|
|
297
|
+
if (!option || option.disabled)
|
|
298
|
+
break;
|
|
299
|
+
normalized.push(option.value);
|
|
300
|
+
options = option.children ? [...option.children] : [];
|
|
301
|
+
}
|
|
302
|
+
return normalized;
|
|
303
|
+
}
|
|
304
|
+
_resolveOptions(value) {
|
|
305
|
+
const resolved = [];
|
|
306
|
+
let options = this._options;
|
|
307
|
+
for (const segment of value) {
|
|
308
|
+
const option = options.find(item => sameValue(item.value, segment));
|
|
309
|
+
if (!option)
|
|
310
|
+
break;
|
|
311
|
+
resolved.push(option);
|
|
312
|
+
options = option.children ? [...option.children] : [];
|
|
313
|
+
}
|
|
314
|
+
return resolved;
|
|
315
|
+
}
|
|
316
|
+
_render() {
|
|
317
|
+
const selected = this._resolveOptions(this._value);
|
|
318
|
+
const text = selected.map(option => option.label).join(this.displaySeparator);
|
|
319
|
+
this._label.textContent = text || this.placeholder;
|
|
320
|
+
this._label.classList.toggle('placeholder', !text);
|
|
321
|
+
this._trigger.disabled = this.disabled;
|
|
322
|
+
this._trigger.setAttribute('aria-expanded', String(this.open));
|
|
323
|
+
const accessibleName = this.getAttribute('aria-label');
|
|
324
|
+
if (accessibleName)
|
|
325
|
+
this._trigger.setAttribute('aria-label', accessibleName);
|
|
326
|
+
else
|
|
327
|
+
this._trigger.removeAttribute('aria-label');
|
|
328
|
+
this._clear.hidden = !this.clearable || !this._value.length;
|
|
329
|
+
this._clear.disabled = this.disabled;
|
|
330
|
+
this._panel.hidden = !this.open;
|
|
331
|
+
if (this.open)
|
|
332
|
+
this._renderPanel();
|
|
333
|
+
else
|
|
334
|
+
this._panel.replaceChildren();
|
|
335
|
+
}
|
|
336
|
+
_renderPanel() {
|
|
337
|
+
if (!this._options.length) {
|
|
338
|
+
const empty = document.createElement('div');
|
|
339
|
+
empty.className = 'empty';
|
|
340
|
+
empty.part.add('empty');
|
|
341
|
+
empty.textContent = 'No options';
|
|
342
|
+
this._panel.replaceChildren(empty);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const columns = [];
|
|
346
|
+
let options = this._options;
|
|
347
|
+
let depth = 0;
|
|
348
|
+
while (options.length) {
|
|
349
|
+
const column = document.createElement('ul');
|
|
350
|
+
column.className = 'column';
|
|
351
|
+
column.part.add('column');
|
|
352
|
+
column.dataset.depth = String(depth);
|
|
353
|
+
column.setAttribute('role', 'listbox');
|
|
354
|
+
column.setAttribute('aria-label', `Level ${depth + 1}`);
|
|
355
|
+
const activeValue = this._activePath[depth];
|
|
356
|
+
for (const option of options)
|
|
357
|
+
column.append(this._createOption(option, depth, activeValue));
|
|
358
|
+
columns.push(column);
|
|
359
|
+
const active = options.find(option => activeValue !== undefined && sameValue(option.value, activeValue));
|
|
360
|
+
if (!active?.children?.length)
|
|
361
|
+
break;
|
|
362
|
+
options = active.children;
|
|
363
|
+
depth += 1;
|
|
364
|
+
}
|
|
365
|
+
this._panel.replaceChildren(...columns);
|
|
366
|
+
}
|
|
367
|
+
_createOption(option, depth, activeValue) {
|
|
368
|
+
const row = document.createElement('li');
|
|
369
|
+
row.part.add('option-item');
|
|
370
|
+
const button = document.createElement('button');
|
|
371
|
+
button.type = 'button';
|
|
372
|
+
button.className = 'option';
|
|
373
|
+
button.part.add('option');
|
|
374
|
+
button.setAttribute('role', 'option');
|
|
375
|
+
button.setAttribute('aria-selected', String(activeValue !== undefined && sameValue(option.value, activeValue)));
|
|
376
|
+
button.disabled = Boolean(option.disabled);
|
|
377
|
+
const label = document.createElement('span');
|
|
378
|
+
label.className = 'option-label';
|
|
379
|
+
label.textContent = option.label;
|
|
380
|
+
button.append(label);
|
|
381
|
+
if (option.children?.length) {
|
|
382
|
+
button.setAttribute('aria-haspopup', 'listbox');
|
|
383
|
+
const arrow = document.createElement('span');
|
|
384
|
+
arrow.className = 'option-arrow';
|
|
385
|
+
arrow.setAttribute('aria-hidden', 'true');
|
|
386
|
+
arrow.textContent = '›';
|
|
387
|
+
button.append(arrow);
|
|
388
|
+
}
|
|
389
|
+
button.addEventListener('click', () => this._activateOption(option, depth));
|
|
390
|
+
button.addEventListener('keydown', event => this._handleOptionKeydown(event, option, depth, button));
|
|
391
|
+
row.append(button);
|
|
392
|
+
return row;
|
|
393
|
+
}
|
|
394
|
+
_activateOption(option, depth, focusNext = false) {
|
|
395
|
+
if (option.disabled)
|
|
396
|
+
return;
|
|
397
|
+
const next = [...this._activePath.slice(0, depth), option.value];
|
|
398
|
+
this._activePath = next;
|
|
399
|
+
if (option.children?.length) {
|
|
400
|
+
if (this.changeOnSelect)
|
|
401
|
+
this._commit(next, false);
|
|
402
|
+
else
|
|
403
|
+
this._renderPanel();
|
|
404
|
+
if (focusNext)
|
|
405
|
+
queueMicrotask(() => this._panel.querySelector(`.column[data-depth="${depth + 1}"]`)?.querySelector('.option:not(:disabled)')?.focus());
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
this._commit(next, true);
|
|
409
|
+
}
|
|
410
|
+
_commit(value, close) {
|
|
411
|
+
this._value = this._normalizePath(value);
|
|
412
|
+
this._activePath = [...this._value];
|
|
413
|
+
this._reflectValue();
|
|
414
|
+
if (close && this.open)
|
|
415
|
+
this.open = false;
|
|
416
|
+
else
|
|
417
|
+
this._render();
|
|
418
|
+
this.dispatchEvent(new CustomEvent('value-change', {
|
|
419
|
+
bubbles: true,
|
|
420
|
+
composed: true,
|
|
421
|
+
detail: { value: [...this._value], options: this._resolveOptions(this._value) },
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
_handleOptionKeydown(event, option, depth, button) {
|
|
425
|
+
if (event.key === 'Escape') {
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
this.open = false;
|
|
428
|
+
this._trigger.focus();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (event.key === 'ArrowRight' && option.children?.length) {
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
this._activateOption(option, depth, true);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (event.key === 'ArrowLeft' && depth > 0) {
|
|
437
|
+
event.preventDefault();
|
|
438
|
+
const previous = this._panel.querySelector(`.column[data-depth="${depth - 1}"]`);
|
|
439
|
+
(previous?.querySelector('.option[aria-selected="true"]') ?? previous?.querySelector('.option:not(:disabled)'))?.focus();
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key))
|
|
443
|
+
return;
|
|
444
|
+
event.preventDefault();
|
|
445
|
+
const buttons = [...button.closest('.column').querySelectorAll('.option:not(:disabled)')];
|
|
446
|
+
const current = buttons.indexOf(button);
|
|
447
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1
|
|
448
|
+
: (current + (event.key === 'ArrowDown' ? 1 : -1) + buttons.length) % buttons.length;
|
|
449
|
+
buttons[next]?.focus();
|
|
450
|
+
}
|
|
451
|
+
_syncOutsideListener() {
|
|
452
|
+
this._outsideAbort?.abort();
|
|
453
|
+
this._outsideAbort = null;
|
|
454
|
+
if (!this.open || !this.isConnected)
|
|
455
|
+
return;
|
|
456
|
+
const abort = new AbortController();
|
|
457
|
+
this._outsideAbort = abort;
|
|
458
|
+
queueMicrotask(() => {
|
|
459
|
+
if (abort.signal.aborted)
|
|
460
|
+
return;
|
|
461
|
+
document.addEventListener('pointerdown', event => {
|
|
462
|
+
if (!event.composedPath().includes(this))
|
|
463
|
+
this.open = false;
|
|
464
|
+
}, { capture: true, signal: abort.signal });
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
_dispatchOpenChange() {
|
|
468
|
+
this.dispatchEvent(new CustomEvent('open-change', {
|
|
469
|
+
bubbles: true,
|
|
470
|
+
composed: true,
|
|
471
|
+
detail: { open: this.open },
|
|
472
|
+
}));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
export function defineCascaderComponents() {
|
|
476
|
+
if (!customElements.get('hy-cascader'))
|
|
477
|
+
customElements.define('hy-cascader', HYCascader);
|
|
478
|
+
}
|
|
479
|
+
//# sourceMappingURL=cascader.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type HYCollapseKey = string;
|
|
2
|
+
export type HYCollapseIconPosition = 'start' | 'end';
|
|
3
|
+
export type HYCollapseTrigger = 'header' | 'icon' | 'disabled';
|
|
4
|
+
export type HYCollapseContent = string | Node | (() => string | Node);
|
|
5
|
+
export interface HYCollapseItem {
|
|
6
|
+
readonly key: HYCollapseKey;
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly content?: HYCollapseContent;
|
|
9
|
+
readonly extra?: string;
|
|
10
|
+
readonly disabled?: boolean;
|
|
11
|
+
readonly showArrow?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface HYCollapseChangeDetail {
|
|
14
|
+
readonly activeKeys: readonly HYCollapseKey[];
|
|
15
|
+
readonly changedKey: HYCollapseKey;
|
|
16
|
+
readonly expanded: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Accessible accordion and multi-panel disclosure group. */
|
|
19
|
+
export declare class HYCollapse extends HTMLElement {
|
|
20
|
+
private readonly _container;
|
|
21
|
+
private readonly _uid;
|
|
22
|
+
private _items;
|
|
23
|
+
private _itemsAssigned;
|
|
24
|
+
private _activeKeys;
|
|
25
|
+
private _panels;
|
|
26
|
+
static get observedAttributes(): string[];
|
|
27
|
+
constructor();
|
|
28
|
+
connectedCallback(): void;
|
|
29
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
30
|
+
get items(): readonly HYCollapseItem[];
|
|
31
|
+
set items(value: readonly HYCollapseItem[]);
|
|
32
|
+
get activeKeys(): readonly HYCollapseKey[];
|
|
33
|
+
set activeKeys(value: readonly HYCollapseKey[]);
|
|
34
|
+
get accordion(): boolean;
|
|
35
|
+
set accordion(value: boolean);
|
|
36
|
+
get bordered(): boolean;
|
|
37
|
+
set bordered(value: boolean);
|
|
38
|
+
get ghost(): boolean;
|
|
39
|
+
set ghost(value: boolean);
|
|
40
|
+
get iconPosition(): HYCollapseIconPosition;
|
|
41
|
+
set iconPosition(value: HYCollapseIconPosition);
|
|
42
|
+
get collapsible(): HYCollapseTrigger;
|
|
43
|
+
set collapsible(value: HYCollapseTrigger);
|
|
44
|
+
get destroyOnHidden(): boolean;
|
|
45
|
+
set destroyOnHidden(value: boolean);
|
|
46
|
+
toggle(key: HYCollapseKey, expanded?: boolean): void;
|
|
47
|
+
private _readItemsAttribute;
|
|
48
|
+
private _syncItems;
|
|
49
|
+
private _syncExpanded;
|
|
50
|
+
private _renderContent;
|
|
51
|
+
private _normalizeActiveKeys;
|
|
52
|
+
private _reflectActiveKeys;
|
|
53
|
+
private _syncLabel;
|
|
54
|
+
}
|
|
55
|
+
export declare function defineCollapseComponents(): void;
|