@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.
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 +1407 -887
  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 -18
  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 +82 -23
  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 +328 -5
  137. package/src/components/input/input.scss +100 -4
  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 -8
  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 -42
  186. package/src/components/translations/translations.scss +5 -1
  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
@@ -12,6 +12,33 @@
12
12
  width: 100%;
13
13
  }
14
14
 
15
+ /* Gap variants */
16
+ .input-group--gap-sm {
17
+ gap: var(--zn-spacing-x-small);
18
+ }
19
+
20
+ .input-group--gap-md {
21
+ gap: var(--zn-spacing-small);
22
+ }
23
+
24
+ .input-group--gap-lg {
25
+ gap: var(--zn-spacing-medium);
26
+ }
27
+
28
+ /* When gap is set, restore individual border radii */
29
+ .input-group--gap ::slotted([data-zn-input-group__input]) {
30
+ margin-inline-end: 0;
31
+ }
32
+
33
+ .input-group--gap ::slotted([data-zn-input-group__input--first]),
34
+ .input-group--gap ::slotted([data-zn-input-group__input--inner]),
35
+ .input-group--gap ::slotted([data-zn-input-group__input--last]) {
36
+ --zn-input-border-radius-x-small: var(--zn-border-radius-medium);
37
+ --zn-input-border-radius-small: var(--zn-border-radius-medium);
38
+ --zn-input-border-radius-medium: var(--zn-border-radius-medium);
39
+ --zn-input-border-radius-large: var(--zn-border-radius-medium);
40
+ }
41
+
15
42
  /* Common styles for slotted inputs */
16
43
  ::slotted([data-zn-input-group__input]) {
17
44
  /* Overlap borders */
@@ -21,6 +48,15 @@
21
48
  position: relative;
22
49
  z-index: 1;
23
50
  flex: 1;
51
+ min-width: 0;
52
+ }
53
+
54
+ @for $i from 1 through 12 {
55
+ ::slotted([data-zn-input-group__input][span="#{$i}"]) {
56
+ @include wc.media-query(md) {
57
+ flex: #{$i};
58
+ }
59
+ }
24
60
  }
25
61
 
26
62
  ::slotted([data-zn-input-group__input]:focus-within) {
@@ -50,17 +86,19 @@
50
86
  margin-inline-end: 0;
51
87
  }
52
88
 
