@kubex/zinc 1.0.24 → 1.0.98
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/.claude/project.md +556 -0
- package/.github/workflows/js_build_and_deploy.yaml +9 -10
- package/dist/custom-elements.json +13431 -8728
- package/dist/vscode.html-custom-data.json +571 -37
- package/dist/web-types.json +1439 -181
- package/dist/zn.d.ts +1642 -661
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +1407 -887
- package/docs/_utilities/code-previews.cjs +10 -1
- package/docs/assets/scripts/docs.js +42 -18
- package/docs/data/data-table.json +229 -66
- package/docs/data/empty.json +6 -0
- package/docs/data/products-table.json +41 -193
- package/docs/pages/components/action-bar.md +121 -6
- package/docs/pages/components/alert.md +118 -15
- package/docs/pages/components/animated-button.md +299 -0
- package/docs/pages/components/audio-select.md +534 -0
- package/docs/pages/components/bulk-actions.md +79 -5
- package/docs/pages/components/button-group.md +235 -27
- package/docs/pages/components/button.md +139 -1
- package/docs/pages/components/chart.md +365 -9
- package/docs/pages/components/checkbox.md +387 -58
- package/docs/pages/components/chip.md +253 -33
- package/docs/pages/components/collapsible.md +393 -46
- package/docs/pages/components/cols.md +152 -30
- package/docs/pages/components/confirm.md +182 -16
- package/docs/pages/components/copy-button.md +169 -6
- package/docs/pages/components/data-select.md +194 -15
- package/docs/pages/components/data-table-filter.md +96 -5
- package/docs/pages/components/data-table-search.md +451 -0
- package/docs/pages/components/data-table-sort.md +66 -5
- package/docs/pages/components/data-table.md +727 -66
- package/docs/pages/components/datepicker.md +402 -7
- package/docs/pages/components/defined-label.md +120 -5
- package/docs/pages/components/dialog.md +339 -59
- package/docs/pages/components/dropdown.md +366 -11
- package/docs/pages/components/editor.md +604 -7
- package/docs/pages/components/empty-state.md +315 -16
- package/docs/pages/components/expanding-action.md +212 -28
- package/docs/pages/components/file.md +421 -12
- package/docs/pages/components/filter-container.md +158 -5
- package/docs/pages/components/filter-wrapper.md +167 -6
- package/docs/pages/components/form-group.md +383 -6
- package/docs/pages/components/hover-container.md +237 -6
- package/docs/pages/components/icon-picker.md +121 -0
- package/docs/pages/components/icon.md +454 -48
- package/docs/pages/components/inline-edit.md +787 -19
- package/docs/pages/components/input-group.md +207 -13
- package/docs/pages/components/input.md +194 -0
- package/docs/pages/components/key-container.md +115 -0
- package/docs/pages/components/key.md +101 -0
- package/docs/pages/components/menu.md +512 -6
- package/docs/pages/components/note.md +239 -36
- package/docs/pages/components/opt-group.md +107 -0
- package/docs/pages/components/pagination.md +332 -6
- package/docs/pages/components/pane.md +222 -6
- package/docs/pages/components/panel.md +213 -26
- package/docs/pages/components/popup.md +1354 -6
- package/docs/pages/components/priority-list.md +233 -0
- package/docs/pages/components/progress-bar.md +387 -6
- package/docs/pages/components/radio-group.md +242 -5
- package/docs/pages/components/radio.md +474 -6
- package/docs/pages/components/rating.md +331 -7
- package/docs/pages/components/reveal.md +524 -6
- package/docs/pages/components/scroll-container.md +552 -6
- package/docs/pages/components/select.md +762 -160
- package/docs/pages/components/skeleton.md +473 -30
- package/docs/pages/components/slideout.md +261 -5
- package/docs/pages/components/sp.md +99 -41
- package/docs/pages/components/split-button.md +347 -9
- package/docs/pages/components/split-pane.md +445 -6
- package/docs/pages/components/stat.md +549 -9
- package/docs/pages/components/status-indicator.md +230 -1
- package/docs/pages/components/stepper.md +563 -6
- package/docs/pages/components/tabs.md +439 -13
- package/docs/pages/components/textarea.md +415 -17
- package/docs/pages/components/tile.md +385 -26
- package/docs/pages/components/toggle.md +424 -6
- package/docs/pages/components/tooltip.md +367 -0
- package/docs/pages/components/translation-group.md +210 -0
- package/docs/pages/components/translations.md +319 -18
- package/docs/pages/components/vertical-stepper.md +401 -8
- package/docs/pages/components/well.md +300 -3
- package/docs/pages/getting-started/form-controls.md +162 -0
- package/docs/pages/index.md +116 -15
- package/package.json +5 -6
- package/scss/_global-spacing.scss +4 -0
- package/scss/_root.scss +2 -2
- package/scss/boot.scss +19 -0
- package/scss/shared/layout.scss +5 -0
- package/scss/themes/_light.scss +3 -3
- package/src/components/alert/alert.scss +1 -0
- package/src/components/animated-button/README.md +306 -0
- package/src/components/animated-button/animated-button.component.ts +229 -0
- package/src/components/animated-button/animated-button.scss +228 -0
- package/src/components/animated-button/animated-button.test.ts +143 -0
- package/src/components/animated-button/index.ts +12 -0
- package/src/components/button/button.component.ts +5 -0
- package/src/components/button-group/button-group.component.ts +25 -13
- package/src/components/button-group/button-group.scss +6 -0
- package/src/components/checkbox/checkbox.component.ts +4 -0
- package/src/components/checkbox/checkbox.scss +7 -4
- package/src/components/checkbox-group/checkbox-group.scss +7 -0
- package/src/components/chip/chip.component.ts +7 -2
- package/src/components/chip/chip.scss +16 -2
- package/src/components/collapsible/collapsible.component.ts +27 -20
- package/src/components/cols/cols.component.ts +14 -2
- package/src/components/cols/cols.scss +6 -12
- package/src/components/confirm/confirm.component.ts +7 -0
- package/src/components/copy-button/copy-button.component.ts +12 -11
- package/src/components/data-select/data-select.component.ts +215 -44
- package/src/components/data-select/data-select.scss +66 -6
- package/src/components/data-select/providers/currency-data-provider.ts +20 -6
- package/src/components/data-select/providers/provider.ts +1 -0
- package/src/components/data-select/providers/us-state-data-provider.ts +68 -0
- package/src/components/data-table/data-table.component.ts +82 -23
- package/src/components/data-table-search/data-table-search.component.ts +184 -0
- package/src/components/data-table-search/data-table-search.scss +17 -0
- package/src/components/data-table-search/data-table-search.test.ts +11 -0
- package/src/components/data-table-search/index.ts +12 -0
- package/src/components/datepicker/datepicker.component.ts +347 -13
- package/src/components/dialog/dialog.component.ts +1 -1
- package/src/components/empty-state/empty-state.scss +6 -1
- package/src/components/header/header.scss +3 -3
- package/src/components/icon/icon.component.ts +23 -1
- package/src/components/icon/icon.scss +23 -0
- package/src/components/icon-picker/brand-icons.ts +24 -0
- package/src/components/icon-picker/icon-picker.component.ts +407 -0
- package/src/components/icon-picker/icon-picker.scss +205 -0
- package/src/components/icon-picker/index.ts +12 -0
- package/src/components/icon-picker/line-icons.ts +612 -0
- package/src/components/icon-picker/material-icons.ts +14543 -0
- package/src/components/inline-edit/inline-edit.component.ts +190 -46
- package/src/components/inline-edit/inline-edit.scss +29 -0
- package/src/components/inline-edit/inline-edit.test.ts +314 -1
- package/src/components/input/input.component.ts +328 -5
- package/src/components/input/input.scss +100 -4
- package/src/components/input-group/input-group.component.ts +28 -7
- package/src/components/input-group/input-group.scss +48 -10
- package/src/components/item/item.component.ts +11 -2
- package/src/components/item/item.scss +7 -0
- package/src/components/key/index.ts +12 -0
- package/src/components/key/key.component.ts +47 -0
- package/src/components/key/key.scss +38 -0
- package/src/components/key-container/index.ts +12 -0
- package/src/components/key-container/key-container.component.ts +142 -0
- package/src/components/key-container/key-container.scss +33 -0
- package/src/components/menu-item/menu-item.component.ts +20 -0
- package/src/components/menu-item/menu-item.scss +69 -2
- package/src/components/navbar/navbar.component.ts +103 -8
- package/src/components/navbar/navbar.scss +6 -1
- package/src/components/opt-group/index.ts +12 -0
- package/src/components/opt-group/opt-group.component.ts +74 -0
- package/src/components/opt-group/opt-group.scss +37 -0
- package/src/components/opt-group/opt-group.test.ts +27 -0
- package/src/components/option/option.component.ts +1 -16
- package/src/components/option/option.scss +4 -0
- package/src/components/panel/panel.component.ts +10 -8
- package/src/components/panel/panel.scss +20 -0
- package/src/components/priority-list/index.ts +12 -0
- package/src/components/priority-list/priority-list.component.ts +611 -0
- package/src/components/priority-list/priority-list.scss +185 -0
- package/src/components/priority-list/priority-list.test.ts +139 -0
- package/src/components/progress-bar/progress-bar.component.ts +22 -16
- package/src/components/radio/radio.scss +10 -3
- package/src/components/select/select.component.ts +812 -72
- package/src/components/select/select.scss +108 -2
- package/src/components/select/select.test.ts +147 -2
- package/src/components/settings-container/settings-container.component.ts +44 -19
- package/src/components/settings-container/settings-container.scss +1 -1
- package/src/components/skeleton/skeleton.component.ts +6 -1
- package/src/components/slideout/slideout.scss +8 -3
- package/src/components/sp/sp.component.ts +3 -1
- package/src/components/sp/sp.scss +19 -0
- package/src/components/stepper/stepper.component.ts +16 -10
- package/src/components/textarea/textarea.scss +0 -4
- package/src/components/tile/tile.component.ts +12 -3
- package/src/components/tile/tile.scss +1 -1
- package/src/components/timer/timer.component.ts +15 -2
- package/src/components/toggle/toggle.component.ts +29 -26
- package/src/components/toggle/toggle.scss +13 -1
- package/src/components/translation-group/index.ts +12 -0
- package/src/components/translation-group/translation-group.component.ts +207 -0
- package/src/components/translation-group/translation-group.scss +8 -0
- package/src/components/translations/translations.component.ts +90 -42
- package/src/components/translations/translations.scss +5 -1
- package/src/components/vertical-stepper/vertical-stepper.component.ts +14 -9
- package/src/events/events.ts +12 -7
- package/src/events/zn-language-change.ts +7 -0
- package/src/events/zn-purchase.ts +11 -0
- package/src/events/zn-redirect.ts +7 -0
- package/src/events/zn-reorder.ts +7 -0
- package/src/events/zn-search-change.ts +11 -0
- package/src/events/zn-submit.ts +1 -1
- package/src/form-control.scss +4 -0
- package/src/internal/form-navigation.ts +510 -0
- package/src/internal/form.ts +6 -0
- package/src/zinc.ts +91 -83
|
@@ -82,8 +82,9 @@
|
|
|
82
82
|
outline: none;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
/* Visually hide the display input when multiple is enabled */
|
|
86
|
-
.select--multiple:not(.select--placeholder-visible) .select__display-input
|
|
85
|
+
/* Visually hide the display input when multiple is enabled (but not when search is active and open) */
|
|
86
|
+
.select--multiple:not(.select--placeholder-visible):not(.select--search) .select__display-input,
|
|
87
|
+
.select--multiple.select--search:not(.select--open):not(.select--placeholder-visible) .select__display-input {
|
|
87
88
|
position: absolute;
|
|
88
89
|
z-index: -1;
|
|
89
90
|
top: 0;
|
|
@@ -93,6 +94,41 @@
|
|
|
93
94
|
opacity: 0;
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
/* When multiple + search + open, shrink the input to fit inline with the tags */
|
|
98
|
+
.select--multiple.select--search.select--open .select__display-input {
|
|
99
|
+
flex: 1 1 60px;
|
|
100
|
+
min-width: 60px;
|
|
101
|
+
width: auto;
|
|
102
|
+
order: 2;
|
|
103
|
+
margin-inline-start: var(--zn-spacing-2x-small);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* When multiple + search + open, place tags before the input */
|
|
107
|
+
.select--multiple.select--search.select--open .select__prefix {
|
|
108
|
+
order: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.select--multiple.select--search.select--open .select__tags {
|
|
112
|
+
order: 1;
|
|
113
|
+
flex: 0 1 auto;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Keep clear button and expand icon at the end */
|
|
117
|
+
.select--multiple.select--search.select--open .select__clear {
|
|
118
|
+
order: 4;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.select--multiple.select--search.select--open .select__expand-icon {
|
|
122
|
+
order: 5;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Ensure the combobox wraps so the input flows after tags (only when open to avoid affecting initial size) */
|
|
126
|
+
.select--multiple.select--search.select--open .select__combobox {
|
|
127
|
+
flex-wrap: wrap;
|
|
128
|
+
gap: var(--zn-spacing-2x-small);
|
|
129
|
+
padding-inline-start: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
96
132
|
.select__value-input {
|
|
97
133
|
position: absolute;
|
|
98
134
|
top: 0;
|
|
@@ -308,6 +344,24 @@
|
|
|
308
344
|
rotate: -180deg;
|
|
309
345
|
}
|
|
310
346
|
|
|
347
|
+
/* Fetch loading spinner */
|
|
348
|
+
.select__spinner {
|
|
349
|
+
animation: spin 1s linear infinite;
|
|
350
|
+
--icon-size: var(--zn-font-size-large);
|
|
351
|
+
--icon-color: var(--zn-input-icon-color);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@keyframes spin {
|
|
355
|
+
from { rotate: 0deg; }
|
|
356
|
+
to { rotate: 360deg; }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Fetch error icon */
|
|
360
|
+
.select__fetch-error-icon {
|
|
361
|
+
--icon-size: var(--zn-font-size-large);
|
|
362
|
+
--icon-color: var(--zn-color-danger-600);
|
|
363
|
+
}
|
|
364
|
+
|
|
311
365
|
/* Listbox */
|
|
312
366
|
.select__listbox {
|
|
313
367
|
display: block;
|
|
@@ -344,3 +398,55 @@
|
|
|
344
398
|
user-select: none;
|
|
345
399
|
-webkit-user-select: none;
|
|
346
400
|
}
|
|
401
|
+
|
|
402
|
+
.select__listbox ::slotted(zn-opt-group) {
|
|
403
|
+
display: block;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.select__listbox ::slotted(zn-opt-group[hidden]) {
|
|
407
|
+
display: none;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/* Search loading indicator (remote search in-flight) */
|
|
411
|
+
.select__search-loading {
|
|
412
|
+
display: flex;
|
|
413
|
+
align-items: center;
|
|
414
|
+
justify-content: center;
|
|
415
|
+
gap: var(--zn-spacing-small);
|
|
416
|
+
padding: var(--zn-spacing-medium) var(--zn-spacing-2x-large);
|
|
417
|
+
color: var(--zn-color-neutral-500);
|
|
418
|
+
font-size: var(--zn-font-size-medium);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Max results truncation indicator */
|
|
422
|
+
.select__max-results-indicator {
|
|
423
|
+
display: block;
|
|
424
|
+
padding: var(--zn-spacing-small) var(--zn-spacing-2x-large);
|
|
425
|
+
text-align: center;
|
|
426
|
+
color: var(--zn-color-neutral-500);
|
|
427
|
+
font-size: var(--zn-font-size-small);
|
|
428
|
+
font-style: italic;
|
|
429
|
+
border-top: solid var(--zn-panel-border-width) var(--zn-panel-border-color);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/* Empty state (no search results) */
|
|
433
|
+
.select__empty-state {
|
|
434
|
+
display: block;
|
|
435
|
+
padding: var(--zn-spacing-medium) var(--zn-spacing-2x-large);
|
|
436
|
+
text-align: center;
|
|
437
|
+
color: var(--zn-color-neutral-500);
|
|
438
|
+
font-size: var(--zn-font-size-medium);
|
|
439
|
+
|
|
440
|
+
&[hidden] {
|
|
441
|
+
display: none;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* Search mode */
|
|
446
|
+
.select--search .select__combobox {
|
|
447
|
+
cursor: text;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.select--search .select__display-input {
|
|
451
|
+
cursor: text;
|
|
452
|
+
}
|
|
@@ -1,10 +1,155 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
|
-
import {
|
|
2
|
+
import {expect, fixture, html} from '@open-wc/testing';
|
|
3
|
+
import type ZnOption from '../option/option.component';
|
|
4
|
+
import type ZnSelect from './select.component';
|
|
3
5
|
|
|
4
6
|
describe('<zn-select>', () => {
|
|
5
7
|
it('should render a component', async () => {
|
|
6
|
-
const el = await fixture(html`
|
|
8
|
+
const el = await fixture(html`
|
|
9
|
+
<zn-select></zn-select> `);
|
|
7
10
|
|
|
8
11
|
expect(el).to.exist;
|
|
9
12
|
});
|
|
13
|
+
|
|
14
|
+
describe('search functionality', () => {
|
|
15
|
+
let el: ZnSelect;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
el = await fixture<ZnSelect>(html`
|
|
19
|
+
<zn-select search>
|
|
20
|
+
<zn-option value="apple">Apple</zn-option>
|
|
21
|
+
<zn-option value="banana">Banana</zn-option>
|
|
22
|
+
<zn-option value="cherry">Cherry</zn-option>
|
|
23
|
+
<zn-option value="date">Date</zn-option>
|
|
24
|
+
<zn-option value="elderberry">Elderberry</zn-option>
|
|
25
|
+
</zn-select>
|
|
26
|
+
`);
|
|
27
|
+
await el.updateComplete;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should have the search attribute set', () => {
|
|
31
|
+
expect(el.search).to.be.true;
|
|
32
|
+
expect(el.hasAttribute('search')).to.be.true;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should have a readonly display input when closed', () => {
|
|
36
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
37
|
+
expect(displayInput.readOnly).to.be.true;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should make the display input editable when opened', async () => {
|
|
41
|
+
await el.show();
|
|
42
|
+
await el.updateComplete;
|
|
43
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
44
|
+
expect(displayInput.readOnly).to.be.false;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should filter options based on typed text', async () => {
|
|
48
|
+
await el.show();
|
|
49
|
+
await el.updateComplete;
|
|
50
|
+
|
|
51
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
52
|
+
displayInput.value = 'app';
|
|
53
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
54
|
+
await el.updateComplete;
|
|
55
|
+
|
|
56
|
+
const options = el.querySelectorAll<ZnOption>('zn-option');
|
|
57
|
+
const visibleOptions = [...options].filter(o => !o.hidden);
|
|
58
|
+
const hiddenOptions = [...options].filter(o => o.hidden);
|
|
59
|
+
|
|
60
|
+
expect(visibleOptions.length).to.equal(1);
|
|
61
|
+
expect(visibleOptions[0].value).to.equal('apple');
|
|
62
|
+
expect(hiddenOptions.length).to.equal(4);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should show all options when search is cleared', async () => {
|
|
66
|
+
await el.show();
|
|
67
|
+
await el.updateComplete;
|
|
68
|
+
|
|
69
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
70
|
+
|
|
71
|
+
// Type to filter
|
|
72
|
+
displayInput.value = 'app';
|
|
73
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
74
|
+
await el.updateComplete;
|
|
75
|
+
|
|
76
|
+
// Clear the search
|
|
77
|
+
displayInput.value = '';
|
|
78
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
79
|
+
await el.updateComplete;
|
|
80
|
+
|
|
81
|
+
const options = el.querySelectorAll<ZnOption>('zn-option');
|
|
82
|
+
const visibleOptions = [...options].filter(o => !o.hidden);
|
|
83
|
+
expect(visibleOptions.length).to.equal(5);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should match against option values as well as labels', async () => {
|
|
87
|
+
await el.show();
|
|
88
|
+
await el.updateComplete;
|
|
89
|
+
|
|
90
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
91
|
+
displayInput.value = 'elder';
|
|
92
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
93
|
+
await el.updateComplete;
|
|
94
|
+
|
|
95
|
+
const options = el.querySelectorAll<ZnOption>('zn-option');
|
|
96
|
+
const visibleOptions = [...options].filter(o => !o.hidden);
|
|
97
|
+
|
|
98
|
+
expect(visibleOptions.length).to.equal(1);
|
|
99
|
+
expect(visibleOptions[0].value).to.equal('elderberry');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should not show search text after dropdown closes', async () => {
|
|
103
|
+
// Open and type a search query
|
|
104
|
+
await el.show();
|
|
105
|
+
await el.updateComplete;
|
|
106
|
+
|
|
107
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
108
|
+
displayInput.value = 'ban';
|
|
109
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
110
|
+
await el.updateComplete;
|
|
111
|
+
|
|
112
|
+
// Close the dropdown
|
|
113
|
+
await el.hide();
|
|
114
|
+
await el.updateComplete;
|
|
115
|
+
|
|
116
|
+
// Display input should not show the search text (no value selected, so should be empty)
|
|
117
|
+
expect(displayInput.value).to.not.equal('ban');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should restore all options visibility after closing', async () => {
|
|
121
|
+
await el.show();
|
|
122
|
+
await el.updateComplete;
|
|
123
|
+
|
|
124
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
125
|
+
displayInput.value = 'app';
|
|
126
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
127
|
+
await el.updateComplete;
|
|
128
|
+
|
|
129
|
+
// Close the dropdown
|
|
130
|
+
await el.hide();
|
|
131
|
+
await el.updateComplete;
|
|
132
|
+
|
|
133
|
+
// All options should be visible again
|
|
134
|
+
const options = el.querySelectorAll<ZnOption>('zn-option');
|
|
135
|
+
const hiddenOptions = [...options].filter(o => o.hidden);
|
|
136
|
+
expect(hiddenOptions.length).to.equal(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should be case-insensitive when filtering', async () => {
|
|
140
|
+
await el.show();
|
|
141
|
+
await el.updateComplete;
|
|
142
|
+
|
|
143
|
+
const displayInput = el.shadowRoot!.querySelector<HTMLInputElement>('.select__display-input')!;
|
|
144
|
+
displayInput.value = 'APPLE';
|
|
145
|
+
displayInput.dispatchEvent(new Event('input'));
|
|
146
|
+
await el.updateComplete;
|
|
147
|
+
|
|
148
|
+
const options = el.querySelectorAll<ZnOption>('zn-option');
|
|
149
|
+
const visibleOptions = [...options].filter(o => !o.hidden);
|
|
150
|
+
|
|
151
|
+
expect(visibleOptions.length).to.equal(1);
|
|
152
|
+
expect(visibleOptions[0].value).to.equal('apple');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
10
155
|
});
|
|
@@ -57,9 +57,8 @@ export default class ZnSettingsContainer extends ZincElement {
|
|
|
57
57
|
this._store = new Store(window.sessionStorage, this.storeKey);
|
|
58
58
|
|
|
59
59
|
this.recomputeFiltersFromSlot();
|
|
60
|
-
this.updateFilters();
|
|
61
60
|
|
|
62
|
-
// Load saved filter states
|
|
61
|
+
// Load saved filter states before applying filters
|
|
63
62
|
if (this.storeKey) {
|
|
64
63
|
const savedFilters = this._store.get('filters');
|
|
65
64
|
if (savedFilters) {
|
|
@@ -68,10 +67,11 @@ export default class ZnSettingsContainer extends ZincElement {
|
|
|
68
67
|
...filter,
|
|
69
68
|
checked: Object.prototype.hasOwnProperty.call(parsedFilters, filter.attribute) ? parsedFilters[filter.attribute] : filter.checked
|
|
70
69
|
}));
|
|
71
|
-
this.updateFilters();
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
this.updateFilters();
|
|
74
|
+
|
|
75
75
|
this._mutationObserver = new MutationObserver(mutations => {
|
|
76
76
|
for (const mutation of mutations) {
|
|
77
77
|
if (mutation.type === 'attributes' && mutation.attributeName === 'hidden') {
|
|
@@ -127,27 +127,47 @@ export default class ZnSettingsContainer extends ZincElement {
|
|
|
127
127
|
this.filters = nextFilters;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
private updateSingleFilter(filter: SettingsContainerFilter, checked: boolean) {
|
|
131
|
+
let items;
|
|
132
|
+
const attrAppend = filter.attribute === "*" ? `` : `[${filter.attribute}]`;
|
|
133
|
+
if (filter.itemSelector && filter.itemSelector.startsWith('>')) {
|
|
134
|
+
items = deepQuerySelectorAll(filter.itemSelector.substring(1) + attrAppend, this, "");
|
|
135
|
+
} else {
|
|
136
|
+
items = this.querySelectorAll(filter.itemSelector + attrAppend);
|
|
137
|
+
}
|
|
138
|
+
items.forEach(item => {
|
|
139
|
+
if (checked) {
|
|
140
|
+
const shouldStayHidden = this.filters.some(f => {
|
|
141
|
+
if (f.attribute === filter.attribute || f.checked) return false;
|
|
142
|
+
const fAttrAppend = f.attribute === "*" ? `` : `[${f.attribute}]`;
|
|
143
|
+
let fItems;
|
|
144
|
+
if (f.itemSelector && f.itemSelector.startsWith('>')) {
|
|
145
|
+
fItems = deepQuerySelectorAll(f.itemSelector.substring(1) + fAttrAppend, this, "");
|
|
146
|
+
} else {
|
|
147
|
+
fItems = this.querySelectorAll(f.itemSelector + fAttrAppend);
|
|
148
|
+
}
|
|
149
|
+
return Array.from(fItems).includes(item);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!shouldStayHidden) {
|
|
153
|
+
item.removeAttribute('hidden');
|
|
154
|
+
this._hiddenElements.delete(item);
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
item.setAttribute('hidden', 'true');
|
|
158
|
+
this._hiddenElements.add(item);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
130
163
|
updateFilters() {
|
|
131
164
|
// reset all items
|
|
132
165
|
this._hiddenElements.forEach(el => el.removeAttribute('hidden'));
|
|
166
|
+
this._hiddenElements.clear();
|
|
133
167
|
|
|
134
168
|
// apply filters
|
|
135
169
|
this.filters.forEach(filter => {
|
|
136
|
-
|
|
137
|
-
const attrAppend = filter.attribute === "*" ? `` : `[${filter.attribute}]`;
|
|
138
|
-
if (filter.itemSelector && filter.itemSelector.startsWith('>')) {
|
|
139
|
-
items = deepQuerySelectorAll(filter.itemSelector.substring(1) + attrAppend, this, "");
|
|
140
|
-
} else {
|
|
141
|
-
items = this.querySelectorAll(filter.itemSelector + attrAppend);
|
|
142
|
-
}
|
|
143
|
-
items.forEach(item => {
|
|
144
|
-
if (filter.checked) {
|
|
145
|
-
item.removeAttribute('hidden');
|
|
146
|
-
} else {
|
|
147
|
-
item.setAttribute('hidden', 'true');
|
|
148
|
-
this._hiddenElements.add(item);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
170
|
+
this.updateSingleFilter(filter, filter.checked);
|
|
151
171
|
});
|
|
152
172
|
}
|
|
153
173
|
|
|
@@ -155,6 +175,10 @@ export default class ZnSettingsContainer extends ZincElement {
|
|
|
155
175
|
const attribute = (e.target as ZnCheckbox).getAttribute('data-attribute');
|
|
156
176
|
const checked = (e.target as ZnCheckbox).isChecked
|
|
157
177
|
|
|
178
|
+
// Find the specific filter that changed
|
|
179
|
+
const changedFilter = this.filters.find(f => f.attribute === attribute);
|
|
180
|
+
if (!changedFilter) return;
|
|
181
|
+
|
|
158
182
|
this.filters = this.filters.map(filter => {
|
|
159
183
|
if (filter.attribute === attribute) {
|
|
160
184
|
return {...filter, checked};
|
|
@@ -171,7 +195,8 @@ export default class ZnSettingsContainer extends ZincElement {
|
|
|
171
195
|
this._store.set('filters', JSON.stringify(filterStates));
|
|
172
196
|
}
|
|
173
197
|
|
|
174
|
-
this
|
|
198
|
+
// Only update elements affected by this specific filter
|
|
199
|
+
this.updateSingleFilter(changedFilter, checked);
|
|
175
200
|
}
|
|
176
201
|
|
|
177
202
|
render() {
|
|
@@ -7,10 +7,15 @@ import type {CSSResultGroup} from "lit";
|
|
|
7
7
|
import styles from './skeleton.scss';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @summary
|
|
10
|
+
* @summary Skeleton loaders provide visual placeholders while content is loading, improving perceived performance and user experience.
|
|
11
11
|
* @documentation https://zinc.style/components/skeleton
|
|
12
12
|
* @status experimental
|
|
13
13
|
* @since 1.0
|
|
14
|
+
*
|
|
15
|
+
* @property {string} speed - Animation speed for the shimmer effect. Default: "3s"
|
|
16
|
+
* @property {string} width - Width of the skeleton element. Default: "100%"
|
|
17
|
+
* @property {string} height - Height of the skeleton element. Default: "20px"
|
|
18
|
+
* @property {string} radius - Border radius of the skeleton element. Default: "4px"
|
|
14
19
|
*/
|
|
15
20
|
export default class ZnSkeleton extends ZincElement {
|
|
16
21
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
@@ -25,15 +25,20 @@
|
|
|
25
25
|
flex-direction: column;
|
|
26
26
|
z-index: 2;
|
|
27
27
|
padding: 0;
|
|
28
|
-
width:
|
|
29
|
-
background
|
|
30
|
-
box-shadow:
|
|
28
|
+
width: 1100px;
|
|
29
|
+
background: rgb(var(--zn-body));
|
|
30
|
+
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
|
31
|
+
|
|
32
|
+
&::backdrop {
|
|
33
|
+
background: var(--zn-overlay-background-color);
|
|
34
|
+
}
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
&__header {
|
|
34
38
|
flex: 0 0 auto;
|
|
35
39
|
display: flex;
|
|
36
40
|
border-bottom: 1px solid rgb(var(--zn-border-color));
|
|
41
|
+
background-color: var(--zn-panel-background-color);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
&__title {
|
|
@@ -17,7 +17,7 @@ export const defaultSizes = {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* @summary
|
|
20
|
+
* @summary A flexible layout container for spacing and arranging child elements in rows or columns with configurable gap, padding, and dividers.
|
|
21
21
|
* @documentation https://zinc.style/components/sp
|
|
22
22
|
* @status experimental
|
|
23
23
|
* @since 1.0
|
|
@@ -43,6 +43,7 @@ export default class ZnSp extends ZincElement {
|
|
|
43
43
|
@property({attribute: 'pad-x', type: Boolean, reflect: true}) padX: boolean = false;
|
|
44
44
|
@property({attribute: 'pad-y', type: Boolean, reflect: true}) padY: boolean = false;
|
|
45
45
|
@property({attribute: 'no-gap', type: Boolean, reflect: true}) noGap: boolean = false;
|
|
46
|
+
@property({attribute: 'no-pos', type: Boolean, reflect: true}) noPosition: boolean = false;
|
|
46
47
|
@property({attribute: 'flush', type: Boolean, reflect: true}) flush: boolean = false;
|
|
47
48
|
@property({attribute: 'flush-x', type: Boolean, reflect: true}) flushX: boolean = false;
|
|
48
49
|
@property({attribute: 'flush-y', type: Boolean, reflect: true}) flushY: boolean = false;
|
|
@@ -73,6 +74,7 @@ export default class ZnSp extends ZincElement {
|
|
|
73
74
|
'sp--row': this.row,
|
|
74
75
|
'sp--grow': this.grow,
|
|
75
76
|
'sp--no-gap': this.noGap,
|
|
77
|
+
'sp--no-pos': this.noPosition,
|
|
76
78
|
'sp--flush': this.flush,
|
|
77
79
|
'sp--flush-y': this.flushY,
|
|
78
80
|
'sp--flush-x': this.flushX,
|
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
--zn-sp-gap: var(--zn-divide-gap, var(--zn-spacing-medium));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
:host([grow]) {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-grow: 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:host([max-full]) {
|
|
18
|
+
max-height: 100%;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:host([scroll]) {
|
|
23
|
+
max-height: 100%;
|
|
24
|
+
overflow: auto;
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
.sp {
|
|
13
28
|
position: relative;
|
|
14
29
|
display: flex;
|
|
@@ -49,6 +64,10 @@
|
|
|
49
64
|
flex-direction: row;
|
|
50
65
|
}
|
|
51
66
|
|
|
67
|
+
&--no-pos {
|
|
68
|
+
position: unset;
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
&--no-gap {
|
|
53
72
|
gap: 0;
|
|
54
73
|
}
|
|
@@ -5,21 +5,27 @@ import ZincElement from '../../internal/zinc-element';
|
|
|
5
5
|
import styles from './stepper.scss';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @summary
|
|
8
|
+
* @summary Steppers provide visual feedback about progress through a multi-step process or workflow.
|
|
9
9
|
* @documentation https://zinc.style/components/stepper
|
|
10
10
|
* @status experimental
|
|
11
11
|
* @since 1.0
|
|
12
12
|
*
|
|
13
|
-
* @
|
|
13
|
+
* @property {string} caption - A descriptive label displayed above the stepper to indicate the current step or phase.
|
|
14
|
+
* @property {string} label - An optional label displayed above the caption, typically used for the wizard or workflow name.
|
|
15
|
+
* @property {number} steps - The total number of steps in the process.
|
|
16
|
+
* @property {number} value - The current step position (0 to steps). Progress is calculated as value/steps.
|
|
17
|
+
* @property {boolean} show-progress - When true, displays the step count (e.g., "2 / 5 steps") next to the caption.
|
|
14
18
|
*
|
|
15
|
-
* @
|
|
16
|
-
*
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
19
|
-
*
|
|
20
|
-
* @csspart
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
19
|
+
* @csspart step-container - The container holding the progress line and steps.
|
|
20
|
+
* @csspart step-line - The background line showing the full step track.
|
|
21
|
+
* @csspart step-progress - The filled progress indicator showing completion.
|
|
22
|
+
* @csspart steps - The container for individual step markers.
|
|
23
|
+
* @csspart step - An individual step marker circle.
|
|
24
|
+
* @csspart header - The header container with caption and progress text.
|
|
25
|
+
* @csspart caption - The caption text element.
|
|
26
|
+
* @csspart progress - The progress count text (e.g., "2 / 5 steps").
|
|
27
|
+
* @csspart label - The label text container.
|
|
28
|
+
* @csspart info - The label text element.
|
|
23
29
|
*/
|
|
24
30
|
export default class ZnStepper extends ZincElement {
|
|
25
31
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
@@ -28,7 +28,7 @@ import styles from './tile.scss';
|
|
|
28
28
|
export default class ZnTile extends ZincElement {
|
|
29
29
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
30
30
|
|
|
31
|
-
private readonly hasSlotController = new HasSlotController(this, '[default]');
|
|
31
|
+
private readonly hasSlotController = new HasSlotController(this, '[default]', 'properties', 'actions', 'image');
|
|
32
32
|
|
|
33
33
|
@property({attribute: 'caption'}) caption: string;
|
|
34
34
|
|
|
@@ -54,6 +54,13 @@ export default class ZnTile extends ZincElement {
|
|
|
54
54
|
return this.href || this.dataUri;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
private _handleActionsClick(e: MouseEvent) {
|
|
58
|
+
if (this._isLink()) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
render() {
|
|
58
65
|
const isLink = this._isLink();
|
|
59
66
|
const tag = isLink ? literal`a` : literal`div`;
|
|
@@ -99,8 +106,10 @@ export default class ZnTile extends ZincElement {
|
|
|
99
106
|
</div>
|
|
100
107
|
|
|
101
108
|
<div class="tile__right">
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
${hasProperties ? html`
|
|
110
|
+
<slot name="properties" part="properties" class="tile__properties"></slot>` : ''}
|
|
111
|
+
${hasActions ? html`
|
|
112
|
+
<slot name="actions" part="actions" class="tile__actions" @click=${this._handleActionsClick}></slot>` : ''}
|
|
104
113
|
</div>`}
|
|
105
114
|
</${tag}>`;
|
|
106
115
|
}
|
|
@@ -27,15 +27,28 @@ export default class ZnTimer extends ZincElement {
|
|
|
27
27
|
@property({type: Number})
|
|
28
28
|
private timestamp = '';
|
|
29
29
|
|
|
30
|
+
private _timerId: number | null = null;
|
|
31
|
+
|
|
32
|
+
disconnectedCallback() {
|
|
33
|
+
super.disconnectedCallback();
|
|
34
|
+
if (this._timerId !== null) {
|
|
35
|
+
clearTimeout(this._timerId);
|
|
36
|
+
this._timerId = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
private _getLastMessage() {
|
|
31
41
|
const time = new Date(parseInt(this.timestamp) * 1000);
|
|
32
42
|
const diff = (new Date()).getTime() - time.getTime();
|
|
33
43
|
const times = this._getTimes(diff);
|
|
34
44
|
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
if (this._timerId !== null) {
|
|
47
|
+
clearTimeout(this._timerId);
|
|
48
|
+
}
|
|
49
|
+
this._timerId = setTimeout(() => {
|
|
37
50
|
this.requestUpdate();
|
|
38
|
-
}, 1000);
|
|
51
|
+
}, 1000) as unknown as number;
|
|
39
52
|
|
|
40
53
|
return html`
|
|
41
54
|
<div class="last-message upper-limit-reached">
|