@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.
- package/.claude/project.md +556 -0
- package/.github/workflows/js_build_and_deploy.yaml +9 -10
- package/dist/custom-elements.json +13431 -8728
- package/dist/vscode.html-custom-data.json +571 -37
- package/dist/web-types.json +1439 -181
- package/dist/zn.d.ts +1642 -661
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +1407 -887
- package/docs/_utilities/code-previews.cjs +10 -1
- package/docs/assets/scripts/docs.js +42 -18
- package/docs/data/data-table.json +229 -66
- package/docs/data/empty.json +6 -0
- package/docs/data/products-table.json +41 -193
- package/docs/pages/components/action-bar.md +121 -6
- package/docs/pages/components/alert.md +118 -15
- package/docs/pages/components/animated-button.md +299 -0
- package/docs/pages/components/audio-select.md +534 -0
- package/docs/pages/components/bulk-actions.md +79 -5
- package/docs/pages/components/button-group.md +235 -27
- package/docs/pages/components/button.md +139 -1
- package/docs/pages/components/chart.md +365 -9
- package/docs/pages/components/checkbox.md +387 -58
- package/docs/pages/components/chip.md +253 -33
- package/docs/pages/components/collapsible.md +393 -46
- package/docs/pages/components/cols.md +152 -30
- package/docs/pages/components/confirm.md +182 -16
- package/docs/pages/components/copy-button.md +169 -6
- package/docs/pages/components/data-select.md +194 -15
- package/docs/pages/components/data-table-filter.md +96 -5
- package/docs/pages/components/data-table-search.md +451 -0
- package/docs/pages/components/data-table-sort.md +66 -5
- package/docs/pages/components/data-table.md +727 -66
- package/docs/pages/components/datepicker.md +402 -7
- package/docs/pages/components/defined-label.md +120 -5
- package/docs/pages/components/dialog.md +339 -59
- package/docs/pages/components/dropdown.md +366 -11
- package/docs/pages/components/editor.md +604 -7
- package/docs/pages/components/empty-state.md +315 -16
- package/docs/pages/components/expanding-action.md +212 -28
- package/docs/pages/components/file.md +421 -12
- package/docs/pages/components/filter-container.md +158 -5
- package/docs/pages/components/filter-wrapper.md +167 -6
- package/docs/pages/components/form-group.md +383 -6
- package/docs/pages/components/hover-container.md +237 -6
- package/docs/pages/components/icon-picker.md +121 -0
- package/docs/pages/components/icon.md +454 -48
- package/docs/pages/components/inline-edit.md +787 -19
- package/docs/pages/components/input-group.md +207 -13
- package/docs/pages/components/input.md +194 -0
- package/docs/pages/components/key-container.md +115 -0
- package/docs/pages/components/key.md +101 -0
- package/docs/pages/components/menu.md +512 -6
- package/docs/pages/components/note.md +239 -36
- package/docs/pages/components/opt-group.md +107 -0
- package/docs/pages/components/pagination.md +332 -6
- package/docs/pages/components/pane.md +222 -6
- package/docs/pages/components/panel.md +213 -26
- package/docs/pages/components/popup.md +1354 -6
- package/docs/pages/components/priority-list.md +233 -0
- package/docs/pages/components/progress-bar.md +387 -6
- package/docs/pages/components/radio-group.md +242 -5
- package/docs/pages/components/radio.md +474 -6
- package/docs/pages/components/rating.md +331 -7
- package/docs/pages/components/reveal.md +524 -6
- package/docs/pages/components/scroll-container.md +552 -6
- package/docs/pages/components/select.md +762 -160
- package/docs/pages/components/skeleton.md +473 -30
- package/docs/pages/components/slideout.md +261 -5
- package/docs/pages/components/sp.md +99 -41
- package/docs/pages/components/split-button.md +347 -9
- package/docs/pages/components/split-pane.md +445 -6
- package/docs/pages/components/stat.md +549 -9
- package/docs/pages/components/status-indicator.md +230 -1
- package/docs/pages/components/stepper.md +563 -6
- package/docs/pages/components/tabs.md +439 -13
- package/docs/pages/components/textarea.md +415 -17
- package/docs/pages/components/tile.md +385 -26
- package/docs/pages/components/toggle.md +424 -6
- package/docs/pages/components/tooltip.md +367 -0
- package/docs/pages/components/translation-group.md +210 -0
- package/docs/pages/components/translations.md +319 -18
- package/docs/pages/components/vertical-stepper.md +401 -8
- package/docs/pages/components/well.md +300 -3
- package/docs/pages/getting-started/form-controls.md +162 -0
- package/docs/pages/index.md +116 -15
- package/package.json +5 -6
- package/scss/_global-spacing.scss +4 -0
- package/scss/_root.scss +2 -2
- package/scss/boot.scss +19 -0
- package/scss/shared/layout.scss +5 -0
- package/scss/themes/_light.scss +3 -3
- package/src/components/alert/alert.scss +1 -0
- package/src/components/animated-button/README.md +306 -0
- package/src/components/animated-button/animated-button.component.ts +229 -0
- package/src/components/animated-button/animated-button.scss +228 -0
- package/src/components/animated-button/animated-button.test.ts +143 -0
- package/src/components/animated-button/index.ts +12 -0
- package/src/components/button/button.component.ts +5 -0
- package/src/components/button-group/button-group.component.ts +25 -13
- package/src/components/button-group/button-group.scss +6 -0
- package/src/components/checkbox/checkbox.component.ts +4 -0
- package/src/components/checkbox/checkbox.scss +7 -4
- package/src/components/checkbox-group/checkbox-group.scss +7 -0
- package/src/components/chip/chip.component.ts +7 -2
- package/src/components/chip/chip.scss +16 -2
- package/src/components/collapsible/collapsible.component.ts +27 -20
- package/src/components/cols/cols.component.ts +14 -2
- package/src/components/cols/cols.scss +6 -12
- package/src/components/confirm/confirm.component.ts +7 -0
- package/src/components/copy-button/copy-button.component.ts +12 -11
- package/src/components/data-select/data-select.component.ts +215 -44
- package/src/components/data-select/data-select.scss +66 -6
- package/src/components/data-select/providers/currency-data-provider.ts +20 -6
- package/src/components/data-select/providers/provider.ts +1 -0
- package/src/components/data-select/providers/us-state-data-provider.ts +68 -0
- package/src/components/data-table/data-table.component.ts +82 -23
- package/src/components/data-table-search/data-table-search.component.ts +184 -0
- package/src/components/data-table-search/data-table-search.scss +17 -0
- package/src/components/data-table-search/data-table-search.test.ts +11 -0
- package/src/components/data-table-search/index.ts +12 -0
- package/src/components/datepicker/datepicker.component.ts +347 -13
- package/src/components/dialog/dialog.component.ts +1 -1
- package/src/components/empty-state/empty-state.scss +6 -1
- package/src/components/header/header.scss +3 -3
- package/src/components/icon/icon.component.ts +23 -1
- package/src/components/icon/icon.scss +23 -0
- package/src/components/icon-picker/brand-icons.ts +24 -0
- package/src/components/icon-picker/icon-picker.component.ts +407 -0
- package/src/components/icon-picker/icon-picker.scss +205 -0
- package/src/components/icon-picker/index.ts +12 -0
- package/src/components/icon-picker/line-icons.ts +612 -0
- package/src/components/icon-picker/material-icons.ts +14543 -0
- package/src/components/inline-edit/inline-edit.component.ts +190 -46
- package/src/components/inline-edit/inline-edit.scss +29 -0
- package/src/components/inline-edit/inline-edit.test.ts +314 -1
- package/src/components/input/input.component.ts +328 -5
- package/src/components/input/input.scss +100 -4
- package/src/components/input-group/input-group.component.ts +28 -7
- package/src/components/input-group/input-group.scss +48 -10
- package/src/components/item/item.component.ts +11 -2
- package/src/components/item/item.scss +7 -0
- package/src/components/key/index.ts +12 -0
- package/src/components/key/key.component.ts +47 -0
- package/src/components/key/key.scss +38 -0
- package/src/components/key-container/index.ts +12 -0
- package/src/components/key-container/key-container.component.ts +142 -0
- package/src/components/key-container/key-container.scss +33 -0
- package/src/components/menu-item/menu-item.component.ts +20 -0
- package/src/components/menu-item/menu-item.scss +69 -2
- package/src/components/navbar/navbar.component.ts +103 -8
- package/src/components/navbar/navbar.scss +6 -1
- package/src/components/opt-group/index.ts +12 -0
- package/src/components/opt-group/opt-group.component.ts +74 -0
- package/src/components/opt-group/opt-group.scss +37 -0
- package/src/components/opt-group/opt-group.test.ts +27 -0
- package/src/components/option/option.component.ts +1 -16
- package/src/components/option/option.scss +4 -0
- package/src/components/panel/panel.component.ts +10 -8
- package/src/components/panel/panel.scss +20 -0
- package/src/components/priority-list/index.ts +12 -0
- package/src/components/priority-list/priority-list.component.ts +611 -0
- package/src/components/priority-list/priority-list.scss +185 -0
- package/src/components/priority-list/priority-list.test.ts +139 -0
- package/src/components/progress-bar/progress-bar.component.ts +22 -16
- package/src/components/radio/radio.scss +10 -3
- package/src/components/select/select.component.ts +812 -72
- package/src/components/select/select.scss +108 -2
- package/src/components/select/select.test.ts +147 -2
- package/src/components/settings-container/settings-container.component.ts +44 -19
- package/src/components/settings-container/settings-container.scss +1 -1
- package/src/components/skeleton/skeleton.component.ts +6 -1
- package/src/components/slideout/slideout.scss +8 -3
- package/src/components/sp/sp.component.ts +3 -1
- package/src/components/sp/sp.scss +19 -0
- package/src/components/stepper/stepper.component.ts +16 -10
- package/src/components/textarea/textarea.scss +0 -4
- package/src/components/tile/tile.component.ts +12 -3
- package/src/components/tile/tile.scss +1 -1
- package/src/components/timer/timer.component.ts +15 -2
- package/src/components/toggle/toggle.component.ts +29 -26
- package/src/components/toggle/toggle.scss +13 -1
- package/src/components/translation-group/index.ts +12 -0
- package/src/components/translation-group/translation-group.component.ts +207 -0
- package/src/components/translation-group/translation-group.scss +8 -0
- package/src/components/translations/translations.component.ts +90 -42
- package/src/components/translations/translations.scss +5 -1
- package/src/components/vertical-stepper/vertical-stepper.component.ts +14 -9
- package/src/events/events.ts +12 -7
- package/src/events/zn-language-change.ts +7 -0
- package/src/events/zn-purchase.ts +11 -0
- package/src/events/zn-redirect.ts +7 -0
- package/src/events/zn-reorder.ts +7 -0
- package/src/events/zn-search-change.ts +11 -0
- package/src/events/zn-submit.ts +1 -1
- package/src/form-control.scss +4 -0
- package/src/internal/form-navigation.ts +510 -0
- package/src/internal/form.ts +6 -0
- 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
|
|
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-
|
|
24
|
+
* @dependency zn-icon
|
|
25
|
+
* @dependency zn-tooltip
|
|
25
26
|
*
|
|
26
|
-
* @event zn-
|
|
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
|
|
29
|
-
* @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
|
-
* @
|
|
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
|
-
|
|
246
|
+
const options: AirDatepickerOptions = {
|
|
176
247
|
locale: enLocale,
|
|
248
|
+
dateFormat: this.format,
|
|
177
249
|
range: this.range,
|
|
178
|
-
|
|
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
|
|
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:
|
|
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-
|
|
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
|
+
];
|