@kubex/zinc 1.0.25 → 1.0.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/project.md +556 -0
- package/.github/workflows/js_build_and_deploy.yaml +9 -10
- package/dist/custom-elements.json +13575 -8856
- package/dist/vscode.html-custom-data.json +583 -48
- package/dist/web-types.json +1441 -181
- package/dist/zn.d.ts +1645 -660
- package/dist/zn.min.css +1 -1
- package/dist/zn.min.js +1412 -893
- 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/item.md +18 -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 -19
- 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 +81 -22
- 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 +324 -5
- package/src/components/input/input.scss +87 -0
- 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 +24 -3
- package/src/components/item/item.scss +18 -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 -11
- 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 -44
- package/src/components/translations/translations.scss +4 -0
- 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
|
@@ -53,12 +53,24 @@ export default class ZnCols extends ZincElement {
|
|
|
53
53
|
this.maxColumns = layout.reduce((a, b) => a + b, 0);
|
|
54
54
|
|
|
55
55
|
const prefix = 'zn-col-';
|
|
56
|
-
|
|
56
|
+
// Only count children slotted into the default slot (no slot attribute)
|
|
57
|
+
const children = Array.from(this.querySelectorAll(':scope > *:not([slot])')) as HTMLElement[];
|
|
58
|
+
const colsPerRow = layout.length;
|
|
59
|
+
const lastRowStart = children.length - (children.length % colsPerRow || colsPerRow);
|
|
60
|
+
|
|
61
|
+
children.forEach((element, index) => {
|
|
57
62
|
const classes = element.className.split(' ').filter((c) => !c.startsWith(prefix));
|
|
58
63
|
element.className = classes.join(' ');
|
|
59
64
|
|
|
60
|
-
const col = index %
|
|
65
|
+
const col = index % colsPerRow;
|
|
61
66
|
element.classList.add(prefix + layout[col]);
|
|
67
|
+
|
|
68
|
+
// Clip overflow on last-row elements to hide the ::before border pseudo
|
|
69
|
+
if(this.border && index >= lastRowStart) {
|
|
70
|
+
element.style.overflow = 'hidden';
|
|
71
|
+
} else {
|
|
72
|
+
element.style.overflow = '';
|
|
73
|
+
}
|
|
62
74
|
});
|
|
63
75
|
|
|
64
76
|
return html`
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
--col-width: 23%;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
:host([contents]) {
|
|
10
|
+
display: contents;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
.cols {
|
|
10
14
|
display: flex;
|
|
11
15
|
flex-wrap: wrap;
|
|
@@ -72,7 +76,8 @@
|
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
// Adds full-width lines between bordered cols
|
|
75
|
-
|
|
79
|
+
// Last row elements get overflow:hidden via JS to clip this pseudo
|
|
80
|
+
&--border ::slotted(*):before {
|
|
76
81
|
content: "";
|
|
77
82
|
position: absolute;
|
|
78
83
|
bottom: -1px;
|
|
@@ -82,17 +87,6 @@
|
|
|
82
87
|
border-bottom: 1px solid rgb(var(--zn-border-color)) !important;
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
// Stops the last child/row having a border
|
|
86
|
-
&--border ::slotted(*:last-child):before {
|
|
87
|
-
content: "";
|
|
88
|
-
position: absolute;
|
|
89
|
-
bottom: -1px;
|
|
90
|
-
left: -100vw;
|
|
91
|
-
right: -5px;
|
|
92
|
-
z-index: 1111;
|
|
93
|
-
border-bottom: 1px solid var(--zn-panel-background-color) !important;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
90
|
&--pad {
|
|
97
91
|
padding: var(--zn-spacing-2x-small);
|
|
98
92
|
}
|
|
@@ -178,6 +178,13 @@ export default class ZnConfirm extends ZincElement {
|
|
|
178
178
|
form.action = this.action;
|
|
179
179
|
form.method = 'POST';
|
|
180
180
|
|
|
181
|
+
// copy data-attributes from the confirm element to the form
|
|
182
|
+
Array.from(this.attributes).forEach(attr => {
|
|
183
|
+
if (attr.name.startsWith('data-')) {
|
|
184
|
+
form?.setAttribute(attr.name, attr.value);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
181
188
|
this.querySelectorAll('input').forEach((el: HTMLInputElement) => {
|
|
182
189
|
form?.appendChild(el.cloneNode() as Node);
|
|
183
190
|
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {classMap} from "lit/directives/class-map.js";
|
|
2
2
|
import {type CSSResultGroup, html, unsafeCSS} from 'lit';
|
|
3
|
+
import {property, query, state} from 'lit/decorators.js';
|
|
3
4
|
import ZincElement from '../../internal/zinc-element';
|
|
5
|
+
import type ZnTooltip from "../tooltip";
|
|
4
6
|
|
|
5
7
|
import styles from './copy-button.scss';
|
|
6
|
-
import {classMap} from "lit/directives/class-map.js";
|
|
7
|
-
import ZnTooltip from "../tooltip";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @summary Short summary of the component's intended use.
|
|
@@ -44,6 +44,7 @@ export default class ZnCopyButton extends ZincElement {
|
|
|
44
44
|
|
|
45
45
|
@property() src = '';
|
|
46
46
|
|
|
47
|
+
@property({type: Number, reflect: true}) size: number = 24;
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* An id that references an element in the same document from which data will be copied. If both this and `value` are
|
|
@@ -67,20 +68,20 @@ export default class ZnCopyButton extends ZincElement {
|
|
|
67
68
|
content=${copyLabel}>
|
|
68
69
|
<button class="copy-button__button" part="button" @click=${this.handleCopy}>
|
|
69
70
|
<slot part="copy-icon" name="copy-icon">
|
|
70
|
-
<zn-icon src="${this.src ? this.src : 'content_copy'}"></zn-icon>
|
|
71
|
+
<zn-icon src="${this.src ? this.src : 'content_copy'}" size="${this.size}"></zn-icon>
|
|
71
72
|
</slot>
|
|
72
73
|
<slot part="success-icon" name="success-icon" hidden>
|
|
73
|
-
<zn-icon src="check"></zn-icon>
|
|
74
|
+
<zn-icon src="check" size="${this.size}"></zn-icon>
|
|
74
75
|
</slot>
|
|
75
76
|
<slot part="error-icon" name="error-icon" hidden>
|
|
76
|
-
<zn-icon src="priority_high"></zn-icon>
|
|
77
|
+
<zn-icon src="priority_high" size="${this.size}"></zn-icon>
|
|
77
78
|
</slot>
|
|
78
79
|
</button>
|
|
79
80
|
</zn-tooltip>
|
|
80
81
|
`;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
private
|
|
84
|
+
private showStatus(status: 'success' | 'error') {
|
|
84
85
|
const iconToShow = status === 'success' ? this.successIcon : this.errorIcon;
|
|
85
86
|
this.tooltip.content = status === 'success' ? 'Copied!' : 'Error!';
|
|
86
87
|
|
|
@@ -103,7 +104,7 @@ export default class ZnCopyButton extends ZincElement {
|
|
|
103
104
|
}
|
|
104
105
|
this.isCopying = true;
|
|
105
106
|
|
|
106
|
-
let valueToCopy = this.value||this.textContent;
|
|
107
|
+
let valueToCopy = this.value || this.textContent;
|
|
107
108
|
|
|
108
109
|
if (this.from) {
|
|
109
110
|
const root = this.getRootNode() as ShadowRoot | Document;
|
|
@@ -140,14 +141,14 @@ export default class ZnCopyButton extends ZincElement {
|
|
|
140
141
|
|
|
141
142
|
if (!valueToCopy) {
|
|
142
143
|
console.error('No value to copy. Please provide a value or a valid "from" attribute.');
|
|
143
|
-
|
|
144
|
+
this.showStatus('error');
|
|
144
145
|
this.emit('zn-error');
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
try {
|
|
149
150
|
await navigator.clipboard.writeText(valueToCopy);
|
|
150
|
-
|
|
151
|
+
this.showStatus('success');
|
|
151
152
|
this.emit('zn-copy', {
|
|
152
153
|
detail: {
|
|
153
154
|
value: valueToCopy
|
|
@@ -155,7 +156,7 @@ export default class ZnCopyButton extends ZincElement {
|
|
|
155
156
|
})
|
|
156
157
|
} catch (error) {
|
|
157
158
|
console.error('Failed to copy text: ', error);
|
|
158
|
-
|
|
159
|
+
this.showStatus('error');
|
|
159
160
|
this.emit('zn-error');
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -6,48 +6,73 @@ import {
|
|
|
6
6
|
type DataProviderOption,
|
|
7
7
|
emptyDataProvider,
|
|
8
8
|
type LocalDataProvider,
|
|
9
|
+
usStateDataProvider,
|
|
9
10
|
} from "./providers/provider";
|
|
10
11
|
import {type CSSResultGroup, html, nothing, type PropertyValues, unsafeCSS} from 'lit';
|
|
11
12
|
import {FormControlController} from "../../internal/form";
|
|
12
13
|
import {property, query} from 'lit/decorators.js';
|
|
13
14
|
import {watch} from "../../internal/watch";
|
|
14
15
|
import ZincElement from '../../internal/zinc-element';
|
|
16
|
+
import ZnOption from "../option";
|
|
17
|
+
import ZnSelect from "../select";
|
|
15
18
|
import type {ZincFormControl} from '../../internal/zinc-element';
|
|
16
|
-
import type ZnSelect from "../select";
|
|
17
19
|
|
|
18
20
|
import styles from './data-select.scss';
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* @summary
|
|
23
|
+
* @summary A select component with built-in data providers for common options like colors, currencies, and countries.
|
|
22
24
|
* @documentation https://zinc.style/components/data-select
|
|
23
25
|
* @status experimental
|
|
24
26
|
* @since 1.0
|
|
25
27
|
*
|
|
26
|
-
* @dependency zn-
|
|
28
|
+
* @dependency zn-select
|
|
29
|
+
* @dependency zn-option
|
|
27
30
|
*
|
|
28
|
-
* @event zn-
|
|
31
|
+
* @event zn-input - Emitted when the select's value changes.
|
|
32
|
+
* @event zn-clear - Emitted when the clear button is activated.
|
|
33
|
+
* @event blur - Emitted when the select loses focus.
|
|
29
34
|
*
|
|
30
|
-
* @slot - The
|
|
31
|
-
* @slot
|
|
35
|
+
* @slot label - The select's label. Alternatively, you can use the `label` attribute.
|
|
36
|
+
* @slot label-tooltip - Used to add text that is displayed in a tooltip next to the label. Alternatively, you can use the `label-tooltip` attribute.
|
|
37
|
+
* @slot context-note - Used to add contextual text that is displayed above the select, on the right. Alternatively, you can use the `context-note` attribute.
|
|
38
|
+
* @slot help-text - Text that describes how to use the select. Alternatively, you can use the `help-text` attribute.
|
|
32
39
|
*
|
|
33
|
-
* @csspart
|
|
34
|
-
*
|
|
35
|
-
* @
|
|
40
|
+
* @csspart combobox - The container that wraps the prefix, combobox, clear icon, and expand button (forwarded from zn-select).
|
|
41
|
+
* @csspart expand-icon - The container that wraps the expand icon (forwarded from zn-select).
|
|
42
|
+
* @csspart form-control-help-text - The help text's wrapper (forwarded from zn-select).
|
|
43
|
+
* @csspart form-control-input - The select's wrapper (forwarded from zn-select).
|
|
44
|
+
* @csspart display-input - The element that displays the selected option's label (forwarded from zn-select).
|
|
36
45
|
*/
|
|
37
46
|
export default class ZnDataSelect extends ZincElement implements ZincFormControl {
|
|
38
47
|
static styles: CSSResultGroup = unsafeCSS(styles);
|
|
48
|
+
static dependencies = {
|
|
49
|
+
'zn-select': ZnSelect,
|
|
50
|
+
'zn-option': ZnOption,
|
|
51
|
+
};
|
|
39
52
|
|
|
40
53
|
@query('#select') select: ZnSelect;
|
|
41
|
-
@query('#select__prefix') selectPrefix: HTMLElement;
|
|
42
54
|
|
|
43
55
|
/** The name of the select. Used for form submission. */
|
|
44
56
|
@property() name: string;
|
|
45
57
|
|
|
46
|
-
/** The value of the select. Used for form submission. */
|
|
47
|
-
@property(
|
|
58
|
+
/** The value of the select. Used for form submission. When `multiple` is enabled, this is an array of strings. */
|
|
59
|
+
@property({
|
|
60
|
+
converter: {
|
|
61
|
+
fromAttribute: (value: string | null) => {
|
|
62
|
+
if (!value) return '';
|
|
63
|
+
return value;
|
|
64
|
+
},
|
|
65
|
+
toAttribute: (value: string | string[]) => {
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
return value.join(',');
|
|
68
|
+
}
|
|
69
|
+
return value || '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}) value: string | string[] = '';
|
|
48
73
|
|
|
49
74
|
/** The provider of the select. */
|
|
50
|
-
@property() provider: 'color' | 'currency' | 'country' | 'phone';
|
|
75
|
+
@property() provider: 'color' | 'currency' | 'country' | 'phone' | 'us-state';
|
|
51
76
|
|
|
52
77
|
/** The position of the icon. */
|
|
53
78
|
@property({attribute: 'icon-position'}) iconPosition: 'start' | 'end' | 'none' = 'none';
|
|
@@ -70,6 +95,9 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
70
95
|
/** Include an "All" option at the top. */
|
|
71
96
|
@property({type: Boolean, attribute: 'allow-all'}) allowAll = false;
|
|
72
97
|
|
|
98
|
+
/** Include a "Common" option that selects multiple common currencies. */
|
|
99
|
+
@property({type: Boolean, attribute: 'allow-common'}) allowCommon = false;
|
|
100
|
+
|
|
73
101
|
/** The selects label. If you need to display HTML, use the `label` slot instead. */
|
|
74
102
|
@property() label = '';
|
|
75
103
|
|
|
@@ -91,12 +119,23 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
91
119
|
/** The selects required attribute. */
|
|
92
120
|
@property({type: Boolean, reflect: true}) required = false;
|
|
93
121
|
|
|
122
|
+
/** Disables the select. */
|
|
123
|
+
@property({type: Boolean, reflect: true}) disabled = false;
|
|
124
|
+
|
|
94
125
|
@property({attribute: "icon-only", type: Boolean, reflect: true}) iconOnly = false;
|
|
95
126
|
|
|
96
127
|
@property({type: Boolean}) multiple = false;
|
|
97
128
|
|
|
129
|
+
@property({attribute: "select-first", type: Boolean}) selectFirst = false;
|
|
130
|
+
|
|
131
|
+
@property() distinct = "";
|
|
132
|
+
|
|
133
|
+
@property() conditional = "";
|
|
134
|
+
|
|
98
135
|
protected readonly formControlController = new FormControlController(this);
|
|
99
136
|
|
|
137
|
+
private selectObserver?: MutationObserver;
|
|
138
|
+
|
|
100
139
|
get validationMessage() {
|
|
101
140
|
return this.select.validationMessage;
|
|
102
141
|
}
|
|
@@ -120,13 +159,39 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
120
159
|
disconnectedCallback() {
|
|
121
160
|
super.disconnectedCallback();
|
|
122
161
|
window.removeEventListener('keydown', this.closeOnTab);
|
|
162
|
+
this.selectObserver?.disconnect();
|
|
123
163
|
}
|
|
124
164
|
|
|
125
|
-
protected firstUpdated(_changedProperties: PropertyValues) {
|
|
165
|
+
protected async firstUpdated(_changedProperties: PropertyValues) {
|
|
126
166
|
super.firstUpdated(_changedProperties);
|
|
167
|
+
|
|
168
|
+
if (this.select) {
|
|
169
|
+
await this.select.updateComplete;
|
|
170
|
+
this.select.addEventListener('zn-change', () => {
|
|
171
|
+
this._updatePrefix();
|
|
172
|
+
});
|
|
173
|
+
this._observeSelectDisabled();
|
|
174
|
+
}
|
|
127
175
|
this._updatePrefix();
|
|
128
176
|
}
|
|
129
177
|
|
|
178
|
+
protected async updated(_changedProperties: PropertyValues) {
|
|
179
|
+
super.updated(_changedProperties);
|
|
180
|
+
|
|
181
|
+
if (_changedProperties.has('value') || _changedProperties.has('multiple')) {
|
|
182
|
+
this._normalizeValue();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (_changedProperties.has('value') ||
|
|
186
|
+
_changedProperties.has('provider') ||
|
|
187
|
+
_changedProperties.has('iconPosition')) {
|
|
188
|
+
if (this.select) {
|
|
189
|
+
await this.select.updateComplete;
|
|
190
|
+
}
|
|
191
|
+
this._updatePrefix();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
130
195
|
checkValidity(): boolean {
|
|
131
196
|
return this.select.checkValidity();
|
|
132
197
|
}
|
|
@@ -145,7 +210,7 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
145
210
|
|
|
146
211
|
closeOnTab = (e: KeyboardEvent) => {
|
|
147
212
|
if (this.select.open && e.key === 'Tab') {
|
|
148
|
-
this.select.hide();
|
|
213
|
+
this.select.hide().then();
|
|
149
214
|
}
|
|
150
215
|
}
|
|
151
216
|
|
|
@@ -153,15 +218,20 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
153
218
|
async handleValueChange() {
|
|
154
219
|
await this.updateComplete;
|
|
155
220
|
this.formControlController.updateValidity();
|
|
221
|
+
if (this.select) {
|
|
222
|
+
await this.select.updateComplete;
|
|
223
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
224
|
+
}
|
|
156
225
|
this._updatePrefix();
|
|
157
226
|
}
|
|
158
227
|
|
|
159
228
|
handleInput = (e: Event) => {
|
|
160
|
-
|
|
229
|
+
const target = e.target as ZnSelect;
|
|
230
|
+
this.value = target.value ?? (this.multiple ? [] : '');
|
|
161
231
|
};
|
|
162
232
|
|
|
163
233
|
handleClear = () => {
|
|
164
|
-
this.value = '';
|
|
234
|
+
this.value = this.multiple ? [] : '';
|
|
165
235
|
}
|
|
166
236
|
|
|
167
237
|
getLocalProvider(name: string): LocalDataProvider<DataProviderOption> {
|
|
@@ -169,11 +239,13 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
169
239
|
case 'color':
|
|
170
240
|
return colorDataProvider;
|
|
171
241
|
case 'currency':
|
|
172
|
-
return currencyDataProvider;
|
|
242
|
+
return currencyDataProvider(this.allowCommon);
|
|
173
243
|
case 'country':
|
|
174
244
|
return countryDataProvider;
|
|
175
245
|
case 'phone':
|
|
176
246
|
return countryDialPrefixDataProvider;
|
|
247
|
+
case 'us-state':
|
|
248
|
+
return usStateDataProvider;
|
|
177
249
|
default:
|
|
178
250
|
return emptyDataProvider;
|
|
179
251
|
}
|
|
@@ -189,16 +261,35 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
189
261
|
let data = localProvider.getData;
|
|
190
262
|
|
|
191
263
|
if (filterKeys.length) {
|
|
192
|
-
|
|
264
|
+
const normalizedFilterKeys = filterKeys.map(key => key.toUpperCase());
|
|
265
|
+
data = localProvider.getData.reduce<DataProviderOption[]>((acc, item) => {
|
|
266
|
+
const key = item.key.toUpperCase();
|
|
267
|
+
if (key.includes(',')) {
|
|
268
|
+
const parts = key.split(',').map(p => p.trim());
|
|
269
|
+
const matchingParts = parts.filter(part => normalizedFilterKeys.includes(part));
|
|
270
|
+
if (matchingParts.length > 1) {
|
|
271
|
+
acc.push({
|
|
272
|
+
...item,
|
|
273
|
+
key: matchingParts.join(','),
|
|
274
|
+
value: `Common - ${matchingParts.join(', ')}`
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
} else if (normalizedFilterKeys.includes(key)) {
|
|
278
|
+
acc.push(item);
|
|
279
|
+
}
|
|
280
|
+
return acc;
|
|
281
|
+
}, []);
|
|
193
282
|
}
|
|
194
283
|
|
|
195
284
|
if (this.provider !== 'color' && this.allowAll) {
|
|
196
|
-
const label = {
|
|
285
|
+
const label: Record<string, string> = {
|
|
197
286
|
currency: 'All Currencies',
|
|
198
287
|
country: 'All Countries',
|
|
199
|
-
phone: 'All Phones'
|
|
200
|
-
|
|
201
|
-
|
|
288
|
+
phone: 'All Phones',
|
|
289
|
+
'us-state': 'All States'
|
|
290
|
+
};
|
|
291
|
+
const allLabel = label[this.provider] || 'All';
|
|
292
|
+
const allOption: DataProviderOption = {key: '', value: allLabel, prefix: ''};
|
|
202
293
|
data = [allOption, ...data.filter(item => item.key !== allOption.key)];
|
|
203
294
|
}
|
|
204
295
|
|
|
@@ -213,50 +304,130 @@ export default class ZnDataSelect extends ZincElement implements ZincFormControl
|
|
|
213
304
|
required=${this.required || nothing}
|
|
214
305
|
placement=${this.placement}
|
|
215
306
|
name="${this.name}"
|
|
307
|
+
distinct="${this.distinct}"
|
|
308
|
+
?disabled="${this.disabled}"
|
|
309
|
+
?select-first="${this.selectFirst}"
|
|
310
|
+
conditional="${this.conditional}"
|
|
216
311
|
@zn-input="${this.handleInput}"
|
|
217
312
|
@zn-clear="${this.handleClear}"
|
|
218
313
|
@blur=${this.blur}
|
|
219
|
-
value="${this.value}"
|
|
314
|
+
.value="${this.value}"
|
|
220
315
|
?multiple=${this.multiple}
|
|
221
316
|
placeholder="${this.getPlaceholder(localProvider)}"
|
|
222
317
|
exportparts="combobox,expand-icon,form-control-help-text,form-control-input,display-input">
|
|
223
318
|
${(this.iconPosition !== 'none' || this.iconOnly) ? html`
|
|
224
|
-
<div
|
|
319
|
+
<div slot="prefix"
|
|
320
|
+
class="select__icon ${this.iconPosition === 'end' ? 'select__icon--end' : ''}"></div>` : ''}
|
|
225
321
|
${data.map((item: DataProviderOption) => html`
|
|
226
322
|
<zn-option class="select__option" value="${item.key}">
|
|
227
323
|
${(this.iconPosition !== 'none' || this.iconOnly) ? html`<span
|
|
228
|
-
slot="
|
|
324
|
+
slot="prefix">${item.prefix}</span>` : ''}
|
|
229
325
|
${this.iconOnly ? undefined : item.value}
|
|
230
326
|
</zn-option>`)}
|
|
231
327
|
</zn-select>`;
|
|
232
328
|
}
|
|
233
329
|
|
|
234
330
|
private _updatePrefix() {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
331
|
+
if (!this.select || !this.shadowRoot) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const selectIcon = this.shadowRoot.querySelector('.select__icon')!;
|
|
336
|
+
const shouldShowIcon = this.iconPosition !== 'none' || this.iconOnly;
|
|
337
|
+
if (!shouldShowIcon) {
|
|
338
|
+
if (selectIcon) selectIcon.innerHTML = '';
|
|
339
|
+
this._updateIconEmptyState();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (this.iconPosition === 'none' && this.iconOnly) {
|
|
344
|
+
this.iconPosition = 'start';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const selectedOptions = this.select.selectedOptions;
|
|
348
|
+
if (!selectedOptions || selectedOptions.length === 0) {
|
|
349
|
+
if (selectIcon) selectIcon.innerHTML = '';
|
|
350
|
+
this._updateIconEmptyState();
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!selectIcon) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const selectedOption = selectedOptions[0];
|
|
359
|
+
const slot = selectedOption.querySelector('[slot="prefix"]');
|
|
360
|
+
|
|
361
|
+
if (slot) {
|
|
362
|
+
selectIcon.innerHTML = '';
|
|
363
|
+
selectIcon.appendChild(slot.cloneNode(true));
|
|
364
|
+
} else {
|
|
365
|
+
selectIcon.innerHTML = '';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this._updateIconEmptyState();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private _updateIconEmptyState() {
|
|
372
|
+
if (!this.shadowRoot) return;
|
|
373
|
+
|
|
374
|
+
const selectIcon = this.shadowRoot.querySelector('.select__icon');
|
|
375
|
+
const isEmpty = !selectIcon || selectIcon.innerHTML.trim() === '';
|
|
376
|
+
|
|
377
|
+
if (isEmpty) {
|
|
378
|
+
this.setAttribute('icon-empty', '');
|
|
379
|
+
} else {
|
|
380
|
+
this.removeAttribute('icon-empty');
|
|
252
381
|
}
|
|
253
382
|
}
|
|
254
383
|
|
|
255
384
|
private getPlaceholder(localProvider: LocalDataProvider<any>) {
|
|
256
|
-
if (this.value) {
|
|
385
|
+
if (!this.value) {
|
|
386
|
+
return `Choose a ${localProvider.getName}`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const hasValue = Array.isArray(this.value) ? this.value.length > 0 : this.value.length > 0;
|
|
390
|
+
if (hasValue) {
|
|
257
391
|
return '';
|
|
258
392
|
}
|
|
259
393
|
|
|
260
394
|
return `Choose a ${localProvider.getName}`
|
|
261
395
|
}
|
|
396
|
+
|
|
397
|
+
private _observeSelectDisabled() {
|
|
398
|
+
if (!this.select) return;
|
|
399
|
+
|
|
400
|
+
this._syncDisabledState();
|
|
401
|
+
|
|
402
|
+
this.selectObserver = new MutationObserver(() => {
|
|
403
|
+
this._syncDisabledState();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
this.selectObserver.observe(this.select, {
|
|
407
|
+
attributes: true,
|
|
408
|
+
attributeFilter: ['disabled']
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private _syncDisabledState() {
|
|
413
|
+
if (!this.select) return;
|
|
414
|
+
|
|
415
|
+
this.disabled = this.select.disabled;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private _normalizeValue() {
|
|
419
|
+
if (this.multiple) {
|
|
420
|
+
if (typeof this.value === 'string') {
|
|
421
|
+
if (this.value === '') {
|
|
422
|
+
this.value = [];
|
|
423
|
+
} else {
|
|
424
|
+
this.value = this.value.split(',').map(v => v.trim()).filter(v => v !== '');
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
if (Array.isArray(this.value)) {
|
|
429
|
+
this.value = this.value.length > 0 ? this.value[0] : '';
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
262
433
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@use "../../wc";
|
|
2
2
|
|
|
3
|
+
// Color icon palette
|
|
3
4
|
$colors: (
|
|
4
5
|
red: var(--zn-color-red-500),
|
|
5
6
|
blue: var(--zn-color-blue-500),
|
|
@@ -12,15 +13,47 @@ $colors: (
|
|
|
12
13
|
gray: var(--zn-color-gray-500)
|
|
13
14
|
);
|
|
14
15
|
|
|
16
|
+
// Size spacing configuration
|
|
17
|
+
$sizes: (
|
|
18
|
+
small: var(--zn-input-spacing-small),
|
|
19
|
+
medium: var(--zn-input-spacing-medium),
|
|
20
|
+
large: var(--zn-input-spacing-large)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Mixin for icon spacing based on size
|
|
24
|
+
@mixin icon-spacing($size, $spacing) {
|
|
25
|
+
:host([size="#{$size}"]) .select__icon {
|
|
26
|
+
padding-inline-start: $spacing;
|
|
27
|
+
margin-inline-end: $spacing;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Mixin for end-positioned icon spacing
|
|
32
|
+
@mixin icon-end-spacing($size, $spacing) {
|
|
33
|
+
:host([size="#{$size}"]) .select__icon--end {
|
|
34
|
+
padding-inline-start: 0;
|
|
35
|
+
padding-inline-end: $spacing;
|
|
36
|
+
margin-inline-end: 0;
|
|
37
|
+
margin-inline-start: auto;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Mixin for combobox padding restoration
|
|
42
|
+
@mixin combobox-padding($condition, $size, $spacing) {
|
|
43
|
+
:host([#{$condition}][size="#{$size}"]) zn-select::part(combobox) {
|
|
44
|
+
padding-inline-start: $spacing !important;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
15
48
|
:host {
|
|
16
49
|
display: block;
|
|
17
50
|
}
|
|
18
51
|
|
|
19
52
|
.color-icon {
|
|
20
|
-
width:
|
|
21
|
-
height:
|
|
53
|
+
width: var(--zn-spacing-medium);
|
|
54
|
+
height: var(--zn-spacing-medium);
|
|
22
55
|
display: block;
|
|
23
|
-
border-radius:
|
|
56
|
+
border-radius: var(--zn-border-radius-circle);
|
|
24
57
|
flex-shrink: 0;
|
|
25
58
|
|
|
26
59
|
@each $name, $color in $colors {
|
|
@@ -34,10 +67,37 @@ zn-select::part(listbox) {
|
|
|
34
67
|
max-height: 300px;
|
|
35
68
|
}
|
|
36
69
|
|
|
37
|
-
.
|
|
38
|
-
font-size:
|
|
70
|
+
.select__icon {
|
|
71
|
+
font-size: var(--zn-font-size-x-large);
|
|
72
|
+
line-height: 1;
|
|
73
|
+
display: inline-flex;
|
|
74
|
+
align-items: center;
|
|
39
75
|
|
|
40
76
|
&:empty {
|
|
41
|
-
display:
|
|
77
|
+
display: none;
|
|
42
78
|
}
|
|
43
79
|
}
|
|
80
|
+
|
|
81
|
+
@each $size, $spacing in $sizes {
|
|
82
|
+
@include icon-spacing($size, $spacing);
|
|
83
|
+
@include icon-end-spacing($size, $spacing);
|
|
84
|
+
@include combobox-padding('icon-position="end"', $size, $spacing);
|
|
85
|
+
@include combobox-padding('icon-position="start"][icon-empty', $size, $spacing);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:host(:not([size])) .select__icon {
|
|
89
|
+
padding-inline-start: var(--zn-input-spacing-medium);
|
|
90
|
+
margin-inline-end: var(--zn-input-spacing-medium);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
:host(:not([size])) .select__icon--end {
|
|
94
|
+
padding-inline-start: 0;
|
|
95
|
+
padding-inline-end: var(--zn-input-spacing-medium);
|
|
96
|
+
margin-inline-end: 0;
|
|
97
|
+
margin-inline-start: auto;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:host([icon-position="end"]:not([size])) zn-select::part(combobox),
|
|
101
|
+
:host([icon-position="start"][icon-empty]:not([size])) zn-select::part(combobox) {
|
|
102
|
+
padding-inline-start: var(--zn-input-spacing-medium) !important;
|
|
103
|
+
}
|