@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
@@ -7,7 +7,7 @@ import {ifDefined} from "lit/directives/if-defined.js";
7
7
  import {live} from "lit/directives/live.js";
8
8
  import {property, query, state} from 'lit/decorators.js';
9
9
  import {watch} from "../../internal/watch";
10
- import AirDatepicker, {type AirDatepickerLocale} from "air-datepicker";
10
+ import AirDatepicker, {type AirDatepickerLocale, type AirDatepickerOptions} from "air-datepicker";
11
11
  import ZincElement from '../../internal/zinc-element';
12
12
  import ZnIcon from "../icon";
13
13
  import ZnTooltip from "../tooltip";
@@ -16,21 +16,41 @@ import type {ZincFormControl} from '../../internal/zinc-element';
16
16
  import styles from './datepicker.scss';
17
17
 
18
18
  /**
19
- * @summary Short summary of the component's intended use.
19
+ * @summary A date picker component with calendar popup and input validation.
20
20
  * @documentation https://zinc.style/components/datepicker
21
21
  * @status experimental
22
22
  * @since 1.0
23
23
  *
24
- * @dependency zn-example
24
+ * @dependency zn-icon
25
+ * @dependency zn-tooltip
25
26
  *
26
- * @event zn-event-name - Emitted as an example.
27
+ * @event zn-change - Emitted when the date value changes.
28
+ * @event zn-input - Emitted when the input value changes.
29
+ * @event zn-blur - Emitted when the input loses focus.
30
+ * @event zn-focus - Emitted when the input gains focus.
27
31
  *
28
- * @slot - The default slot.
29
- * @slot example - An example slot.
32
+ * @slot label - The datepicker's label. Alternatively, you can use the `label` attribute.
33
+ * @slot label-tooltip - Tooltip content for the label. Alternatively, you can use the `label-tooltip` attribute.
34
+ * @slot context-note - Additional context text displayed above the input. Alternatively, you can use the `context-note` attribute.
35
+ * @slot help-text - Help text displayed below the input. Alternatively, you can use the `help-text` attribute.
36
+ * @slot prefix - Content to display before the input (in addition to the default calendar icon).
37
+ * @slot suffix - Content to display after the input.
30
38
  *
31
39
  * @csspart base - The component's base wrapper.
40
+ * @csspart form-control - The form control wrapper.
41
+ * @csspart form-control-label - The label element.
42
+ * @csspart form-control-input - The input wrapper.
43
+ * @csspart form-control-help-text - The help text element.
32
44
  *
33
- * @cssproperty --example - An example CSS custom property.
45
+ * @property format - Date format using AirDatepicker tokens. Default: 'dd/MM/yyyy'
46
+ * Supported formats:
47
+ * - dd/MM/yyyy (31/12/2024) - Default
48
+ * - MM/dd/yyyy (12/31/2024)
49
+ * - yyyy-MM-dd (2024-12-31)
50
+ * - dd-MM-yyyy (31-12-2024)
51
+ * - yyyy/MM/dd (2024/12/31)
52
+ *
53
+ * @cssproperty --zn-input-* - Inherited input component CSS custom properties.
34
54
  */
