@kubex/zinc 1.0.25 → 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.
Files changed (198) hide show
  1. package/.claude/project.md +556 -0
  2. package/.github/workflows/js_build_and_deploy.yaml +9 -10
  3. package/dist/custom-elements.json +13431 -8728
  4. package/dist/vscode.html-custom-data.json +571 -37
  5. package/dist/web-types.json +1439 -181
  6. package/dist/zn.d.ts +1642 -661
  7. package/dist/zn.min.css +1 -1
  8. package/dist/zn.min.js +1406 -891
  9. package/docs/_utilities/code-previews.cjs +10 -1
  10. package/docs/assets/scripts/docs.js +42 -18
  11. package/docs/data/data-table.json +229 -66
  12. package/docs/data/empty.json +6 -0
  13. package/docs/data/products-table.json +41 -193
  14. package/docs/pages/components/action-bar.md +121 -6
  15. package/docs/pages/components/alert.md +118 -15
  16. package/docs/pages/components/animated-button.md +299 -0
  17. package/docs/pages/components/audio-select.md +534 -0
  18. package/docs/pages/components/bulk-actions.md +79 -5
  19. package/docs/pages/components/button-group.md +235 -27
  20. package/docs/pages/components/button.md +139 -1
  21. package/docs/pages/components/chart.md +365 -9
  22. package/docs/pages/components/checkbox.md +387 -58
  23. package/docs/pages/components/chip.md +253 -33
  24. package/docs/pages/components/collapsible.md +393 -46
  25. package/docs/pages/components/cols.md +152 -30
  26. package/docs/pages/components/confirm.md +182 -16
  27. package/docs/pages/components/copy-button.md +169 -6
  28. package/docs/pages/components/data-select.md +194 -15
  29. package/docs/pages/components/data-table-filter.md +96 -5
  30. package/docs/pages/components/data-table-search.md +451 -0
  31. package/docs/pages/components/data-table-sort.md +66 -5
  32. package/docs/pages/components/data-table.md +727 -66
  33. package/docs/pages/components/datepicker.md +402 -7
  34. package/docs/pages/components/defined-label.md +120 -5
  35. package/docs/pages/components/dialog.md +339 -59
  36. package/docs/pages/components/dropdown.md +366 -11
  37. package/docs/pages/components/editor.md +604 -7
  38. package/docs/pages/components/empty-state.md +315 -16
  39. package/docs/pages/components/expanding-action.md +212 -28
  40. package/docs/pages/components/file.md +421 -12
  41. package/docs/pages/components/filter-container.md +158 -5
  42. package/docs/pages/components/filter-wrapper.md +167 -6
  43. package/docs/pages/components/form-group.md +383 -6
  44. package/docs/pages/components/hover-container.md +237 -6
  45. package/docs/pages/components/icon-picker.md +121 -0
  46. package/docs/pages/components/icon.md +454 -48
  47. package/docs/pages/components/inline-edit.md +787 -19
  48. package/docs/pages/components/input-group.md +207 -13
  49. package/docs/pages/components/input.md +194 -0
  50. package/docs/pages/components/key-container.md +115 -0
  51. package/docs/pages/components/key.md +101 -0
  52. package/docs/pages/components/menu.md +512 -6
  53. package/docs/pages/components/note.md +239 -36
  54. package/docs/pages/components/opt-group.md +107 -0
  55. package/docs/pages/components/pagination.md +332 -6
  56. package/docs/pages/components/pane.md +222 -6
  57. package/docs/pages/components/panel.md +213 -26
  58. package/docs/pages/components/popup.md +1354 -6
  59. package/docs/pages/components/priority-list.md +233 -0
  60. package/docs/pages/components/progress-bar.md +387 -6
  61. package/docs/pages/components/radio-group.md +242 -5
  62. package/docs/pages/components/radio.md +474 -6
  63. package/docs/pages/components/rating.md +331 -7
  64. package/docs/pages/components/reveal.md +524 -6
  65. package/docs/pages/components/scroll-container.md +552 -6
  66. package/docs/pages/components/select.md +762 -160
  67. package/docs/pages/components/skeleton.md +473 -30
  68. package/docs/pages/components/slideout.md +261 -5
  69. package/docs/pages/components/sp.md +99 -41
  70. package/docs/pages/components/split-button.md +347 -9
  71. package/docs/pages/components/split-pane.md +445 -6
  72. package/docs/pages/components/stat.md +549 -9
  73. package/docs/pages/components/status-indicator.md +230 -1
  74. package/docs/pages/components/stepper.md +563 -6
  75. package/docs/pages/components/tabs.md +439 -13
  76. package/docs/pages/components/textarea.md +415 -17
  77. package/docs/pages/components/tile.md +385 -26
  78. package/docs/pages/components/toggle.md +424 -6
  79. package/docs/pages/components/tooltip.md +367 -0
  80. package/docs/pages/components/translation-group.md +210 -0
  81. package/docs/pages/components/translations.md +319 -19
  82. package/docs/pages/components/vertical-stepper.md +401 -8
  83. package/docs/pages/components/well.md +300 -3
  84. package/docs/pages/getting-started/form-controls.md +162 -0
  85. package/docs/pages/index.md +116 -15
  86. package/package.json +5 -6
  87. package/scss/_global-spacing.scss +4 -0
  88. package/scss/_root.scss +2 -2
  89. package/scss/boot.scss +19 -0
  90. package/scss/shared/layout.scss +5 -0
  91. package/scss/themes/_light.scss +3 -3
  92. package/src/components/alert/alert.scss +1 -0
  93. package/src/components/animated-button/README.md +306 -0
  94. package/src/components/animated-button/animated-button.component.ts +229 -0
  95. package/src/components/animated-button/animated-button.scss +228 -0
  96. package/src/components/animated-button/animated-button.test.ts +143 -0
  97. package/src/components/animated-button/index.ts +12 -0
  98. package/src/components/button/button.component.ts +5 -0
  99. package/src/components/button-group/button-group.component.ts +25 -13
  100. package/src/components/button-group/button-group.scss +6 -0
  101. package/src/components/checkbox/checkbox.component.ts +4 -0
  102. package/src/components/checkbox/checkbox.scss +7 -4
  103. package/src/components/checkbox-group/checkbox-group.scss +7 -0
  104. package/src/components/chip/chip.component.ts +7 -2
  105. package/src/components/chip/chip.scss +16 -2
  106. package/src/components/collapsible/collapsible.component.ts +27 -20
  107. package/src/components/cols/cols.component.ts +14 -2
  108. package/src/components/cols/cols.scss +6 -12
  109. package/src/components/confirm/confirm.component.ts +7 -0
  110. package/src/components/copy-button/copy-button.component.ts +12 -11
  111. package/src/components/data-select/data-select.component.ts +215 -44
  112. package/src/components/data-select/data-select.scss +66 -6
  113. package/src/components/data-select/providers/currency-data-provider.ts +20 -6
  114. package/src/components/data-select/providers/provider.ts +1 -0
  115. package/src/components/data-select/providers/us-state-data-provider.ts +68 -0
  116. package/src/components/data-table/data-table.component.ts +81 -22
  117. package/src/components/data-table-search/data-table-search.component.ts +184 -0
  118. package/src/components/data-table-search/data-table-search.scss +17 -0
  119. package/src/components/data-table-search/data-table-search.test.ts +11 -0
  120. package/src/components/data-table-search/index.ts +12 -0
  121. package/src/components/datepicker/datepicker.component.ts +347 -13
  122. package/src/components/dialog/dialog.component.ts +1 -1
  123. package/src/components/empty-state/empty-state.scss +6 -1
  124. package/src/components/header/header.scss +3 -3
  125. package/src/components/icon/icon.component.ts +23 -1
  126. package/src/components/icon/icon.scss +23 -0
  127. package/src/components/icon-picker/brand-icons.ts +24 -0
  128. package/src/components/icon-picker/icon-picker.component.ts +407 -0
  129. package/src/components/icon-picker/icon-picker.scss +205 -0
  130. package/src/components/icon-picker/index.ts +12 -0
  131. package/src/components/icon-picker/line-icons.ts +612 -0
  132. package/src/components/icon-picker/material-icons.ts +14543 -0
  133. package/src/components/inline-edit/inline-edit.component.ts +190 -46
  134. package/src/components/inline-edit/inline-edit.scss +29 -0
  135. package/src/components/inline-edit/inline-edit.test.ts +314 -1
  136. package/src/components/input/input.component.ts +324 -5
  137. package/src/components/input/input.scss +87 -0
  138. package/src/components/input-group/input-group.component.ts +28 -7
  139. package/src/components/input-group/input-group.scss +48 -10
  140. package/src/components/item/item.component.ts +11 -2
  141. package/src/components/item/item.scss +7 -0
  142. package/src/components/key/index.ts +12 -0
  143. package/src/components/key/key.component.ts +47 -0
  144. package/src/components/key/key.scss +38 -0
  145. package/src/components/key-container/index.ts +12 -0
  146. package/src/components/key-container/key-container.component.ts +142 -0
  147. package/src/components/key-container/key-container.scss +33 -0
  148. package/src/components/menu-item/menu-item.component.ts +20 -0
  149. package/src/components/menu-item/menu-item.scss +69 -2
  150. package/src/components/navbar/navbar.component.ts +103 -11
  151. package/src/components/navbar/navbar.scss +6 -1
  152. package/src/components/opt-group/index.ts +12 -0
  153. package/src/components/opt-group/opt-group.component.ts +74 -0
  154. package/src/components/opt-group/opt-group.scss +37 -0
  155. package/src/components/opt-group/opt-group.test.ts +27 -0
  156. package/src/components/option/option.component.ts +1 -16
  157. package/src/components/option/option.scss +4 -0
  158. package/src/components/panel/panel.component.ts +10 -8
  159. package/src/components/panel/panel.scss +20 -0
  160. package/src/components/priority-list/index.ts +12 -0
  161. package/src/components/priority-list/priority-list.component.ts +611 -0
  162. package/src/components/priority-list/priority-list.scss +185 -0
  163. package/src/components/priority-list/priority-list.test.ts +139 -0
  164. package/src/components/progress-bar/progress-bar.component.ts +22 -16
  165. package/src/components/radio/radio.scss +10 -3
  166. package/src/components/select/select.component.ts +812 -72
  167. package/src/components/select/select.scss +108 -2
  168. package/src/components/select/select.test.ts +147 -2
  169. package/src/components/settings-container/settings-container.component.ts +44 -19
  170. package/src/components/settings-container/settings-container.scss +1 -1
  171. package/src/components/skeleton/skeleton.component.ts +6 -1
  172. package/src/components/slideout/slideout.scss +8 -3
  173. package/src/components/sp/sp.component.ts +3 -1
  174. package/src/components/sp/sp.scss +19 -0
  175. package/src/components/stepper/stepper.component.ts +16 -10
  176. package/src/components/textarea/textarea.scss +0 -4
  177. package/src/components/tile/tile.component.ts +12 -3
  178. package/src/components/tile/tile.scss +1 -1
  179. package/src/components/timer/timer.component.ts +15 -2
  180. package/src/components/toggle/toggle.component.ts +29 -26
  181. package/src/components/toggle/toggle.scss +13 -1
  182. package/src/components/translation-group/index.ts +12 -0
  183. package/src/components/translation-group/translation-group.component.ts +207 -0
  184. package/src/components/translation-group/translation-group.scss +8 -0
  185. package/src/components/translations/translations.component.ts +90 -44
  186. package/src/components/translations/translations.scss +4 -0
  187. package/src/components/vertical-stepper/vertical-stepper.component.ts +14 -9
  188. package/src/events/events.ts +12 -7
  189. package/src/events/zn-language-change.ts +7 -0
  190. package/src/events/zn-purchase.ts +11 -0
  191. package/src/events/zn-redirect.ts +7 -0
  192. package/src/events/zn-reorder.ts +7 -0
  193. package/src/events/zn-search-change.ts +11 -0
  194. package/src/events/zn-submit.ts +1 -1
  195. package/src/form-control.scss +4 -0
  196. package/src/internal/form-navigation.ts +510 -0
  197. package/src/internal/form.ts +6 -0
  198. 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 { expect, fixture, html } from '@open-wc/testing';
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` <zn-select></zn-select> `);
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
- let items;
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.updateFilters();
198
+ // Only update elements affected by this specific filter
199
+ this.updateSingleFilter(changedFilter, checked);
175
200
  }