53
- /* Button overrides */
54
- ::slotted(zn-button[data-zn-input-group__input])::part(base) {
55
- border-radius: 0;
89
+ /* Button overrides - don't join to other inputs */
90
+ ::slotted(zn-button[data-zn-input-group__input]) {
91
+ flex: 0 0 auto;
92
+ margin-inline-start: var(--zn-spacing-x-small);
93
+ margin-inline-end: 0;
56
94
  }
57
95
 
58
- ::slotted(zn-button[data-zn-input-group__input--first])::part(base) {
59
- border-start-start-radius: var(--zn-border-radius-medium);
60
- border-end-start-radius: var(--zn-border-radius-medium);
96
+ ::slotted(zn-button[data-zn-input-group__input--first]),
97
+ ::slotted(zn-button[data-zn-input-group__input--inner]),
98
+ ::slotted(zn-button[data-zn-input-group__input--last]) {
99
+ --zn-input-border-radius-x-small: var(--zn-border-radius-medium);
100
+ --zn-input-border-radius-small: var(--zn-border-radius-medium);
101
+ --zn-input-border-radius-medium: var(--zn-border-radius-medium);
102
+ --zn-input-border-radius-large: var(--zn-border-radius-medium);
61
103
  }
62
104
 
63
- ::slotted(zn-button[data-zn-input-group__input--last])::part(base) {
64
- border-start-end-radius: var(--zn-border-radius-medium);
65
- border-end-end-radius: var(--zn-border-radius-medium);
66
- }
@@ -52,7 +52,7 @@ export default class ZnItem extends ZincElement {
52
52
  @property({type: Boolean, attribute: 'align-end'}) alignEnd: boolean;
53
53
 
54
54
  // align items in the center
55
- @property({type: Boolean, attribute: 'align-center'}) alignCenter: boolean;
55
+ @property({type: Boolean, attribute: 'align-center'}) alignCenter: boolean;
56
56
 
57
57
  connectedCallback() {
58
58
  super.connectedCallback();
@@ -74,12 +74,21 @@ export default class ZnItem extends ZincElement {
74
74
  return slotContent > 0 || this.innerText.length > 0 || !(this.value === undefined || this.value === null)
75
75
  }
76
76
 
77
+ protected _hasRequiredSlot(): boolean {
78
+ const slotElements = this.querySelectorAll(":scope > *:not([slot])");
79
+ return Array.from(slotElements).some(el => el.hasAttribute && el.hasAttribute('required'));
80
+ }
81
+
77
82
  render() {
78
83
  const hasIcon = this.icon && this.icon.length > 0;
79
84
 
80
85
  const headings = html`
81
86
  <div class="item__headings">
82
- <div class="item__caption" part="caption">${this.caption}</div>
87
+ <div class=${classMap({
88
+ 'item__caption': true,
89
+ 'item__caption--required': this._hasRequiredSlot()
90
+ })} part="caption">${this.caption}
91
+ </div>
83
92
  ${this.description ? html`
84
93
  <div class="item__description">${this.description}</div>` : ''}
85
94
  </div>`;
@@ -12,6 +12,7 @@
12
12
  flex-direction: row;
13
13
  padding: 0 var(--zn-spacing-medium);
14
14
  gap: var(--zn-spacing-2x-small);
15
+ min-height: 35px;
15
16
 
16
17
  @include wc.container-query(null, md) {
17
18
  --zn-item-label-max-width: 220px;
@@ -95,6 +96,12 @@
95
96
  line-height: var(--zn-line-height-loose);
96
97
  width: 100%;
97
98
  flex-shrink: 0;
99
+
100
+ &--required::after {
101
+ content: var(--zn-input-required-content);
102
+ margin-inline-start: var(--zn-input-required-content-offset);
103
+ color: var(--zn-input-required-content-color);
104
+ }
98
105
  }
99
106
 
100
107
  &__description {
@@ -0,0 +1,12 @@
1
+ import ZnKey from './key.component';
2
+
3
+ export * from './key.component';
4
+ export default ZnKey;
5
+
6
+ ZnKey.define('zn-key');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-key': ZnKey;
11
+ }
12
+ }
@@ -0,0 +1,47 @@
1
+ import {type CSSResultGroup, html, unsafeCSS} from 'lit';
2
+ import {property} from 'lit/decorators.js';
3
+ import ZincElement from '../../internal/zinc-element';
4
+
5
+ import styles from './key.scss';
6
+
7
+ /**
8
+ * @summary A legend key item, typically used alongside charts to label and toggle visibility of data series by category.
9
+ * @documentation https://zinc.style/components/key
10
+ * @status experimental
11
+ * @since 1.0
12
+ *
13
+ * @slot - The description of the key.
14
+ */
15
+ export default class ZnKey extends ZincElement {
16
+ static styles: CSSResultGroup = unsafeCSS(styles);
17
+
18
+ @property() icon: string = '';
19
+ @property() attribute: string = '';
20
+ @property() value: string = '';
21
+ @property() color: 'primary' | 'success' | 'warning' | 'error' | 'info' | 'neutral' = 'primary';
22
+ @property({type: Boolean, reflect: true}) active: boolean = false;
23
+ @property({attribute: 'icon-size', type: Number}) iconSize: number = 24;
24
+
25
+ private _handleClick() {
26
+ this.active = !this.active;
27
+ this.emit('zn-change');
28
+ }
29
+
30
+ render() {
31
+ const chipType = this.active ? this.color : 'transparent';
32
+
33
+ return html`
34
+ <div class="key" @click="${this._handleClick}">
35
+ <zn-chip
36
+ .icon="${this.icon}"
37
+ .iconSize="${this.iconSize}"
38
+ .type="${chipType}"
39
+ size="medium"
40
+ ></zn-chip>
41
+ <div class="key__description">
42
+ <slot></slot>
43
+ </div>
44
+ </div>
45
+ `;
46
+ }
47
+ }
@@ -0,0 +1,38 @@
1
+ @use "../../wc";
2
+
3
+ :host {
4
+ display: block;
5
+ }
6
+
7
+ .key {
8
+ cursor: pointer;
9
+ display: flex;
10
+ flex-direction: row;
11
+ gap: var(--zn-spacing-medium);
12
+ align-items: center;
13
+ text-align: left;
14
+
15
+ &__description {
16
+ font-size: var(--zn-font-size-small);
17
+ color: rgb(var(--zn-text-body));
18
+ opacity: 0.7;
19
+ line-height: var(--zn-line-height-normal);
20
+ user-select: none;
21
+ }
22
+ }
23
+
24
+ :host([active]) .key__label {
25
+ color: rgb(var(--zn-text-heading));
26
+ font-weight: 600;
27
+ }
28
+
29
+ :host(:not([active])) {
30
+ .key__description {
31
+ opacity: 0.6;
32
+ }
33
+
34
+ zn-chip {
35
+ --zn-chip-color: rgb(var(--zn-text-body));
36
+ opacity: 0.5;
37
+ }
38
+ }
@@ -0,0 +1,12 @@
1
+ import ZnKeyContainer from './key-container.component';
2
+
3
+ export * from './key-container.component';
4
+ export default ZnKeyContainer;
5
+
6
+ ZnKeyContainer.define('zn-key-container');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-key-container': ZnKeyContainer;
11
+ }
12
+ }
@@ -0,0 +1,142 @@
1
+ import {classMap} from "lit/directives/class-map.js";
2
+ import {type CSSResultGroup, html, unsafeCSS} from 'lit';
3
+ import {deepQuerySelectorAll} from "../../utilities/query";
4
+ import {property, query} from 'lit/decorators.js';
5
+ import ZincElement from '../../internal/zinc-element';
6
+ import type ZnKey from "../key/key.component";
7
+
8
+ import styles from './key-container.scss';
9
+
10
+ /**
11
+ * @summary A legend container that manages key items, typically used alongside charts to toggle visibility of data series by category.
12
+ * @documentation https://zinc.style/components/key-container
13
+ * @status experimental
14
+ * @since 1.0
15
+ *
16
+ * @slot - The content to be filtered.
17
+ * @slot keys - The key items (zn-key) that define the filters.
18
+ *
19
+ * @event zn-change - Emitted when the set of active keys changes.
20
+ */
21
+ export default class ZnKeyContainer extends ZincElement {
22
+ static styles: CSSResultGroup = unsafeCSS(styles);
23
+
24
+ @query('slot[name="keys"]') keysSlot: HTMLSlotElement;
25
+ @query('slot:not([name])') defaultSlot: HTMLSlotElement;
26
+
27
+ @property() position: 'top-end' | 'top-start' | 'bottom-end' | 'bottom-start' = 'bottom-start';
28
+ @property() target: string = ''; // ID of the target element containing items
29
+ @property({attribute: 'item-selector'}) itemSelector: string = '[data-type]';
30
+ @property({attribute: 'filter-attribute'}) filterAttribute: string = '';
31
+ @property({attribute: 'no-scroll', type: Boolean}) noScroll: boolean = false;
32
+
33
+ private _hiddenElements: Set<Element> = new Set();
34
+ private _targetElement: HTMLElement | null = null;
35
+
36
+ connectedCallback() {
37
+ super.connectedCallback();
38
+ this.updateFilters();
39
+ }
40
+
41
+ firstUpdated() {
42
+ if (this.target) {
43
+ const rootNode = this.getRootNode() as Document | ShadowRoot;
44
+ this._targetElement = rootNode.getElementById ? rootNode.getElementById(this.target) : document.getElementById(this.target);
45
+ }
46
+ this.updateFilters();
47
+ }
48
+
49
+ private _handleKeyChange() {
50
+ this.updateFilters();
51
+ this.emit('zn-change');
52
+ }
53
+
54
+ private getKeys(): ZnKey[] {
55
+ const keys = [];
56
+ if (this.keysSlot) {
57
+ keys.push(...this.keysSlot.assignedElements({flatten: true}));
58
+ }
59
+ if (this.defaultSlot) {
60
+ keys.push(...this.defaultSlot.assignedElements({flatten: true}));
61
+ }
62
+ return keys.filter(el => el.tagName.toLowerCase() === 'zn-key') as ZnKey[];
63
+ }
64
+
65
+ private updateFilters() {
66
+ const keys = this.getKeys();
67
+ if (!keys || keys.length === 0) return;
68
+
69
+ // Reset hidden elements
70
+ this._hiddenElements.forEach(el => el.removeAttribute('hidden'));
71
+ this._hiddenElements.clear();
72
+
73
+ if (this.target && !this._targetElement) {
74
+ const rootNode = this.getRootNode() as Document | ShadowRoot;
75
+ this._targetElement = rootNode.getElementById?.(this.target) ?? document.getElementById(this.target);
76
+ }
77
+ let root: Element | Document | DocumentFragment | null = this._targetElement ?? null;
78
+
79
+ if (!root) {
80
+ const slotElements = this.defaultSlot?.assignedElements({flatten: true}) ?? [];
81
+ root = slotElements.some(el => el.tagName !== 'ZN-KEY')
82
+ ? this
83
+ : this.parentElement ?? (this.parentNode as DocumentFragment);
84
+ }
85
+ if (!root) return;
86
+
87
+ const selector = this.filterAttribute ? `[${this.filterAttribute}]` : this.itemSelector;
88
+ const items = deepQuerySelectorAll(selector, root as HTMLElement, "");
89
+ items.forEach(item => {
90
+ if (item === this) return;
91
+
92
+ const matchesInactive = keys.some(key => !key.active && this.itemMatchesKey(item, key));
93
+ if (matchesInactive) {
94
+ item.setAttribute('hidden', '');
95
+ this._hiddenElements.add(item);
96
+ }
97
+ });
98
+ }
99
+
100
+ private itemMatchesKey(item: Element, key: ZnKey): boolean {
101
+ // 1. If the container specifies filterAttribute, use key.attribute as the value to look for
102
+ if (this.filterAttribute) {
103
+ const itemValue = item.getAttribute(this.filterAttribute);
104
+ if (!itemValue) return false;
105
+
106
+ const targetValue = key.attribute;
107
+ if (!targetValue) return false;
108
+
109
+ return itemValue.split(/\s+/).includes(targetValue);
110
+ }
111
+
112
+ // 2. If key specifies attribute and value
113
+ if (key.attribute && key.value) {
114
+ const itemValue = item.getAttribute(key.attribute);
115
+ if (!itemValue) return false;
116
+
117
+ return itemValue.split(/\s+/).includes(key.value);
118
+ }
119
+
120
+ // 3. If the key only specifies attribute (and no value), check for existence
121
+ if (key.attribute) {
122
+ return item.hasAttribute(key.attribute);
123
+ }
124
+
125
+ return false;
126
+ }
127
+
128
+ render() {
129
+ return html`
130
+ <div class="${classMap({
131
+ 'key-container': true,
132
+ 'key-container--scroll': !this.noScroll,
133
+ })}" @zn-change="${this._handleKeyChange}">
134
+ ${this.target ? '' : html`
135
+ <div class="key-container__content">
136
+ <slot @slotchange="${this.updateFilters.bind(this)}" class="key-container__keys"></slot>
137
+ </div>
138
+ `}
139
+ </div>
140
+ `;
141
+ }
142
+ }
@@ -0,0 +1,33 @@
1
+ @use "../../wc";
2
+
3
+ :host {
4
+ display: contents;
5
+ }
6
+
7
+ .key-container--scroll {
8
+ height: 100%;
9
+ width: 100%;
10
+ overflow: hidden;
11
+ position: relative;
12
+ }
13
+
14
+ .key-container__content {
15
+ height: 100%;
16
+ width: 100%;
17
+ overflow: auto;
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .key-container__keys {
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: var(--zn-spacing-medium);
25
+ }
26
+
27
+ ::slotted(zn-key) {
28
+ flex: 0 0 auto;
29
+ }
30
+
31
+ ::slotted([hidden]) {
32
+ display: none !important;
33
+ }
@@ -78,6 +78,21 @@ export default class ZnMenuItem extends ZincElement {
78
78
 
79
79
  @property({type: Boolean, reflect: true}) confirm = false;
80
80
 
81
+ /** Removes all padding from the menu item. */
82
+ @property({type: Boolean, reflect: true}) flush = false;
83
+
84
+ /** Removes horizontal (left/right) padding only. Ignored if flush is set. */
85
+ @property({type: Boolean, reflect: true, attribute: 'flush-x'}) flushX = false;
86
+
87
+ /** Removes vertical (top/bottom) padding only. Ignored if flush is set. */
88
+ @property({type: Boolean, reflect: true, attribute: 'flush-y'}) flushY = false;
89
+
90
+ /** Removes the border from the menu item. */
91
+ @property({type: Boolean, reflect: true, attribute: 'no-border'}) noBorder = false;
92
+
93
+ /** Marks the menu item as currently active/selected. */
94
+ @property({type: Boolean, reflect: true}) active = false;
95
+
81
96
  private readonly localize = new LocalizeController(this);
82
97
  private readonly hasSlotController = new HasSlotController(this, 'submenu');
83
98
  private submenuController: SubmenuController = new SubmenuController(this, this.hasSlotController, this.localize);
@@ -209,6 +224,11 @@ export default class ZnMenuItem extends ZincElement {
209
224
  'menu-item--success': this.color === 'success',
210
225
  'menu-item--warning': this.color === 'warning',
211
226
  'menu-item--transparent': this.color === 'transparent',
227
+ 'menu-item--flush': this.flush,
228
+ 'menu-item--flush-x': this.flushX && !this.flush,
229
+ 'menu-item--flush-y': this.flushY && !this.flush,
230
+ 'menu-item--no-border': this.noBorder,
231
+ 'menu-item--active': this.active,
212
232
 
213
233
  })}
214
234
  ?aria-haspopup="${this.isSubmenu()}"
@@ -23,7 +23,7 @@
23
23
  white-space: nowrap;
24
24
  cursor: pointer;
25
25
  border-bottom-width: 1px;
26
-
26
+ width: 100%;
27
27
  text-decoration: none;
28
28
  color: var(--color);
29
29
  }
@@ -44,6 +44,72 @@
44
44
  cursor: wait;
45
45
  }
