@kubex/zinc 1.0.25 → 1.0.99

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 (199) 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 +13575 -8856
  4. package/dist/vscode.html-custom-data.json +583 -48
  5. package/dist/web-types.json +1441 -181
  6. package/dist/zn.d.ts +1645 -660
  7. package/dist/zn.min.css +1 -1
  8. package/dist/zn.min.js +1412 -893
  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/item.md +18 -0
  51. package/docs/pages/components/key-container.md +115 -0
  52. package/docs/pages/components/key.md +101 -0
  53. package/docs/pages/components/menu.md +512 -6
  54. package/docs/pages/components/note.md +239 -36
  55. package/docs/pages/components/opt-group.md +107 -0
  56. package/docs/pages/components/pagination.md +332 -6
  57. package/docs/pages/components/pane.md +222 -6
  58. package/docs/pages/components/panel.md +213 -26
  59. package/docs/pages/components/popup.md +1354 -6
  60. package/docs/pages/components/priority-list.md +233 -0
  61. package/docs/pages/components/progress-bar.md +387 -6
  62. package/docs/pages/components/radio-group.md +242 -5
  63. package/docs/pages/components/radio.md +474 -6
  64. package/docs/pages/components/rating.md +331 -7
  65. package/docs/pages/components/reveal.md +524 -6
  66. package/docs/pages/components/scroll-container.md +552 -6
  67. package/docs/pages/components/select.md +762 -160
  68. package/docs/pages/components/skeleton.md +473 -30
  69. package/docs/pages/components/slideout.md +261 -5
  70. package/docs/pages/components/sp.md +99 -41
  71. package/docs/pages/components/split-button.md +347 -9
  72. package/docs/pages/components/split-pane.md +445 -6
  73. package/docs/pages/components/stat.md +549 -9
  74. package/docs/pages/components/status-indicator.md +230 -1
  75. package/docs/pages/components/stepper.md +563 -6
  76. package/docs/pages/components/tabs.md +439 -13
  77. package/docs/pages/components/textarea.md +415 -17
  78. package/docs/pages/components/tile.md +385 -26
  79. package/docs/pages/components/toggle.md +424 -6
  80. package/docs/pages/components/tooltip.md +367 -0
  81. package/docs/pages/components/translation-group.md +210 -0
  82. package/docs/pages/components/translations.md +319 -19
  83. package/docs/pages/components/vertical-stepper.md +401 -8
  84. package/docs/pages/components/well.md +300 -3
  85. package/docs/pages/getting-started/form-controls.md +162 -0
  86. package/docs/pages/index.md +116 -15
  87. package/package.json +5 -6
  88. package/scss/_global-spacing.scss +4 -0
  89. package/scss/_root.scss +2 -2
  90. package/scss/boot.scss +19 -0
  91. package/scss/shared/layout.scss +5 -0
  92. package/scss/themes/_light.scss +3 -3
  93. package/src/components/alert/alert.scss +1 -0
  94. package/src/components/animated-button/README.md +306 -0
  95. package/src/components/animated-button/animated-button.component.ts +229 -0
  96. package/src/components/animated-button/animated-button.scss +228 -0
  97. package/src/components/animated-button/animated-button.test.ts +143 -0
  98. package/src/components/animated-button/index.ts +12 -0
  99. package/src/components/button/button.component.ts +5 -0
  100. package/src/components/button-group/button-group.component.ts +25 -13
  101. package/src/components/button-group/button-group.scss +6 -0
  102. package/src/components/checkbox/checkbox.component.ts +4 -0
  103. package/src/components/checkbox/checkbox.scss +7 -4
  104. package/src/components/checkbox-group/checkbox-group.scss +7 -0
  105. package/src/components/chip/chip.component.ts +7 -2
  106. package/src/components/chip/chip.scss +16 -2
  107. package/src/components/collapsible/collapsible.component.ts +27 -20
  108. package/src/components/cols/cols.component.ts +14 -2
  109. package/src/components/cols/cols.scss +6 -12
  110. package/src/components/confirm/confirm.component.ts +7 -0
  111. package/src/components/copy-button/copy-button.component.ts +12 -11
  112. package/src/components/data-select/data-select.component.ts +215 -44
  113. package/src/components/data-select/data-select.scss +66 -6
  114. package/src/components/data-select/providers/currency-data-provider.ts +20 -6
  115. package/src/components/data-select/providers/provider.ts +1 -0
  116. package/src/components/data-select/providers/us-state-data-provider.ts +68 -0
  117. package/src/components/data-table/data-table.component.ts +81 -22
  118. package/src/components/data-table-search/data-table-search.component.ts +184 -0
  119. package/src/components/data-table-search/data-table-search.scss +17 -0
  120. package/src/components/data-table-search/data-table-search.test.ts +11 -0
  121. package/src/components/data-table-search/index.ts +12 -0
  122. package/src/components/datepicker/datepicker.component.ts +347 -13
  123. package/src/components/dialog/dialog.component.ts +1 -1
  124. package/src/components/empty-state/empty-state.scss +6 -1
  125. package/src/components/header/header.scss +3 -3
  126. package/src/components/icon/icon.component.ts +23 -1
  127. package/src/components/icon/icon.scss +23 -0
  128. package/src/components/icon-picker/brand-icons.ts +24 -0
  129. package/src/components/icon-picker/icon-picker.component.ts +407 -0
  130. package/src/components/icon-picker/icon-picker.scss +205 -0
  131. package/src/components/icon-picker/index.ts +12 -0
  132. package/src/components/icon-picker/line-icons.ts +612 -0
  133. package/src/components/icon-picker/material-icons.ts +14543 -0
  134. package/src/components/inline-edit/inline-edit.component.ts +190 -46
  135. package/src/components/inline-edit/inline-edit.scss +29 -0
  136. package/src/components/inline-edit/inline-edit.test.ts +314 -1
  137. package/src/components/input/input.component.ts +324 -5
  138. package/src/components/input/input.scss +87 -0
  139. package/src/components/input-group/input-group.component.ts +28 -7
  140. package/src/components/input-group/input-group.scss +48 -10
  141. package/src/components/item/item.component.ts +24 -3
  142. package/src/components/item/item.scss +18 -0
  143. package/src/components/key/index.ts +12 -0
  144. package/src/components/key/key.component.ts +47 -0
  145. package/src/components/key/key.scss +38 -0
  146. package/src/components/key-container/index.ts +12 -0
  147. package/src/components/key-container/key-container.component.ts +142 -0
  148. package/src/components/key-container/key-container.scss +33 -0
  149. package/src/components/menu-item/menu-item.component.ts +20 -0
  150. package/src/components/menu-item/menu-item.scss +69 -2
  151. package/src/components/navbar/navbar.component.ts +103 -11
  152. package/src/components/navbar/navbar.scss +6 -1
  153. package/src/components/opt-group/index.ts +12 -0
  154. package/src/components/opt-group/opt-group.component.ts +74 -0
  155. package/src/components/opt-group/opt-group.scss +37 -0
  156. package/src/components/opt-group/opt-group.test.ts +27 -0
  157. package/src/components/option/option.component.ts +1 -16
  158. package/src/components/option/option.scss +4 -0
  159. package/src/components/panel/panel.component.ts +10 -8
  160. package/src/components/panel/panel.scss +20 -0
  161. package/src/components/priority-list/index.ts +12 -0
  162. package/src/components/priority-list/priority-list.component.ts +611 -0
  163. package/src/components/priority-list/priority-list.scss +185 -0
  164. package/src/components/priority-list/priority-list.test.ts +139 -0
  165. package/src/components/progress-bar/progress-bar.component.ts +22 -16
  166. package/src/components/radio/radio.scss +10 -3
  167. package/src/components/select/select.component.ts +812 -72
  168. package/src/components/select/select.scss +108 -2
  169. package/src/components/select/select.test.ts +147 -2
  170. package/src/components/settings-container/settings-container.component.ts +44 -19
  171. package/src/components/settings-container/settings-container.scss +1 -1
  172. package/src/components/skeleton/skeleton.component.ts +6 -1
  173. package/src/components/slideout/slideout.scss +8 -3
  174. package/src/components/sp/sp.component.ts +3 -1
  175. package/src/components/sp/sp.scss +19 -0
  176. package/src/components/stepper/stepper.component.ts +16 -10
  177. package/src/components/textarea/textarea.scss +0 -4
  178. package/src/components/tile/tile.component.ts +12 -3
  179. package/src/components/tile/tile.scss +1 -1
  180. package/src/components/timer/timer.component.ts +15 -2
  181. package/src/components/toggle/toggle.component.ts +29 -26
  182. package/src/components/toggle/toggle.scss +13 -1
  183. package/src/components/translation-group/index.ts +12 -0
  184. package/src/components/translation-group/translation-group.component.ts +207 -0
  185. package/src/components/translation-group/translation-group.scss +8 -0
  186. package/src/components/translations/translations.component.ts +90 -44
  187. package/src/components/translations/translations.scss +4 -0
  188. package/src/components/vertical-stepper/vertical-stepper.component.ts +14 -9
  189. package/src/events/events.ts +12 -7
  190. package/src/events/zn-language-change.ts +7 -0
  191. package/src/events/zn-purchase.ts +11 -0
  192. package/src/events/zn-redirect.ts +7 -0
  193. package/src/events/zn-reorder.ts +7 -0
  194. package/src/events/zn-search-change.ts +11 -0
  195. package/src/events/zn-submit.ts +1 -1
  196. package/src/form-control.scss +4 -0
  197. package/src/internal/form-navigation.ts +510 -0
  198. package/src/internal/form.ts +6 -0
  199. package/src/zinc.ts +91 -83
