@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
@@ -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
  }
@@ -2,12 +2,18 @@ import {classMap} from "lit/directives/class-map.js";
2
2
  import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
3
  import {ifDefined} from "lit/directives/if-defined.js";
4
4
  import {property} from 'lit/decorators.js';
5
+ import {Store} from "../../internal/storage";
5
6
  import ZincElement from '../../internal/zinc-element';
6
7
  import ZnDropdown from "../dropdown";
7
8
  import type {ZnMenuSelectEvent} from "../../events/zn-menu-select";
8
9
 
9
10
  import styles from './navbar.scss';
10
11
 
12
+ interface StoredTab {
13
+ uri: string;
14
+ title: string;
15
+ }
16
+
11
17
  /**
12
18
  * @summary Short summary of the component's intended use.
13
19
  * @documentation https://zinc.style/components/navbar
@@ -44,6 +50,11 @@ export default class ZnNavbar extends ZincElement {
44
50
  @property({attribute: 'manual-add-items', type: Boolean}) manualAddItems = false;
45
51
  @property({type: Boolean}) isolated = false;
46
52
 
53
+ @property({attribute: 'master-id', reflect: true}) masterId: string;
54
+ @property({attribute: 'store-key', type: String}) storeKey: string = '';
55
+ @property({attribute: 'store-ttl', type: Number, reflect: true}) storeTtl = 0;
56
+ @property({attribute: 'local-storage', type: Boolean, reflect: true}) localStorage: boolean;
57
+
47
58
  private _preItems: NodeListOf<Element>;
48
59
  private _postItems: NodeListOf<Element>;
49
60
  @property()
@@ -59,6 +70,8 @@ export default class ZnNavbar extends ZincElement {
59
70
  private _expandableMargin: number = 0;
60
71
  private _totalItemWidth: number = 0;
61
72
 
73
+ protected _store: Store;
74
+
62
75
  appendItem(item: Element) {
63
76
  this._appended = this._appended || [];
64
77
  this._appended.push(item);
@@ -72,6 +85,16 @@ export default class ZnNavbar extends ZincElement {
72
85
 
73
86
  this.resizeObserver = new ResizeObserver(this.handleResize.bind(this));
74
87
  this.resizeObserver.observe(this as HTMLElement); // Observe the parent node
88
+
89
+ if (!this.masterId) {
90
+ this.masterId = this.storeKey || Math.floor(Math.random() * 1000000).toString();
91
+ }
92
+
93
+ if (this.storeKey && this.storeTtl === 0) {
94
+ this.storeTtl = 300;
95
+ }
96
+
97
+ this._store = new Store(this.localStorage ? window.localStorage : window.sessionStorage, "znnav:", this.storeTtl);
75
98
  }
76
99
 
77
100
 
@@ -92,7 +115,7 @@ export default class ZnNavbar extends ZincElement {
92
115
  let parent = this.parentElement;
93
116
  let availableWidth = this.offsetWidth || parent?.offsetWidth || 0;
94
117
 
95
- // Needs to grab the first available width that is not 0
118
+ // Needs to grab the first available width that is 0
96
119
  // This is to avoid issues with the parent/navbar not being visible
97
120
  while (availableWidth === 0 && parent?.parentElement) {
98
121
  parent = parent.parentElement;
@@ -129,7 +152,7 @@ export default class ZnNavbar extends ZincElement {
129
152
  this._navItems?.classList.toggle('has-hidden', hasHidden && hideRemaining)
130
153
  }
131
154
 
132
- public addItem(item: Element): void {
155
+ public addItem(item: Element, persist: boolean = true): void {
133
156
  const tabUri = item.getAttribute('tab-uri');
134
157
  if (typeof tabUri !== 'string' || this._openedTabs.includes(tabUri)) {
135
158
  return;
@@ -137,8 +160,15 @@ export default class ZnNavbar extends ZincElement {
137
160
  this._openedTabs.push(tabUri);
138
161
  const ul = this.shadowRoot?.querySelector('ul');
139
162
  const dropdown = this.shadowRoot?.querySelector('[id="dropdown-item"]');
140
- // @ts-expect-error
141
- dropdown.querySelector('zn-dropdown').hide();
163
+
164
+ if (persist && dropdown) {
165
+ dropdown.querySelector('zn-dropdown')?.hide();
166
+ this._saveTabToStorage({
167
+ uri: tabUri,
168
+ title: (item as HTMLElement).innerText
169
+ });
170
+ }
171
+
142
172
  if (dropdown) {
143
173
  ul?.insertBefore(item, dropdown);
144
174
  } else {
@@ -159,8 +189,21 @@ export default class ZnNavbar extends ZincElement {
159
189
  if (this._navItems) {
160
190
  const computed = getComputedStyle(this._navItems);
161
191
  this._navItemsGap = parseInt(computed.columnGap);
192
+
193
+ this._navItems.addEventListener('click', (e) => {
194
+ const target = (e.target as HTMLElement).closest('li[tab-uri]');
195
+ if (target && this.storeKey) {
196
+ const uri = target.getAttribute('tab-uri');
197
+ if (uri) {
198
+ this._store.set(this.storeKey + ':active', uri);
199
+ }
200
+ }
201
+ });
162
202
  }
163
203
 
204
+ // Load persisted tabs before calculating widths
205
+ this._loadStoredTabs();
206
+
164
207
  setTimeout(() => {
165
208
  const items = this._navItems?.querySelectorAll('li') || [];
166
209
  for (const item of items) {
@@ -182,7 +225,7 @@ export default class ZnNavbar extends ZincElement {
182
225
  const li = document.createElement('li');
183
226
  li.setAttribute('tab-uri', (e.detail.element as HTMLElement).getAttribute('data-path')!);
184
227
  li.innerText = e.detail.value;
185
- this.addItem(li);
228
+ this.addItem(li, true);
186
229
  setTimeout(() => {
187
230
  li.click();
188
231
  }, 300);
@@ -192,8 +235,55 @@ export default class ZnNavbar extends ZincElement {
192
235
  }
193
236
  }
194
237
 
195
- showMore() {
196
- //console.log("Showing More")
238
+ private _loadStoredTabs() {
239
+ if (!this.storeKey || !this._store) return;
240
+
241
+ const storedData = this._store.get(this.storeKey);
242
+ const activeUri = this._store.get(this.storeKey + ':active');
243
+ if (storedData) {
244
+ try {
245
+ const tabs = JSON.parse(storedData) as StoredTab[];
246
+ if (Array.isArray(tabs)) {
247
+ tabs.forEach(tab => {
248
+ if (tab.uri && tab.title) {
249
+ const li = document.createElement('li');
250
+ li.setAttribute('tab-uri', tab.uri);
251
+ li.innerText = tab.title;
252
+ // Pass false to avoid re-saving what was just read
253
+ this.addItem(li, false);
254
+
255
+ if (activeUri && tab.uri === activeUri) {
256
+ setTimeout(() => {
257
+ li.click();
258
+ }, 100);
259
+ }
260
+ }
261
+ });
262
+ }
263
+ } catch (e) {
264
+ console.warn('ZnNavbar: Failed to load stored tabs', e);
265
+ }
266
+ }
267
+ }
268
+
269
+ private _saveTabToStorage(newTab: StoredTab) {
270
+ if (!this.storeKey || !this._store) return;
271
+
272
+ try {
273
+ const storedData = this._store.get(this.storeKey);
274
+ let tabs: StoredTab[] = [];
275
+ if (storedData) {
276
+ tabs = JSON.parse(storedData) as StoredTab[];
277
+ }
278
+
279
+ // Ensure we don't save duplicates
280
+ if (!tabs.some(t => t.uri === newTab.uri)) {
281
+ tabs.push(newTab);
282
+ this._store.set(this.storeKey, JSON.stringify(tabs));
283
+ }
284
+ } catch (e) {
285
+ console.warn('ZnNavbar: Failed to save tab', e);
286
+ }
197
287
  }
198
288
 
199
289
  private handleClick = (e: MouseEvent) => {
@@ -273,10 +363,12 @@ export default class ZnNavbar extends ZincElement {
273
363
  ${this._postItems}
274
364
  ${this._appended}
275
365
  </ul>
276
- <div class="expandables">
277
- ${this._expanding}
278
- <slot name="expand"></slot>
279
- </div>
366
+ ${this._expanding?.length > 0 ? html`
367
+ <div class="expandables">
368
+ ${this._expanding}
369
+ <slot name="expand"></slot>
370
+ </div>
371
+ ` : ''}
280
372
  </div>`;
281
373
  }
282
374
  }
@@ -25,6 +25,11 @@ ul.navbar {
25
25
  padding-top: 0;
26
26
  font-size: var(--zn-font-size-medium);
27
27
  white-space: nowrap;
28
+
29
+ a {
30
+ text-decoration: none;
31
+ color: inherit;
32
+ }
28
33
  }
29
34
  }
30
35
 
@@ -146,7 +151,7 @@ ul.navbar {
146
151
 
147
152
  }
148
153
 
149
- .navbar__container--stacked {
154
+ .navbar__container--stacked.navbar__container--icon-bar {
150
155
  display: inline;
151
156
  }
152
157
 
@@ -0,0 +1,12 @@
1
+ import ZnOptGroup from './opt-group.component';
2
+
3
+ export * from './opt-group.component';
4
+ export default ZnOptGroup;
5
+
6
+ ZnOptGroup.define('zn-opt-group');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-opt-group': ZnOptGroup;
11
+ }
12
+ }