@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
@@ -0,0 +1,407 @@
1
+ import {type CSSResultGroup, html, nothing, unsafeCSS} from 'lit';
2
+ import {classMap} from 'lit/directives/class-map.js';
3
+ import {defaultValue} from '../../internal/default-value';
4
+ import {property, query, state} from 'lit/decorators.js';
5
+ import {FormControlController} from '../../internal/form';
6
+ import ZincElement from '../../internal/zinc-element';
7
+ import ZnButton from '../button';
8
+ import ZnDialog from '../dialog';
9
+ import ZnIcon from '../icon';
10
+ import ZnInput from '../input';
11
+ import ZnOption from '../option';
12
+ import ZnSelect from '../select';
13
+ import type {ZincFormControl} from '../../internal/zinc-element';
14
+
15
+ import {brandIcons} from './brand-icons';
16
+ import {lineIcons} from './line-icons';
17
+ import {
18
+ materialIcons,
19
+ material_outlinedIcons,
20
+ material_roundIcons,
21
+ material_sharpIcons,
22
+ material_two_toneIcons,
23
+ material_symbols_outlinedIcons,
24
+ } from './material-icons';
25
+ import styles from './icon-picker.scss';
26
+
27
+ export default class ZnIconPicker extends ZincElement implements ZincFormControl {
28
+ static styles: CSSResultGroup = unsafeCSS(styles);
29
+ static dependencies = {
30
+ 'zn-icon': ZnIcon,
31
+ 'zn-button': ZnButton,
32
+ 'zn-dialog': ZnDialog,
33
+ 'zn-input': ZnInput,
34
+ 'zn-select': ZnSelect,
35
+ 'zn-option': ZnOption
36
+ };
37
+
38
+ private readonly formControlController = new FormControlController(this, {
39
+ assumeInteractionOn: ['zn-change']
40
+ });
41
+
42
+ @property() name = '';
43
+ @property() icon = '';
44
+ @property() label = '';
45
+ @property() library: string = 'material';
46
+ @property() color: string = '';
47
+ @property({type: Boolean, attribute: 'no-color'}) noColor: boolean = false;
48
+ @property({type: Boolean, attribute: 'no-library'}) noLibrary: boolean = false;
49
+ @property({attribute: 'help-text'}) helpText: string = '';
50
+ @property({type: Boolean, reflect: true}) disabled = false;
51
+ @property({type: Boolean, reflect: true}) required = false;
52
+ @property({reflect: true}) form: string;
53
+
54
+ @defaultValue() defaultValue = '';
55
+
56
+ @state() private _dialogOpen = false;
57
+ @state() private _searchQuery = '';
58
+ @state() private _iconList: string[] = [];
59
+ @state() private _filteredIcons: string[] = [];
60
+
61
+ // Pending selections (not committed until confirm)
62
+ @state() private _pendingIcon = '';
63
+ @state() private _pendingLibrary = '';
64
+ @state() private _pendingColor = '';
65
+
66
+ @query('zn-dialog') private _dialog: ZnDialog;
67
+
68
+ get value(): string {
69
+ return this.icon;
70
+ }
71
+
72
+ set value(val: string) {
73
+ this.icon = val;
74
+ }
75
+
76
+ get validity(): ValidityState {
77
+ if (this.required && !this.icon) {
78
+ return {
79
+ valid: false,
80
+ valueMissing: true,
81
+ badInput: false, customError: false, patternMismatch: false,
82
+ rangeOverflow: false, rangeUnderflow: false, stepMismatch: false,
83
+ tooLong: false, tooShort: false, typeMismatch: false,
84
+ } as ValidityState;
85
+ }
86
+ return {
87
+ valid: true, valueMissing: false, badInput: false, customError: false,
88
+ patternMismatch: false, rangeOverflow: false, rangeUnderflow: false,
89
+ stepMismatch: false, tooLong: false, tooShort: false, typeMismatch: false,
90
+ } as ValidityState;
91
+ }
92
+
93
+ get validationMessage(): string {
94
+ return this.required && !this.icon ? 'Please select an icon.' : '';
95
+ }
96
+
97
+ checkValidity(): boolean { return this.validity.valid; }
98
+ getForm(): HTMLFormElement | null { return this.formControlController.getForm(); }
99
+ reportValidity(): boolean { return this.checkValidity(); }
100
+ setCustomValidity(_message: string) { this.formControlController.updateValidity(); }
101
+
102
+ private static readonly freeInputLibraries = new Set(['gravatar', 'libravatar', 'avatar']);
103
+
104
+ private isFreeInputLibrary(library: string): boolean {
105
+ return ZnIconPicker.freeInputLibraries.has(library);
106
+ }
107
+
108
+ private getIconsForLibrary(library: string): string[] {
109
+ switch (library) {
110
+ case 'material':
111
+ return materialIcons;
112
+ case 'material-outlined':
113
+ return material_outlinedIcons;
114
+ case 'material-round':
115
+ return material_roundIcons;
116
+ case 'material-sharp':
117
+ return material_sharpIcons;
118
+ case 'material-two-tone':
119
+ return material_two_toneIcons;
120
+ case 'material-symbols-outlined':
121
+ return material_symbols_outlinedIcons;
122
+ case 'brands':
123
+ return brandIcons;
124
+ case 'line':
125
+ return lineIcons;
126
+ default:
127
+ return materialIcons;
128
+ }
129
+ }
130
+
131
+ private async openDialog() {
132
+ this._pendingIcon = this.icon;
133
+ this._pendingLibrary = this.library;
134
+ this._pendingColor = this.color;
135
+ this._searchQuery = '';
136
+ this._iconList = this.getIconsForLibrary(this.library);
137
+ this._filteredIcons = this._iconList.slice(0, 200);
138
+ this._dialogOpen = true;
139
+
140
+ await this.updateComplete;
141
+ this._dialog.show();
142
+ }
143
+
144
+ private closeDialog() {
145
+ this._dialog.hide();
146
+ this._dialogOpen = false;
147
+ }
148
+
149
+ private handleConfirm() {
150
+ this.icon = this._pendingIcon;
151
+ this.library = this._pendingLibrary;
152
+ this.color = this._pendingColor;
153
+ this.closeDialog();
154
+ this.emit('zn-change');
155
+ }
156
+
157
+ private handleCancel() {
158
+ this.closeDialog();
159
+ }
160
+
161
+ private handleSearchInput(e: Event) {
162
+ const input = e.target as HTMLInputElement;
163
+ this._searchQuery = input.value.toLowerCase();
164
+ this.filterIcons();
165
+ }
166
+
167
+ private filterIcons() {
168
+ if (!this._searchQuery) {
169
+ this._filteredIcons = this._iconList.slice(0, 200);
170
+ } else {
171
+ this._filteredIcons = this._iconList
172
+ .filter(name => name.includes(this._searchQuery))
173
+ .slice(0, 200);
174
+ }
175
+ }
176
+
177
+ private handleIconSelect(iconName: string) {
178
+ this._pendingIcon = iconName;
179
+ }
180
+
181
+ private handleLibraryChange(e: Event) {
182
+ const select = e.target as HTMLSelectElement;
183
+ const wasFreeInput = this.isFreeInputLibrary(this._pendingLibrary);
184
+ this._pendingLibrary = select.value;
185
+ const isFreeInput = this.isFreeInputLibrary(this._pendingLibrary);
186
+
187
+ // Clear pending icon when switching between grid and free-input modes
188
+ if (wasFreeInput !== isFreeInput) {
189
+ this._pendingIcon = '';
190
+ }
191
+
192
+ if (!isFreeInput) {
193
+ this._iconList = this.getIconsForLibrary(this._pendingLibrary);
194
+ this.filterIcons();
195
+ }
196
+ }
197
+
198
+ private handleColorInput(e: Event) {
199
+ const input = e.target as ZnInput;
200
+ this._pendingColor = input.value;
201
+ }
202
+
203
+ private handleFreeInput(e: Event) {
204
+ const input = e.target as HTMLInputElement;
205
+ this._pendingIcon = input.value;
206
+ }
207
+
208
+ private handleClear(e: Event) {
209
+ e.stopPropagation();
210
+ this.icon = '';
211
+ this.color = '';
212
+ this.emit('zn-change');
213
+ }
214
+
215
+ private _handleTriggerClick() {
216
+ if (this.disabled) return;
217
+ this.openDialog();
218
+ }
219
+
220
+ private _handleTriggerKeyDown(event: KeyboardEvent) {
221
+ if (this.disabled) {
222
+ return;
223
+ }
224
+ if (event.key === 'Enter' || event.key === ' ') {
225
+ event.preventDefault();
226
+ this._handleTriggerClick();
227
+ }
228
+ }
229
+
230
+ render() {
231
+ const hasLabel = !!this.label;
232
+ const hasHelpText = !!this.helpText;
233
+ const hasIcon = !!this.icon;
234
+
235
+ return html`
236
+ <div part="form-control"
237
+ class="form-control ${hasLabel ? 'form-control--has-label' : ''} ${hasHelpText ? 'form-control--has-help-text' : ''}">
238
+
239
+ <label part="form-control-label" class="form-control__label"
240
+ aria-hidden=${hasLabel ? 'false' : 'true'}>
241
+ ${this.label}
242
+ </label>
243
+
244
+ <div part="form-control-input" class="form-control-input">
245
+ <div
246
+ part="trigger"
247
+ class="icon-picker__trigger ${this.disabled ? 'icon-picker__trigger--disabled' : ''}"
248
+ role="button"
249
+ tabindex=${this.disabled ? '-1' : '0'}
250
+ aria-disabled=${this.disabled ? 'true' : 'false'}
251
+ @click=${this._handleTriggerClick}
252
+ @keydown=${this._handleTriggerKeyDown}>
253
+ ${hasIcon
254
+ ? html`
255
+ <zn-icon
256
+ src=${this.icon}
257
+ library=${this.library}
258
+ color=${this.color || nothing}
259
+ size=${24}
260
+ ></zn-icon>
261
+ <span class="icon-picker__edit-text">Click to edit</span>
262
+ <zn-button
263
+ class="icon-picker__clear"
264
+ size="x-small"
265
+ color="transparent"
266
+ icon="close"
267
+ icon-size="16"
268
+ @click="${this.handleClear}"
269
+ ></zn-button>`
270
+ : html`
271
+ <span class="icon-picker__placeholder">Set an icon</span>`
272
+ }
273
+ </div>
274
+ </div>
275
+
276
+ ${this.name ? html`
277
+ <input type="hidden" name="${this.name}[icon]" .value=${this.icon}>
278
+ ${!this.noLibrary ? html`<input type="hidden" name="${this.name}[library]" .value=${this.library}>` : nothing}
279
+ ${!this.noColor ? html`<input type="hidden" name="${this.name}[color]" .value=${this.color}>` : nothing}
280
+ ` : nothing}
281
+
282
+ <div
283
+ part="form-control-help-text"
284
+ class="form-control__help-text"
285
+ aria-hidden=${hasHelpText ? 'false' : 'true'}>
286
+ ${this.helpText}
287
+ </div>
288
+
289
+ ${this._dialogOpen ? html`
290
+ <zn-dialog size="custom" label="Select Icon" @zn-close=${this.handleCancel}>
291
+ <div class="icon-picker__dialog-layout">
292
+ <div class="icon-picker__dialog-main">
293
+ <div class="icon-picker__dialog-controls">
294
+ ${!this.noLibrary ? html`
295
+ <zn-select
296
+ class="icon-picker__library-select"
297
+ value=${this._pendingLibrary}
298
+ size="small"
299
+ @zn-change=${this.handleLibraryChange}>
300
+ <zn-option value="material">Material</zn-option>
301
+ <zn-option value="material-outlined">Material Outlined</zn-option>
302
+ <zn-option value="material-round">Material Round</zn-option>
303
+ <zn-option value="material-sharp">Material Sharp</zn-option>
304
+ <zn-option value="material-two-tone">Material Two Tone</zn-option>
305
+ <zn-option value="material-symbols-outlined">Material Symbols Outlined</zn-option>
306
+ <zn-option value="brands">Brands</zn-option>
307
+ <zn-option value="line">Line</zn-option>
308
+ <zn-option value="gravatar">Gravatar</zn-option>
309
+ <zn-option value="libravatar">Libravatar</zn-option>
310
+ <zn-option value="avatar">Avatar</zn-option>
311
+ </zn-select>
312
+ ` : nothing}
313
+ ${!this.noColor ? html`
314
+ <zn-input
315
+ class="icon-picker__color-input"
316
+ type="color"
317
+ size="small"
318
+ value=${this._pendingColor}
319
+ @zn-input=${this.handleColorInput}
320
+ @zn-change=${this.handleColorInput}
321
+ ></zn-input>
322
+ ` : nothing}
323
+ </div>
324
+
325
+ ${this.isFreeInputLibrary(this._pendingLibrary) ? html`
326
+ <zn-input
327
+ class="icon-picker__free-input"
328
+ placeholder=${this._pendingLibrary === 'avatar' ? 'Enter a name...' : 'Enter an email address...'}
329
+ size="small"
330
+ value=${this._pendingIcon}
331
+ @zn-input=${this.handleFreeInput}>
332
+ <zn-icon slot="prefix" src=${this._pendingLibrary === 'avatar' ? 'person' : 'email'} size="18"></zn-icon>
333
+ </zn-input>
334
+ ` : html`
335
+ <zn-input
336
+ class="icon-picker__icon-input"
337
+ placeholder="Enter icon name..."
338
+ size="small"
339
+ value=${this._pendingIcon}
340
+ @zn-input=${this.handleFreeInput}>
341
+ <zn-icon slot="prefix" src="edit" size="18"></zn-icon>
342
+ </zn-input>
343
+
344
+ <zn-input
345
+ class="icon-picker__search"
346
+ placeholder="Search icons..."
347
+ size="small"
348
+ clearable
349
+ @zn-input=${this.handleSearchInput}>
350
+ <zn-icon slot="prefix" src="search" size="18"></zn-icon>
351
+ </zn-input>
352
+
353
+ <div class="icon-picker__grid">
354
+ ${this._filteredIcons.length === 0 ? html`
355
+ <div class="icon-picker__no-results">No icons found</div>
356
+ ` : this._filteredIcons.map(iconName => html`
357
+ <button
358
+ type="button"
359
+ class=${classMap({
360
+ 'icon-picker__grid-item': true,
361
+ 'icon-picker__grid-item--selected': iconName === this._pendingIcon
362
+ })}
363
+ title=${iconName}
364
+ @click=${() => this.handleIconSelect(iconName)}>
365
+ <zn-icon
366
+ src=${iconName}
367
+ library=${this._pendingLibrary}
368
+ color=${this._pendingColor || nothing}
369
+ size="36"
370
+ ></zn-icon>
371
+ </button>
372
+ `)}
373
+ </div>
374
+ `}
375
+ </div>
376
+
377
+ <div class="icon-picker__dialog-sidebar">
378
+ ${this._pendingIcon ? html`
379
+ <div class="icon-picker__preview">
380
+ <zn-icon
381
+ src=${this._pendingIcon}
382
+ library=${this._pendingLibrary}
383
+ color=${this._pendingColor || nothing}
384
+ size="64"
385
+ ></zn-icon>
386
+ <span class="icon-picker__preview-name">${this._pendingIcon}</span>
387
+ <span class="icon-picker__preview-library">${this._pendingLibrary}</span>
388
+ </div>
389
+ ` : html`
390
+ <div class="icon-picker__preview icon-picker__preview--empty">
391
+ <zn-icon src="touch_app" size="48"></zn-icon>
392
+ <span class="icon-picker__preview-hint">Select an icon</span>
393
+ </div>
394
+ `}
395
+ </div>
396
+ </div>
397
+
398
+ <div slot="footer">
399
+ <zn-button color="default" @click=${this.handleCancel}>Cancel</zn-button>
400
+ <zn-button color="primary" @click=${this.handleConfirm} ?disabled=${!this._pendingIcon}>Confirm</zn-button>
401
+ </div>
402
+ </zn-dialog>
403
+ ` : nothing}
404
+
405
+ </div>`;
406
+ }
407
+ }
@@ -0,0 +1,205 @@
1
+ @use "../../wc";
2
+ @use "../../form-control";
3
+
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .icon-picker__trigger {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: var(--zn-spacing-small);
12
+ padding: var(--zn-spacing-small) var(--zn-spacing-medium);
13
+ border: solid var(--zn-input-border-width) var(--zn-input-border-color);
14
+ border-radius: var(--zn-input-border-radius-medium);
15
+ background-color: var(--zn-input-background-color);
16
+ cursor: pointer;
17
+ transition: var(--zn-transition-fast) border-color,
18
+ var(--zn-transition-fast) background-color;
19
+ font-family: var(--zn-input-font-family), sans-serif;
20
+ font-size: var(--zn-input-font-size-medium);
21
+ color: rgb(var(--zn-text-color));
22
+ user-select: none;
23
+ -webkit-user-select: none;
24
+
25
+ &:hover:not(.icon-picker__trigger--disabled) {
26
+ border-color: var(--zn-input-border-color-hover);
27
+ }
28
+
29
+ &:focus-visible {
30
+ outline: none;
31
+ border-color: var(--zn-input-border-color-focus);
32
+ box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color);
33
+ }
34
+ }
35
+
36
+ .icon-picker__trigger--disabled {
37
+ opacity: 0.5;
38
+ cursor: not-allowed;
39
+ }
40
+
41
+ .icon-picker__edit-text {
42
+ color: rgb(var(--zn-text-color));
43
+ font-size: var(--zn-font-size-medium);
44
+ }
45
+
46
+ .icon-picker__clear {
47
+ margin-left: auto;
48
+ }
49
+
50
+ .icon-picker__placeholder {
51
+ color: var(--zn-input-placeholder-color);
52
+ font-size: var(--zn-font-size-medium);
53
+ }
54
+
55
+ zn-dialog {
56
+ --width: min(80vw, 900px);
57
+ }
58
+
59
+ .icon-picker__dialog-layout {
60
+ display: flex;
61
+ gap: var(--zn-spacing-large);
62
+ height: 40vh;
63
+ }
64
+
65
+ .icon-picker__dialog-main {
66
+ flex: 1;
67
+ min-width: 0;
68
+ display: flex;
69
+ flex-direction: column;
70
+ overflow: hidden;
71
+ }
72
+
73
+ .icon-picker__dialog-sidebar {
74
+ width: 180px;
75
+ flex-shrink: 0;
76
+ }
77
+
78
+ @media (max-width: 640px) {
79
+ zn-dialog {
80
+ --width: 100vw;
81
+ }
82
+
83
+ zn-dialog::part(base) {
84
+ max-width: 100%;
85
+ max-height: 100%;
86
+ margin: 0;
87
+ border-radius: 0;
88
+ height: 100dvh;
89
+ width: 100vw;
90
+ }
91
+
92
+ .icon-picker__dialog-layout {
93
+ height: calc(100dvh - 8rem);
94
+ flex-direction: column;
95
+ }
96
+
97
+ .icon-picker__dialog-sidebar {
98
+ width: 100%;
99
+ flex-shrink: 0;
100
+ }
101
+ }
102
+
103
+ .icon-picker__dialog-controls {
104
+ display: flex;
105
+ gap: var(--zn-spacing-small);
106
+ margin-bottom: var(--zn-spacing-small);
107
+ }
108
+
109
+ .icon-picker__library-select {
110
+ flex: 1;
111
+ min-width: 0;
112
+ }
113
+
114
+ .icon-picker__color-input {
115
+ width: 140px;
116
+ flex-shrink: 0;
117
+ }
118
+
119
+ .icon-picker__icon-input {
120
+ margin-bottom: var(--zn-spacing-small);
121
+ }
122
+
123
+ .icon-picker__search {
124
+ margin-bottom: var(--zn-spacing-small);
125
+ }
126
+
127
+ .icon-picker__free-input {
128
+ margin-bottom: var(--zn-spacing-small);
129
+ }
130
+
131
+ .icon-picker__grid {
132
+ display: flex;
133
+ flex-wrap: wrap;
134
+ gap: var(--zn-spacing-x-small);
135
+ flex: 1;
136
+ overflow-y: auto;
137
+ align-content: flex-start;
138
+ border: solid 1px rgb(var(--zn-border-color));
139
+ border-radius: var(--zn-border-radius-medium);
140
+ padding: var(--zn-spacing-x-small);
141
+ }
142
+
143
+ .icon-picker__grid-item {
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ width: 48px;
148
+ height: 48px;
149
+ padding: 0;
150
+ border: 2px solid transparent;
151
+ border-radius: var(--zn-border-radius-medium);
152
+ background: transparent;
153
+ color: rgb(var(--zn-text-color));
154
+ cursor: pointer;
155
+ transition: var(--zn-transition-fast) background-color,
156
+ var(--zn-transition-fast) border-color;
157
+
158
+ &:hover {
159
+ background-color: rgba(var(--zn-color-primary), 0.1);
160
+ }
161
+
162
+ &--selected {
163
+ border-color: rgb(var(--zn-color-primary));
164
+ background-color: rgba(var(--zn-color-primary), 0.1);
165
+ }
166
+ }
167
+
168
+ .icon-picker__no-results {
169
+ width: 100%;
170
+ text-align: center;
171
+ padding: var(--zn-spacing-large);
172
+ color: rgb(var(--zn-text-muted));
173
+ }
174
+
175
+ .icon-picker__preview {
176
+ display: flex;
177
+ flex-direction: column;
178
+ align-items: center;
179
+ gap: var(--zn-spacing-small);
180
+ padding: var(--zn-spacing-medium);
181
+ background-color: rgba(var(--zn-panel-secondary), 0.5);
182
+ border-radius: var(--zn-border-radius-medium);
183
+ text-align: center;
184
+
185
+ &--empty {
186
+ color: rgb(var(--zn-text-muted));
187
+ }
188
+ }
189
+
190
+ .icon-picker__preview-name {
191
+ font-size: var(--zn-font-size-medium);
192
+ font-family: var(--zn-font-mono);
193
+ word-break: break-all;
194
+ }
195
+
196
+ .icon-picker__preview-library {
197
+ font-size: var(--zn-font-size-small);
198
+ color: rgb(var(--zn-text-muted));
199
+ }
200
+
201
+ .icon-picker__preview-hint {
202
+ font-size: var(--zn-font-size-small);
203
+ color: rgb(var(--zn-text-muted));
204
+ }
205
+
@@ -0,0 +1,12 @@
1
+ import ZnIconPicker from './icon-picker.component';
2
+
3
+ export * from './icon-picker.component';
4
+ export default ZnIconPicker;
5
+
6
+ ZnIconPicker.define('zn-icon-picker');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-icon-picker': ZnIconPicker;
11
+ }
12
+ }