@@ -0,0 +1,228 @@
1
+ @use "../../../scss/shared";
2
+
3
+ :host {
4
+ display: inline-block;
5
+ cursor: pointer;
6
+ }
7
+
8
+ :host([disabled]) {
9
+ cursor: wait;
10
+ }
11
+
12
+ .animated-button {
13
+ @include shared.button;
14
+ position: relative;
15
+ overflow: hidden;
16
+ min-width: 150px;
17
+ min-height: 40px;
18
+ gap: 8px;
19
+ transition:
20
+ transform var(--zn-transition-fast) ease,
21
+ opacity var(--zn-transition-fast) ease;
22
+ border: none;
23
+ font-size: 1rem;
24
+ font-family: inherit;
25
+ cursor: pointer;
26
+
27
+ &:hover:not(:disabled) {
28
+ opacity: 0.9;
29
+ }
30
+
31
+ &:active:not(:disabled) {
32
+ transform: scale(0.98);
33
+ }
34
+
35
+ &:disabled {
36
+ cursor: wait;
37
+ }
38
+
39
+ &:focus-visible {
40
+ outline: 2px solid rgb(var(--zn-primary));
41
+ outline-offset: 2px;
42
+ }
43
+ }
44
+
45
+ .animated-button__content {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ gap: 8px;
50
+ position: relative;
51
+ z-index: 1;
52
+ }
53
+
54
+ .animated-button__text {
55
+ font-weight: 600;
56
+ line-height: 1;
57
+ }
58
+
59
+ .animated-button__icon {
60
+ display: inline-flex;
61
+ flex-shrink: 0;
62
+ }
63
+
64
+ .animated-button__spinner {
65
+ display: inline-flex;
66
+ flex-shrink: 0;
67
+ animation: spin 1s linear infinite;
68
+ }
69
+
70
+ @keyframes spin {
71
+ from {
72
+ transform: rotate(0deg);
73
+ }
74
+ to {
75
+ transform: rotate(360deg);
76
+ }
77
+ }
78
+
79
+ // Shimmer animation for processing state
80
+ .animated-button__shimmer {
81
+ position: absolute;
82
+ top: 0;
83
+ left: -100%;
84
+ width: 100%;
85
+ height: 100%;
86
+ background: linear-gradient(
87
+ 90deg,
88
+ rgba(255, 255, 255, 0) 0%,
89
+ rgba(255, 255, 255, 0.3) 50%,
90
+ rgba(255, 255, 255, 0) 100%
91
+ );
92
+ animation: shimmer 1.5s infinite;
93
+ }
94
+
95
+ @keyframes shimmer {
96
+ 0% {
97
+ left: -100%;
98
+ }
99
+ 100% {
100
+ left: 100%;
101
+ }
102
+ }
103
+
104
+ // State: Idle (default blue)
105
+ .animated-button--idle {
106
+ background-color: rgb(var(--zn-primary)) !important;
107
+ }
108
+
109
+ // State: Processing (blue with shimmer)
110
+ .animated-button--processing {
111
+ background-color: rgb(var(--zn-primary)) !important;
112
+ cursor: wait;
113
+ }
114
+
115
+ // State: Success (green with fill animation)
116
+ .animated-button--success {
117
+ background-color: rgb(var(--zn-primary)) !important;
118
+ cursor: default;
119
+
120
+ &::before {
121
+ content: '';
122
+ position: absolute;
123
+ top: 0;
124
+ left: 0;
125
+ width: 100%;
126
+ height: 100%;
127
+ background-color: rgb(var(--zn-color-success));
128
+ animation: successFill var(--zn-transition-slow) ease-out forwards;
129
+ z-index: 0;
130
+ }
131
+
132
+ .animated-button__content {
133
+ z-index: 1;
134
+ }
135
+
136
+ .animated-button__icon--success {
137
+ opacity: 0;
138
+ animation: iconFadeIn 0.3s ease-out 0.4s forwards;
139
+ }
140
+
141
+ .animated-button__tick {
142
+ stroke-dasharray: 30;
143
+ stroke-dashoffset: 30;
144
+ animation: tickSwoosh 0.5s ease-out 0.4s forwards;
145
+ }
146
+ }
147
+
148
+ @keyframes successFill {
149
+ 0% {
150
+ transform: translateY(100%);
151
+ }
152
+ 100% {
153
+ transform: translateY(0);
154
+ }
155
+ }
156
+
157
+ @keyframes iconFadeIn {
158
+ to {
159
+ opacity: 1;
160
+ }
161
+ }
162
+
163
+ @keyframes tickSwoosh {
164
+ 0% {
165
+ stroke-dashoffset: 30;
166
+ }
167
+ 100% {
168
+ stroke-dashoffset: 0;
169
+ }
170
+ }
171
+
172
+ // State: Failure (red)
173
+ .animated-button--failure {
174
+ background-color: rgb(var(--zn-color-error)) !important;
175
+ cursor: not-allowed;
176
+
177
+ .animated-button__icon--failure {
178
+ animation: failureShake var(--zn-transition-medium) ease-out;
179
+ }
180
+ }
181
+
182
+ @keyframes failureShake {
183
+ 0%,
184
+ 100% {
185
+ transform: translateX(0);
186
+ }
187
+ 25% {
188
+ transform: translateX(-4px);
189
+ }
190
+ 75% {
191
+ transform: translateX(4px);
192
+ }
193
+ }
194
+
195
+ // Respect user's motion preferences
196
+ @media (prefers-reduced-motion: reduce) {
197
+ .animated-button {
198
+ transition: none;
199
+ }
200
+
201
+ .animated-button--success::before {
202
+ animation: none;
203
+ transform: translateY(0);
204
+ }
205
+
206
+ .animated-button__shimmer {
207
+ animation: none;
208
+ display: none;
209
+ }
210
+
211
+ .animated-button__spinner {
212
+ animation: none;
213
+ }
214
+
215
+ .animated-button__icon--success {
216
+ animation: none;
217
+ opacity: 1;
218
+ }
219
+
220
+ .animated-button__tick {
221
+ animation: none;
222
+ stroke-dashoffset: 0;
223
+ }
224
+
225
+ .animated-button__icon--failure {
226
+ animation: none;
227
+ }
228
+ }
@@ -0,0 +1,143 @@
1
+ import '../../../dist/zn.min.js';
2
+ import { expect, fixture, html, waitUntil } from '@open-wc/testing';
3
+ import type ZnAnimatedButton from './animated-button.component';
4
+
5
+ describe('<zn-animated-button>', () => {
6
+ it('should be accessible', async () => {
7
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
8
+ await expect(el).to.be.accessible();
9
+ });
10
+
11
+ it('should render with default idle text', async () => {
12
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
13
+ const button = el.shadowRoot?.querySelector('button');
14
+ expect(button?.textContent?.trim()).to.equal('Purchase');
15
+ });
16
+
17
+ it('should render with custom idle text', async () => {
18
+ const el = await fixture<ZnAnimatedButton>(
19
+ html`<zn-animated-button idle-text="Buy Now"></zn-animated-button>`
20
+ );
21
+ const button = el.shadowRoot?.querySelector('button');
22
+ expect(button?.textContent?.trim()).to.equal('Buy Now');
23
+ });
24
+
25
+ it('should be disabled when processing', async () => {
26
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
27
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
28
+
29
+ expect(button.disabled).to.be.false;
30
+
31
+ button.click();
32
+ await el.updateComplete;
33
+
34
+ expect(button.disabled).to.be.true;
35
+ });
36
+
37
+ it('should emit zn-purchase event when clicked', async () => {
38
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
39
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
40
+
41
+ let eventFired = false;
42
+ el.addEventListener('zn-purchase', () => {
43
+ eventFired = true;
44
+ });
45
+
46
+ button.click();
47
+ await el.updateComplete;
48
+
49
+ expect(eventFired).to.be.true;
50
+ });
51
+
52
+ it('should transition to success state when setSuccess is called', async () => {
53
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
54
+
55
+ el.setSuccess();
56
+ await el.updateComplete;
57
+
58
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
59
+ expect(button.classList.contains('purchase-button--success')).to.be.true;
60
+ });
61
+
62
+ it('should transition to failure state when setFailure is called', async () => {
63
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
64
+
65
+ el.setFailure('Payment failed');
66
+ await el.updateComplete;
67
+
68
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
69
+ expect(button.classList.contains('purchase-button--failure')).to.be.true;
70
+ expect(button.textContent).to.include('Payment failed');
71
+ });
72
+
73
+ it('should reset to idle state after failure timeout', async () => {
74
+ const el = await fixture<ZnAnimatedButton>(
75
+ html`<zn-animated-button failure-reset-delay="100"></zn-animated-button>`
76
+ );
77
+
78
+ el.setFailure('Error');
79
+ await el.updateComplete;
80
+
81
+ let button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
82
+ expect(button.classList.contains('purchase-button--failure')).to.be.true;
83
+
84
+ await waitUntil(
85
+ () => {
86
+ button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
87
+ return button.classList.contains('purchase-button--idle');
88
+ },
89
+ '',
90
+ { timeout: 500 }
91
+ );
92
+
93
+ expect(button.classList.contains('purchase-button--idle')).to.be.true;
94
+ });
95
+
96
+ it('should show spinner icon in processing state', async () => {
97
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
98
+
99
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
100
+ button.click();
101
+ await el.updateComplete;
102
+
103
+ const spinner = el.shadowRoot?.querySelector('.purchase-button__spinner');
104
+ expect(spinner).to.exist;
105
+ });
106
+
107
+ it('should show success icon in success state', async () => {
108
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
109
+
110
+ el.setSuccess();
111
+ await el.updateComplete;
112
+
113
+ const successIcon = el.shadowRoot?.querySelector('.purchase-button__icon--success');
114
+ expect(successIcon).to.exist;
115
+ });
116
+
117
+ it('should show failure icon in failure state', async () => {
118
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button></zn-animated-button>`);
119
+
120
+ el.setFailure();
121
+ await el.updateComplete;
122
+
123
+ const failureIcon = el.shadowRoot?.querySelector('.purchase-button__icon--failure');
124
+ expect(failureIcon).to.exist;
125
+ });
126
+
127
+ it('should respect disabled attribute', async () => {
128
+ const el = await fixture<ZnAnimatedButton>(html`<zn-animated-button disabled></zn-animated-button>`);
129
+
130
+ const button = el.shadowRoot?.querySelector('button') as HTMLButtonElement;
131
+ expect(button.disabled).to.be.true;
132
+
133
+ let eventFired = false;
134
+ el.addEventListener('zn-purchase', () => {
135
+ eventFired = true;
136
+ });
137
+
138
+ button.click();
139
+ await el.updateComplete;
140
+
141
+ expect(eventFired).to.be.false;
142
+ });
143
+ });
@@ -0,0 +1,12 @@
1
+ import ZnAnimatedButton from './animated-button.component';
2
+
3
+ export * from './animated-button.component';
4
+ export default ZnAnimatedButton;
5
+
6
+ ZnAnimatedButton.define('zn-animated-button');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-animated-button': ZnAnimatedButton;
11
+ }
12
+ }
@@ -206,6 +206,11 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
206
206
  }
207
207
  }
208
208
 
209
+ // Override native HTMLButtonElement click method
210
+ click() {
211
+ this.handleClick()
212
+ }
213
+
209
214
  handleClick = () => {
210
215
  if (this.disabled) {
211
216
  return;
@@ -38,22 +38,34 @@ export default class ZnButtonGroup extends ZincElement {
38
38
  return;
39
39
  }
40
40
 
41
- let slottedElements = [...this.defaultSlot.assignedElements({flatten: true})] as HTMLElement[];
41
+ const allSlotted = [...this.defaultSlot.assignedElements({flatten: true})] as HTMLElement[];
42
42
 
43
- slottedElements = slottedElements.filter(el => el.tagName === 'ZN-BUTTON');
44
- slottedElements.forEach(el => {
45
- const index = slottedElements.indexOf(el);
46
- const button = el.closest('zn-button') ?? el.querySelector('zn-button');
43
+ // Collect buttons from direct zn-button elements and from zn-dropdown wrappers
44
+ const groupItems: {button: HTMLElement; wrapper?: HTMLElement}[] = [];
45
+ for (const el of allSlotted) {
46
+ if (el.tagName === 'ZN-BUTTON') {
47
+ groupItems.push({button: el});
48
+ } else if (el.tagName === 'ZN-DROPDOWN') {
49
+ const innerButton = el.querySelector('zn-button');
50
+ if (innerButton) {
51
+ groupItems.push({button: innerButton, wrapper: el});
52
+ }
53
+ }
54
+ }
47
55
 
48
- if (button) {
49
- button.toggleAttribute('data-zn-button-group__button--grow', this.grow);
56
+ groupItems.forEach(({button, wrapper}, index) => {
57
+ button.toggleAttribute('data-zn-button-group__button--grow', this.grow);
50
58
 
51
- if (!this.gap) {
52
- button.toggleAttribute('data-zn-button-group__button', true);
53
- button.toggleAttribute('data-zn-button-group__button--first', index === 0);
54
- button.toggleAttribute('data-zn-button-group__button--inner', index > 0 && index < slottedElements.length - 1);
55
- button.toggleAttribute('data-zn-button-group__button--last', index === slottedElements.length - 1);
56
- }
59
+ if (!this.gap) {
60
+ button.toggleAttribute('data-zn-button-group__button', true);
61
+ button.toggleAttribute('data-zn-button-group__button--first', index === 0);
62
+ button.toggleAttribute('data-zn-button-group__button--inner', index > 0 && index < groupItems.length - 1);
63
+ button.toggleAttribute('data-zn-button-group__button--last', index === groupItems.length - 1);
64
+ }
65
+
66
+ // Mark the dropdown wrapper so it can be styled to not break the group layout
67
+ if (wrapper) {
68
+ wrapper.toggleAttribute('data-zn-button-group__dropdown', true);
57
69
  }
58
70
  });
59
71
  }
@@ -23,6 +23,12 @@
23
23
  }
24
24
  }
25
25
 
26
+ // Ensure dropdown wrappers inside button-group participate in the flex layout
27
+ // without introducing gaps. The data attribute is set by the component logic.
28
+ ::slotted([data-zn-button-group__dropdown]) {
29
+ display: inline-flex !important;
30
+ }
31
+
26
32
  :host([direction="vertical"]),
27
33
  zn-button-group[direction="vertical"] {
28
34
  width: 100%;
@@ -88,6 +88,9 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
88
88
  /** Draws a container around the checkbox. */
89
89
  @property({type: Boolean, reflect: true}) contained = false;
90
90
 
91
+ /** Removes a container around the checkbox. */
92
+ @property({type: Boolean, reflect: true}) borderless = false;
93
+
91
94
  /** Applies styles relevant to checkboxes in a horizontal layout. */
92
95
  @property({type: Boolean, reflect: true}) horizontal = false;
93
96
 
@@ -292,6 +295,7 @@ export default class ZnCheckbox extends ZincElement implements ZincFormControl {
292
295
  'checkbox--focused': this.hasFocus,
293
296
  'checkbox--indeterminate': this.indeterminate,
294
297
  'checkbox--contained': this.contained,
298
+ 'checkbox--no-border': this.borderless,
295
299
  'checkbox--horizontal': this.horizontal,
296
300
  'checkbox--small': this.size === 'small',
297
301
  'checkbox--medium': this.size === 'medium',
@@ -142,7 +142,6 @@
142
142
  }
143
143
 
144
144
  .svg-icon {
145
- width: max(16px, 70%);
146
145
  height: max(16px, 70%);
147
146
  --icon-color: var(--checkbox-icon-color);
148
147
 
@@ -251,6 +250,12 @@
251
250
  -webkit-user-select: none;
252
251
  }
253
252
 
253
+ .checkbox--no-border {
254
+ border: none !important;
255
+ outline: none !important;
256
+ border-radius: 0 !important;
257
+ }
258
+
254
259
  /* Contained */
255
260
  .checkbox--contained {
256
261
  padding: 1.375rem var(--zn-spacing-large) 1.375rem var(--zn-spacing-medium);
@@ -291,9 +296,7 @@
291
296
  .checkbox__control {
292
297
  height: 100%;
293
298
  width: 100%;
294
- border-left: unset;
295
- border-top: unset;
296
- border-bottom: unset;
299
+ border: unset;
297
300
  box-shadow: unset;
298
301
  background-color: var(--zn-color-purple-50);
299
302
  }
@@ -9,6 +9,12 @@
9
9
  :host([horizontal]) .form-control-input {
10
10
  display: flex;
11
11
  column-gap: var(--zn-spacing-2x-large);
12
+ flex-wrap: wrap;
13
+ }
14
+
15
+ ::slotted(zn-checkbox[horizontal]) {
16
+ flex-basis: 20%;
17
+ min-width: 100px;
12
18
  }
13
19
 
14
20
  :host([contained]) .form-control-input {
@@ -36,6 +42,7 @@
36
42
  height: 100%;
37
43
  }
38
44
 
45
+
39
46
  .form-control {
40
47
  position: relative;
41
48
  border: none;
@@ -27,6 +27,9 @@ export default class ZnChip extends ZincElement {
27
27
  static styles: CSSResultGroup = unsafeCSS(styles);
28
28
 
29
29
  @property() icon: string = '';
30
+ @property() caption: string = '';
31
+
32
+ @property({attribute: 'icon-size', type: Number}) iconSize: number = 18;
30
33
 
31
34
  @property() type: 'info' | 'success' | 'warning' | 'error' | 'primary' |
32
35
  'transparent' | 'custom' | 'neutral' = 'neutral';
@@ -63,9 +66,11 @@ export default class ZnChip extends ZincElement {
63
66
  'chip--large': this.size === 'large',
64
67
  })}">
65
68
  ${this.icon ? html`
66
- <zn-icon src="${this.icon}" size="18"></zn-icon>` : ''}
69
+ <zn-icon src="${this.icon}" size="${this.iconSize}"></zn-icon>` : ''}
70
+ ${this.caption ? html`
71
+ <span class="chip__caption">${this.caption}</span>` : ''}
67
72
  ${hasContent ? html`
68
- <slot></slot>` : ''}
73
+ <span class="chip__value"><slot></slot></span>` : ''}
69
74
  ${this.hasSlotController.test('action') ? html`
70
75
  <slot name="action" class="chip__action"></slot>` : ''}
71
76
  </div>
@@ -2,6 +2,7 @@
2
2
 
3
3
  :host {
4
4
  display: inline-block;
5
+ vertical-align: middle;
5
6
  }
6
7
 
7
8
  .chip {
@@ -13,11 +14,11 @@
13
14
  gap: 4px;
14
15
  width: fit-content;
15
16
  margin: 1px;
16
- text-transform: capitalize;
17
17
  justify-content: center;
18
18
  align-items: center;
19
19
  border: 1px solid transparent;
20
20
  line-height: 24px;
21
+ white-space: nowrap;
21
22
 
22
23
  &--flush {
23
24
  padding: 0;
@@ -80,8 +81,21 @@
80
81
  background-color: hsl(from var(--chip-color-override, inherit) h s l / 0.1);
81
82
  }
82
83
 
84
+ .chip__value {
85
+ overflow: hidden;
86
+ text-overflow: ellipsis;
87
+ }
88
+
89
+ .chip__caption {
90
+ opacity: 0.7;
91
+ font-weight: 600;
92
+
93
+ &::after {
94
+ content: ":";
95
+ }
96
+ }
97
+
83
98
  .chip__action {
84
- text-transform: unset;
85
99
  }
86
100
 
87
101
  .chip--small {
@@ -1,13 +1,13 @@
1
- import { classMap } from "lit/directives/class-map.js";
2
- import { type CSSResultGroup, html, unsafeCSS } from 'lit';
3
- import { HasSlotController } from "../../internal/slot";
4
- import { property, state } from 'lit/decorators.js';
5
- import { Store } from "../../internal/storage";
1
+ import {classMap} from "lit/directives/class-map.js";
2
+ import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
+ import {HasSlotController} from "../../internal/slot";
4
+ import {property, state} from 'lit/decorators.js';
5
+ import {Store} from "../../internal/storage";
6
6
  import ZincElement from '../../internal/zinc-element';
7
+ import type {ZnInputEvent} from "../../events/zn-input";
8
+ import type ZnToggle from "../toggle";
7
9
 
8
10
  import styles from './collapsible.scss';
9
- import { ZnInputEvent } from "../../events/zn-input";
10
- import ZnToggle from "../toggle";
11
11
 
12
12
  /**
13
13
  * @summary Toggles between showing and hiding content when clicked
@@ -23,28 +23,28 @@ import ZnToggle from "../toggle";
23
23
  export default class ZnCollapsible extends ZincElement {
24
24
  static styles: CSSResultGroup = unsafeCSS(styles);
25
25
 
26
- @property({ reflect: true }) caption = '';
26
+ @property({reflect: true}) caption = '';
27
27
 
28
- @property({ reflect: true }) description: string;
28
+ @property({reflect: true}) description: string;
29
29
 
30
- @property({ reflect: true }) label = '';
30
+ @property({reflect: true}) label = '';
31
31
 
32
- @property({ type: Boolean, attribute: 'show-number', reflect: true }) showNumber: boolean = false;
32
+ @property({type: Boolean, attribute: 'show-number', reflect: true}) showNumber: boolean = false;
33
33
 
34
34
  // what element name to count
35
- @property({ type: String, attribute: 'count-element' }) countElement: string = '*';
35
+ @property({type: String, attribute: 'count-element'}) countElement: string = '*';
36
36
 
37
- @property({ type: Boolean, reflect: true }) expanded: boolean = false;
37
+ @property({type: Boolean, reflect: true}) expanded: boolean = false;
38
38
 
39
- @property({ attribute: 'default' }) defaultState: 'open' | 'closed';
39
+ @property({attribute: 'default'}) defaultState: 'open' | 'closed';
40
40
 
41
- @property({ attribute: 'local-storage', type: Boolean, reflect: true }) localStorage: boolean;
41
+ @property({attribute: 'local-storage', type: Boolean, reflect: true}) localStorage: boolean;
42
42
 
43
- @property({ attribute: 'store-key', reflect: true }) storeKey: string = "";
43
+ @property({attribute: 'store-key', reflect: true}) storeKey: string = "";
44
44
 
45
- @property({ attribute: 'store-ttl', type: Number, reflect: true }) storeTtl = 0;
45
+ @property({attribute: 'store-ttl', type: Number, reflect: true}) storeTtl = 0;
46
46
 
47
- @property({ attribute: 'flush', type: Boolean, reflect: true }) flush: boolean = false;
47
+ @property({attribute: 'flush', type: Boolean, reflect: true}) flush: boolean = false;
48
48
 
49
49
  @state() numberOfItems: number = 0;
50
50
 
@@ -74,7 +74,7 @@ export default class ZnCollapsible extends ZincElement {
74
74
  this.observer = new MutationObserver(() => {
75
75
  this.recalculateNumberOfItems();
76
76
  });
77
- this.observer.observe(slot, { childList: true, subtree: true });
77
+ this.observer.observe(slot, {childList: true, subtree: true});
78
78
  }
79
79
 
80
80
  await this.updateComplete;
@@ -99,6 +99,13 @@ export default class ZnCollapsible extends ZincElement {
99
99
  }
100
100
  }
101
101
 
102
+ protected updated(changedProperties: PropertyValues) {
103
+ super.updated(changedProperties);
104
+ if (changedProperties.has('expanded') && this.storeKey) {
105
+ this._store.set(this.storeKey, this.expanded.toString());
106
+ }
107
+ }
108
+
102
109
  disconnectedCallback() {
103
110
  super.disconnectedCallback();
104
111
  if (this.observer) {
@@ -150,7 +157,7 @@ export default class ZnCollapsible extends ZincElement {
150
157
  <slot name="label"><p class="label">${this.label}</p></slot>
151
158
  ${this.showNumber && !this.expanded ? html`
152
159
  <zn-chip size="small" type="primary">${this.numberOfItems}</zn-chip>` : ''}
153
- ${this.showArrow ? html`
160
+ ${this.showArrow ? html`
154
161
  <zn-icon library="material-outlined" src="expand_more" class="expand"></zn-icon>` : ''}
155
162
  </div>
156
163
  </slot>