35
55
  export default class ZnDatepicker extends ZincElement implements ZincFormControl {
36
56
  static styles: CSSResultGroup = unsafeCSS(styles);
@@ -96,12 +116,43 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
96
116
  */
97
117
  @property({reflect: true}) form: string;
98
118
 
119
+ @property({type: Boolean, reflect: true}) flush = false;
120
+
99
121
  /** Makes the input a required field. */
100
122
  @property({type: Boolean, reflect: true}) required = false;
101
123
 
124
+ /** Adds a clear button to the calendar for removing a selected date. **/
125
+ @property({type: Boolean}) clearable = false;
126
+
102
127
  /** Makes the input a range picker. **/
103
128
  @property({type: Boolean}) range = false;
104
129
 
130
+ /** Disallows selecting past dates. **/
131
+ @property({type: Boolean, attribute: 'disable-past-dates'}) disablePastDates = false;
132
+
133
+ /** Minimum date that can be selected. Overrides disable-past-dates if both are set. Accepts Date object or date string. **/
134
+ @property({attribute: 'min-date'}) minDate?: string | Date;
135
+
136
+ /** Maximum date that can be selected. Accepts Date object or date string. **/
137
+ @property({attribute: 'max-date'}) maxDate?: string | Date;
138
+
139
+ /**
140
+ * Date format for display and input. Uses AirDatepicker format tokens.
141
+ *
142
+ * Common formats:
143
+ * - 'dd/MM/yyyy' (31/12/2024) - Default, European style
144
+ * - 'MM/dd/yyyy' (12/31/2024) - US style
145
+ * - 'yyyy-MM-dd' (2024-12-31) - ISO style
146
+ * - 'dd-MM-yyyy' (31-12-2024) - Alternative European
147
+ * - 'yyyy/MM/dd' (2024/12/31) - Alternative ISO
148
+ *
149
+ * Format tokens:
150
+ * - dd: Day with leading zero (01-31)
151
+ * - MM: Month with leading zero (01-12)
152
+ * - yyyy: Full year (2024)
153
+ */
154
+ @property() format: string = 'MM/dd/yyyy';
155
+
105
156
  private _instance: AirDatepicker<HTMLInputElement>;
106
157
 
107
158
 
@@ -125,6 +176,26 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
125
176
  async handleValueChange() {
126
177
  await this.updateComplete;
127
178
  this.formControlController.updateValidity();
179
+
180
+ // Sync value with AirDatepicker instance
181
+ if (this._instance && this.value) {
182
+ const date = this.parseDate(String(this.value));
183
+ if (date) {
184
+ this._instance.selectDate(date, {silent: true});
185
+ }
186
+ } else if (this._instance && !this.value) {
187
+ this._instance.clear({silent: true});
188
+ }
189
+ }
190
+
191
+ @watch('clearable', {waitUntilFirstUpdate: true})
192
+ @watch('format', {waitUntilFirstUpdate: true})
193
+ @watch('range', {waitUntilFirstUpdate: true})
194
+ @watch('minDate', {waitUntilFirstUpdate: true})
195
+ @watch('maxDate', {waitUntilFirstUpdate: true})
196
+ @watch('disablePastDates', {waitUntilFirstUpdate: true})
197
+ handleDatepickerOptionsChange() {
198
+ this.init();
128
199
  }
129
200
 
130
201
  /** Sets focus on the input. */
@@ -172,17 +243,64 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
172
243
 
173
244
  const inputElement = this.shadowRoot?.querySelector('input') as HTMLInputElement;
174
245
  if (inputElement) {
175
- this._instance = new AirDatepicker(inputElement, {
246
+ const options: AirDatepickerOptions = {
176
247
  locale: enLocale,
248
+ dateFormat: this.format,
177
249
  range: this.range,
178
- onSelect: () => {
250
+ toggleSelected: false,
251
+ onSelect: ({date}) => {
179
252
  this.handleChange();
253
+ // Blur the input after selection to prevent invisible keyboard navigation
254
+ if (date && !this.range) {
255
+ // For single date selection, blur immediately
256
+ this.input.blur();
257
+ } else if (date && this.range && Array.isArray(date) && date.length === 2) {
258
+ // For range selection, blur only after both dates are selected
259
+ this.input.blur();
260
+ }
180
261
  }
181
- });
262
+ };
263
+
264
+ if (this.minDate) {
265
+ options.minDate = typeof this.minDate === 'string' ? new Date(this.minDate) : this.minDate;
266
+ } else if (this.disablePastDates) {
267
+ options.minDate = this.normalizeDate(new Date());
268
+ }
269
+
270
+ if (this.maxDate) {
271
+ options.maxDate = typeof this.maxDate === 'string' ? new Date(this.maxDate) : this.maxDate;
272
+ }
273
+
274
+ if (this.clearable) {
275
+ options.buttons = ['clear'];
276
+ }
277
+
278
+ this._instance = new AirDatepicker(inputElement, options);
182
279
  }
183
280
  }
184
281
 
185
- private handleInput() {
282
+ private handleInput(event?: InputEvent) {
283
+ const originalValue = this.input.value;
284
+ const cursorPosition = this.input.selectionStart || 0;
285
+ const separator = this.getFormatSeparator();
286
+
287
+ // Remove any invalid characters (keep only digits and the separator)
288
+ const validCharPattern = new RegExp(`[^0-9${this.escapeRegex(separator)}]`, 'g');
289
+ const cleanedValue = originalValue.replace(validCharPattern, '');
290
+ const formattedValue = this.autoFormatDate(cleanedValue, separator);
291
+
292
+ if (originalValue !== formattedValue) {
293
+ this.input.value = formattedValue;
294
+
295
+ // Adjust the cursor position if a separator was auto-inserted
296
+ if (formattedValue.length > originalValue.length && event?.inputType === 'insertText') {
297
+ const newCursorPos = cursorPosition + (formattedValue.length - originalValue.length);
298
+ this.input.setSelectionRange(newCursorPos, newCursorPos);
299
+ } else {
300
+ this.input.setSelectionRange(cursorPosition, cursorPosition);
301
+ }
302
+ }
303
+
186
304
  this.value = this.input.value;
187
305
  this.formControlController.updateValidity();
188
306
  this.emit('zn-input');
@@ -198,6 +316,216 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
198
316
  this.formControlController.emitInvalidEvent(event);
199
317
  }
200
318
 
319
+ private handleKeyDown(event: KeyboardEvent) {
320
+ if (this._instance && this._instance.visible) {
321
+ return;
322
+ }
323
+
324
+ // Allow navigation and control keys
325
+ const allowedKeys = [
326
+ 'Backspace', 'Delete', 'Tab', 'Escape', 'Enter',
327
+ 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
328
+ 'Home', 'End'
329
+ ];
330
+
331
+ // Allow Ctrl/Cmd shortcuts
332
+ if (event.ctrlKey || event.metaKey) {
333
+ return;
334
+ }
335
+
336
+ // Allow allowed navigation keys
337
+ if (allowedKeys.includes(event.key)) {
338
+ return;
339
+ }
340
+
341
+ // Allow digits and the format separator
342
+ const separator = this.getFormatSeparator();
343
+ const allowedPattern = new RegExp(`^[0-9${this.escapeRegex(separator)}]$`);
344
+
345
+ if (allowedPattern.test(event.key)) {
346
+ return;
347
+ }
348
+
349
+ event.preventDefault();
350
+ }
351
+
352
+ private handlePaste(event: ClipboardEvent) {
353
+ const pastedText = event.clipboardData?.getData('text') || '';
354
+
355
+ // Only allow digits, the format separator, and whitespace
356
+ const separator = this.getFormatSeparator();
357
+ const allowedPattern = new RegExp(`^[0-9${this.escapeRegex(separator)}\\s]*$`);
358
+
359
+ if (!allowedPattern.test(pastedText)) {
360
+ event.preventDefault();
361
+ }
362
+ }
363
+
364
+ private handleBlur() {
365
+ if (this._instance && this._instance.visible) {
366
+ return;
367
+ }
368
+
369
+ if (!this.input.value || this.input.value.trim() === '') {
370
+ return;
371
+ }
372
+
373
+ if (!this.isValidDateString(this.input.value)) {
374
+ this.clearInvalidDate();
375
+ return;
376
+ }
377
+
378
+ const date = this.parseDate(this.input.value);
379
+ if (date && !this.isDateInRange(date)) {
380
+ this.clearInvalidDate();
381
+ return;
382
+ }
383
+
384
+ // Sync valid typed date with AirDatepicker instance
385
+ if (date && this._instance) {
386
+ this._instance.selectDate(date);
387
+ }
388
+ }
389
+
390
+ private isValidDateString(value: string): boolean {
391
+ const parsed = this.parseDateString(value);
392
+ if (!parsed) {
393
+ return false;
394
+ }
395
+
396
+ const {day, month, year} = parsed;
397
+ const date = new Date(year, month - 1, day);
398
+
399
+ // Verify the date is valid and wasn't silently corrected
400
+ return date.getFullYear() === year &&
401
+ date.getMonth() === month - 1 &&
402
+ date.getDate() === day;
403
+ }
404
+
405
+ private parseDate(value: string): Date | null {
406
+ const parsed = this.parseDateString(value);
407
+ if (!parsed) {
408
+ return null;
409
+ }
410
+
411
+ const {day, month, year} = parsed;
412
+ const date = new Date(year, month - 1, day);
413
+ return this.normalizeDate(date);
414
+ }
415
+
416
+ private parseDateString(value: string): { day: number; month: number; year: number } | null {
417
+ const separator = this.getFormatSeparator();
418
+
419
+ // Create regex pattern based on format
420
+ // Convert AirDatepicker format tokens to regex
421
+ const escapedSeparator = this.escapeRegex(separator);
422
+ const formatPattern = this.format
423
+ .replace(/dd/g, '(\\d{1,2})')
424
+ .replace(/MM/g, '(\\d{1,2})')
425
+ .replace(/yyyy/g, '(\\d{4})')
426
+ .replace(new RegExp(escapedSeparator, 'g'), escapedSeparator);
427
+
428
+ const regex = new RegExp(`^${formatPattern}$`);
429
+ const match = value.match(regex);
430
+
431
+ if (!match) {
432
+ return null;
433
+ }
434
+
435
+ // Determine which capture group corresponds to which date part
436
+ const formatParts = this.format.split(separator);
437
+ let dayIndex = -1;
438
+ let monthIndex = -1;
439
+ let yearIndex = -1;
440
+
441
+ formatParts.forEach((part, index) => {
442
+ if (part === 'dd') dayIndex = index + 1;
443
+ if (part === 'MM') monthIndex = index + 1;
444
+ if (part === 'yyyy') yearIndex = index + 1;
445
+ });
446
+
447
+ if (dayIndex === -1 || monthIndex === -1 || yearIndex === -1) {
448
+ return null;
449
+ }
450
+
451
+ return {
452
+ day: parseInt(match[dayIndex]),
453
+ month: parseInt(match[monthIndex]),
454
+ year: parseInt(match[yearIndex])
455
+ };
456
+ }
457
+
458
+ private isDateInRange(date: Date): boolean {
459
+ let minDate: Date | undefined;
460
+ let maxDate: Date | undefined;
461
+
462
+ // Determine minDate
463
+ if (this.minDate) {
464
+ minDate = typeof this.minDate === 'string' ? new Date(this.minDate) : new Date(this.minDate);
465
+ this.normalizeDate(minDate);
466
+ } else if (this.disablePastDates) {
467
+ minDate = this.normalizeDate(new Date());
468
+ }
469
+
470
+ // Determine maxDate
471
+ if (this.maxDate) {
472
+ maxDate = typeof this.maxDate === 'string' ? new Date(this.maxDate) : new Date(this.maxDate);
473
+ this.normalizeDate(maxDate);
474
+ }
475
+
476
+ return !((minDate && date < minDate) || (maxDate && date > maxDate));
477
+ }
478
+
479
+ private clearInvalidDate() {
480
+ this.input.value = '';
481
+ this.value = '';
482
+ if (this._instance) {
483
+ this._instance.clear();
484
+ }
485
+ this.formControlController.updateValidity();
486
+ }
487
+
488
+ private getFormatSeparator(): string {
489
+ const match = this.format.match(/[^dMy]/);
490
+ return match ? match[0] : '/';
491
+ }
492
+
493
+ private escapeRegex(str: string): string {
494
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
495
+ }
496
+
497
+ private normalizeDate(date: Date): Date {
498
+ date.setHours(0, 0, 0, 0);
499
+ return date;
500
+ }
501
+
502
+ private autoFormatDate(value: string, separator: string): string {
503
+ const digitsOnly = value.replace(new RegExp(`\\${separator}`, 'g'), '');
504
+
505
+ // Get format structure (e.g., "dd/MM/yyyy" -> [2, 2, 4])
506
+ const formatParts = this.format.split(separator);
507
+ const lengths = formatParts.map(part => part.length);
508
+
509
+ let formatted = '';
510
+ let digitIndex = 0;
511
+
512
+ for (let i = 0; i < lengths.length; i++) {
513
+ const partLength = lengths[i];
514
+ const partValue = digitsOnly.substring(digitIndex, digitIndex + partLength);
515
+
516
+ if (partValue) {
517
+ formatted += partValue;
518
+ digitIndex += partLength;
519
+
520
+ if (i < lengths.length - 1 && digitIndex < digitsOnly.length) {
521
+ formatted += separator;
522
+ }
523
+ }
524
+ }
525
+
526
+ return formatted;
527
+ }
528
+
201
529
  protected updated(_changedProperties: PropertyValues) {
202
530
  super.updated(_changedProperties);
203
531
  this.init();
@@ -246,7 +574,10 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
246
574
  <span class="form-control__label-context-note"><slot name="context-note">${this.contextNote}</slot></span>`
247
575
  : ''}
248
576
 
249
- <div part="form-control-input" class="form-control-input">
577
+ <div part="form-control-input" class=${classMap({
578
+ "form-control-input": true,
579
+ "form-control-input--flush": this.flush
580
+ })}>
250
581
  <div part="base"
251
582
  class=${classMap({
252
583
  'input': true,
@@ -282,7 +613,10 @@ export default class ZnDatepicker extends ZincElement implements ZincFormControl
282
613
  aria-describedby="help-text"
283
614
  @change=${this.handleChange}
284
615
  @input=${this.handleInput}
285
- @invalid=${this.handleInvalid}>
616
+ @invalid=${this.handleInvalid}
617
+ @keydown=${this.handleKeyDown}
618
+ @paste=${this.handlePaste}
619
+ @blur=${this.handleBlur}>
286
620
 
287
621
  <span part="suffix" class="input__suffix">
288
622
  <slot name="suffix"></slot>
@@ -63,7 +63,7 @@ export default class ZnDialog extends ZincElement {
63
63
  @property({reflect: true}) variant: 'default' | 'warning' | 'announcement' = 'default';
64
64
 
65
65
  /** The dialog's size. */
66
- @property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium';
66
+ @property({reflect: true}) size: 'small' | 'medium' | 'large' | 'custom' = 'medium';
67
67
 
68
68
  /**
69
69
  * Indicated whether of not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can
@@ -21,12 +21,17 @@
21
21
  }
22
22
 
23
23
  .caption {
24
- font-weight: 500;
24
+ font-weight: var(--zn-font-weight-semibold);
25
+ font-size: var(--zn-font-size-large);
26
+ line-height: var(--zn-line-height-dense);
25
27
  color: rgb(var(--zn-text-heading));
26
28
  text-align: center;
27
29
  }
28
30
 
29
31
  .description {
32
+ font-size: var(--zn-font-size-small);
33
+ line-height: var(--zn-line-height-normal);
34
+ color: rgb(var(--zn-text));
30
35
  text-align: center;
31
36
  }
32
37
 
@@ -17,8 +17,8 @@
17
17
  }
18
18
 
19
19
  &__caption {
20
- font-weight: var(--zn-font-weight-semibold);
21
- font-size: var(--zn-font-size-large);
20
+ font-weight: var(--zn-font-weight-medium);
21
+ font-size: var(--zn-font-size-x-large);
22
22
  color: rgba(var(--zn-text-panel-title), 100%);
23
23
  margin: 0;
24
24
  overflow: hidden;
@@ -221,7 +221,7 @@
221
221
  font-weight: var(--zn-font-weight-medium);
222
222
 
223
223
  @media screen and (min-width: var(--zn-container-lg)) {
224
- font-size: var(--zn-font-size-large);
224
+ font-size: var(--zn-font-size-x-large);
225
225
  }
226
226
 
227
227
  &:empty {
@@ -29,7 +29,8 @@ export type IconColor =
29
29
  | "indigo"
30
30
  | "violet"
31
31
  | "pink"
32
- | "grey";
32
+ | "grey"
33
+ | (string & {});
33
34
 
34
35
  /**
35
36
  * @summary Short summary of the component's intended use.
@@ -58,6 +59,8 @@ export default class ZnIcon extends ZincElement {
58
59
  @property({type: Number, reflect: true}) size: number = 24;
59
60
 
60
61
  @property({type: Boolean, reflect: true}) round = false;
62
+ @property({type: Boolean, reflect: true}) tile = false;
63
+ @property({type: Boolean, reflect: true}) depth = false;
61
64
 
62
65
  @property({reflect: true}) library: IconLibrary;
63
66
 
@@ -68,6 +71,16 @@ export default class ZnIcon extends ZincElement {
68
71
 
69
72
  @property({type: Boolean}) squared: boolean = false;
70
73
 
74
+ private static readonly presetColors = new Set([
75
+ 'default', 'primary', 'accent', 'info', 'warning', 'error', 'success',
76
+ 'white', 'disabled', 'red', 'blue', 'green', 'orange', 'yellow',
77
+ 'indigo', 'violet', 'pink', 'grey',
78
+ ]);
79
+
80
+ private isPresetColor(color: string): boolean {
81
+ return ZnIcon.presetColors.has(color);
82
+ }
83
+
71
84
  gravatarOptions = "";
72
85
  defaultLibrary: IconLibrary = "material-symbols-outlined";
73
86
 
@@ -123,6 +136,12 @@ export default class ZnIcon extends ZincElement {
123
136
  if (attr === "round") {
124
137
  this.round = true;
125
138
  }
139
+ if (attr === "tile") {
140
+ this.tile = true;
141
+ }
142
+ if (attr === "depth") {
143
+ this.depth = true;
144
+ }
126
145
  });
127
146
  }
128
147
 
@@ -173,6 +192,8 @@ export default class ZnIcon extends ZincElement {
173
192
  <div class="${classMap({
174
193
  'icon': true,
175
194
  'icon--round': this.round,
195
+ 'icon--tile': this.tile,
196
+ 'icon--depth': this.depth,
176
197
  'icon--default': this.color === "default",
177
198
  'icon--primary': this.color === "primary",
178
199
  'icon--accent': this.color === "accent",
@@ -197,6 +218,7 @@ export default class ZnIcon extends ZincElement {
197
218
  'icon--squared': this.squared,
198
219
  })}" style="${styleMap({
199
220
  '--icon-size': this.size + "px",
221
+ '--icon-color': (this.color && !this.isPresetColor(this.color)) ? this.color : null,
200
222
  '--avatar-color': this.color ? null : (this.library === 'avatar' ? this.getColorForAvatar(this.getAvatarInitials(this.src)) : null)
201
223
  })}">
202
224
  ${this.library && this.library !== "src" ? html`
@@ -119,6 +119,29 @@
119
119
  border-radius: 50%;
120
120
  }
121
121
 
122
+ &--tile {
123
+ border-radius: 20%;
124
+ }
125
+
126
+ &--depth {
127
+ position: relative;
128
+
129
+ &:before, &:after {
130
+ content: "";
131
+ position: absolute;
132
+ inset: 0;
133
+ border-radius: inherit;
134
+ }
135
+
136
+ &:before {
137
+ box-shadow: inset -2px -2px 1px rgba(0, 0, 0, 0.1);
138
+ }
139
+
140
+ &:after {
141
+ box-shadow: inset 2px 2px 1px rgba(255, 255, 255, 0.2);
142
+ }
143
+ }
144
+
122
145
  &--blink {
123
146
  animation: blink-animation 1.5s steps(5, start) infinite;
124
147
  }
@@ -0,0 +1,24 @@
1
+ // Auto-generated list of Kubex Brand Icons names
2
+ // Source: https://github.com/kubex/icons/tree/main/src/brands
3
+ // Total: 18 icons
4
+
5
+ export const brandIcons: string[] = [
6
+ 'adyen',
7
+ 'bottomline',
8
+ 'chargehive',
9
+ 'chargie',
10
+ 'chargie-head',
11
+ 'checkout',
12
+ 'clearhaus',
13
+ 'cwams',
14
+ 'epx',
15
+ 'flexpay',
16
+ 'gpayments',
17
+ 'kount',
18
+ 'qualpay',
19
+ 'recaptcha',
20
+ 'tokenex',
21
+ 'trustpayments',
22
+ 'worldpay',
23
+ 'yapstone',
24
+ ];