46
46
 
47
+ // Flush variants - remove padding
48
+ .menu-item--flush {
49
+ padding: 0;
50
+ }
51
+
52
+ .menu-item--flush-x {
53
+ padding-left: 0;
54
+ padding-right: 0;
55
+ }
56
+
57
+ .menu-item--flush-y {
58
+ padding-top: 0;
59
+ padding-bottom: 0;
60
+ }
61
+
62
+ // No border variant
63
+ .menu-item--no-border {
64
+ border-bottom-width: 0;
65
+ }
66
+
67
+ // Active state - vertical strip indicator
68
+ .menu-item--active::before {
69
+ content: '';
70
+ position: absolute;
71
+ left: 0;
72
+ top: 0;
73
+ bottom: 0;
74
+ width: 3px;
75
+ background-color: var(--hover-color);
76
+ }
77
+
78
+ .menu-item.menu-item--active .menu-item__label {
79
+ font-weight: 700;
80
+ color: var(--hover-color);
81
+ }
82
+
83
+ .menu-item--rtl.menu-item--active::before {
84
+ left: auto;
85
+ right: 0;
86
+ }
87
+
88
+ // Active state with color variants
89
+ .menu-item--active.menu-item--primary::before {
90
+ background-color: rgb(var(--zn-primary));
91
+ }
92
+
93
+ .menu-item--active.menu-item--error::before {
94
+ background-color: rgb(var(--zn-color-error));
95
+ }
96
+
97
+ .menu-item--active.menu-item--info::before {
98
+ background-color: rgb(var(--zn-color-info));
99
+ }
100
+
101
+ .menu-item--active.menu-item--warning::before {
102
+ background-color: rgb(var(--zn-color-warning));
103
+ }
104
+
105
+ .menu-item--active.menu-item--success::before {
106
+ background-color: rgb(var(--zn-color-success));
107
+ }
108
+
109
+ .menu-item--active.menu-item--secondary::before {
110
+ background-color: rgb(var(--zn-accent));
111
+ }
112
+
47
113
  .menu-item .menu-item__label {
48
114
  display: flex;
49
115
  flex: 1 1 auto;
@@ -99,7 +165,8 @@
99
165
  }
100
166
 
101
167
  :host(:hover:not([aria-disabled='true'], :focus-visible)) .menu-item,
102
- .menu-item--submenu-expanded {
168
+ .menu-item--submenu-expanded,
169
+ .menu-item--active {
103
170
  background-color: var(--hover-bg);
104
171
  color: var(--hover-color);
105
172
  }