176
201
 
177
202
  render() {
@@ -5,7 +5,7 @@
5
5
  }
6
6
 
7
7
  .container {
8
- padding-bottom: 30px;
8
+ padding-bottom: 50px;
9
9
  }
10
10
 
11
11
  .container--scroll {
@@ -7,10 +7,15 @@ import type {CSSResultGroup} from "lit";
7
7
  import styles from './skeleton.scss';
8
8
 
9
9
  /**
10
- * @summary Short summary of the component's intended use.
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: var(--width);
29
- background-color: var(--zn-panel-background-color);
30
- box-shadow: var(--zn-shadow-x-large);
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 Short summary of the component's intended use.
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 Short summary of the component's intended use.
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
- * @dependency zn-example
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
- * @event zn-event-name - Emitted as an example.
16
- *
17
- * @slot - The default slot.
18
- * @slot example - An example slot.
19
- *
20
- * @csspart base - The component's base wrapper.
21
- *
22
- * @cssproperty --example - An example CSS custom property.
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);
@@ -151,10 +151,6 @@
151
151
  overflow-y: hidden;
152
152
  }
153
153
 
154
- .form-control-input--flush {
155
- margin: 0 !important;
156
- }
157
-
158
154
  .textarea--transparent {
159
155
  background-color: transparent;
160
156
  box-shadow: none !important;
@@ -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
- <slot name="properties" part="properties" class="tile__properties"></slot>
103
- <slot name="actions" part="actions" class="tile__actions"></slot>
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
  }
@@ -59,7 +59,7 @@
59
59
  .tile__right {
60
60
  display: flex;
61
61
  align-items: center;
62
- justify-content: space-between;
62
+ justify-content: flex-end;
63
63
  width: 50%;
64
64
  flex-grow: 1;
65
65
  column-gap: var(--zn-spacing-large);
@@ -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
- setTimeout(() => {
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">