@redvars/peacock 3.4.0 → 3.5.0
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/dist/assets/styles.css +1 -1
- package/dist/assets/styles.css.map +1 -1
- package/dist/banner.js +202 -0
- package/dist/banner.js.map +1 -0
- package/dist/bottom-sheet.js +2 -2
- package/dist/{button-COYCtuA8.js → button-DMN1dPAg.js} +58 -75
- package/dist/button-DMN1dPAg.js.map +1 -0
- package/dist/{button-group-DsXquZQn.js → button-group-CX9CUUXk.js} +9 -14
- package/dist/button-group-CX9CUUXk.js.map +1 -0
- package/dist/button-group.js +8 -5
- package/dist/button-group.js.map +1 -1
- package/dist/button.js +7 -4
- package/dist/button.js.map +1 -1
- package/dist/card.js +15 -5
- package/dist/card.js.map +1 -1
- package/dist/chart-bar.js +2 -2
- package/dist/chart-bar.js.map +1 -1
- package/dist/chart-doughnut.js.map +1 -1
- package/dist/chart-pie.js.map +1 -1
- package/dist/chart-stacked-bar.js +2 -2
- package/dist/chart-stacked-bar.js.map +1 -1
- package/dist/{class-map-3TAnCMAX.js → class-map-YU7g0o3B.js} +2 -2
- package/dist/{class-map-3TAnCMAX.js.map → class-map-YU7g0o3B.js.map} +1 -1
- package/dist/clock.js.map +1 -1
- package/dist/code-editor.js +4 -4
- package/dist/code-editor.js.map +1 -1
- package/dist/code-highlighter.js +3 -3
- package/dist/code-highlighter.js.map +1 -1
- package/dist/custom-elements-jsdocs.json +2918 -1379
- package/dist/custom-elements.json +2783 -1054
- package/dist/directive-ZPhl09Yt.js +9 -0
- package/dist/directive-ZPhl09Yt.js.map +1 -0
- package/dist/dispatch-event-utils-CuEqjlPT.js +127 -0
- package/dist/dispatch-event-utils-CuEqjlPT.js.map +1 -0
- package/dist/fab-C5Nzxk0E.js +497 -0
- package/dist/fab-C5Nzxk0E.js.map +1 -0
- package/dist/fab.js +11 -0
- package/dist/fab.js.map +1 -0
- package/dist/index.js +17 -9
- package/dist/index.js.map +1 -1
- package/dist/{observe-theme-change-DKAIv5BB.js → is-dark-mode-DicqGkCJ.js} +6 -2
- package/dist/is-dark-mode-DicqGkCJ.js.map +1 -0
- package/dist/notification.js +417 -0
- package/dist/notification.js.map +1 -0
- package/dist/number-counter.js +2 -2
- package/dist/number-counter.js.map +1 -1
- package/dist/observe-slot-change-BGJfgg2E.js +31 -0
- package/dist/observe-slot-change-BGJfgg2E.js.map +1 -0
- package/dist/peacock-loader.js +32 -9
- package/dist/peacock-loader.js.map +1 -1
- package/dist/search.js +452 -0
- package/dist/search.js.map +1 -0
- package/dist/{select-C3XAzenC.js → select-4pl4XBj7.js} +778 -374
- package/dist/select-4pl4XBj7.js.map +1 -0
- package/dist/side-sheet.js +2 -2
- package/dist/spread-B5cgadZl.js +32 -0
- package/dist/spread-B5cgadZl.js.map +1 -0
- package/dist/src/__base_element/BaseHyperlink.d.ts +20 -0
- package/dist/src/__utils/cache-fetch.d.ts +1 -0
- package/dist/src/__utils/is-dark-mode.d.ts +1 -0
- package/dist/src/__utils/is-in-viewport.d.ts +1 -0
- package/dist/src/__utils/observe-slot-change.d.ts +1 -0
- package/dist/src/__utils/sanitize-svg.d.ts +1 -0
- package/dist/src/__utils/throttle.d.ts +4 -0
- package/dist/src/accordion/accordion-item.d.ts +33 -9
- package/dist/src/accordion/accordion.d.ts +21 -5
- package/dist/src/banner/banner.d.ts +47 -0
- package/dist/src/banner/index.d.ts +1 -0
- package/dist/src/button/BaseButton.d.ts +6 -13
- package/dist/src/button/button-group/button-group.d.ts +2 -2
- package/dist/src/empty-state/empty-state.d.ts +1 -1
- package/dist/src/fab/fab.d.ts +111 -0
- package/dist/src/fab/index.d.ts +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/link/link.d.ts +3 -10
- package/dist/src/menu/menu/menu.d.ts +3 -2
- package/dist/src/menu/sub-menu/sub-menu.d.ts +1 -0
- package/dist/src/notification/index.d.ts +1 -0
- package/dist/src/notification/notification.d.ts +69 -0
- package/dist/src/pagination/pagination.d.ts +8 -1
- package/dist/src/search/index.d.ts +1 -0
- package/dist/src/search/search.d.ts +76 -0
- package/dist/src/select/select.d.ts +3 -5
- package/dist/src/slider/slider.d.ts +4 -0
- package/dist/src/snackbar/snackbar.d.ts +14 -1
- package/dist/src/toolbar/index.d.ts +1 -0
- package/dist/src/toolbar/toolbar.d.ts +86 -0
- package/dist/{style-map-CRFEoCEg.js → style-map-DVmWOuYy.js} +2 -2
- package/dist/{style-map-CRFEoCEg.js.map → style-map-DVmWOuYy.js.map} +1 -1
- package/dist/test/banner.test.d.ts +1 -0
- package/dist/test/search.test.d.ts +1 -0
- package/dist/test/toolbar.test.d.ts +1 -0
- package/dist/throttle-C7ZAPqtu.js +24 -0
- package/dist/throttle-C7ZAPqtu.js.map +1 -0
- package/dist/toolbar.js +306 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/{unsafe-html-D3GHRaGQ.js → unsafe-html-BsGUjx94.js} +2 -2
- package/dist/{unsafe-html-D3GHRaGQ.js.map → unsafe-html-BsGUjx94.js.map} +1 -1
- package/package.json +1 -1
- package/readme.md +2 -2
- package/scss/styles.scss +4 -0
- package/src/__base_element/BaseHyperlink.ts +42 -0
- package/src/__base_element/README.md +19 -0
- package/src/__utils/cache-fetch.ts +65 -0
- package/src/__utils/is-dark-mode.ts +3 -0
- package/src/__utils/is-in-viewport.ts +6 -0
- package/src/__utils/observe-slot-change.ts +38 -0
- package/src/__utils/sanitize-svg.ts +27 -0
- package/src/__utils/throttle.ts +27 -0
- package/src/accordion/accordion-item.scss +136 -65
- package/src/accordion/accordion-item.ts +117 -44
- package/src/accordion/accordion.scss +24 -5
- package/src/accordion/accordion.ts +29 -23
- package/src/accordion/demo/index.html +74 -35
- package/src/banner/banner.scss +87 -0
- package/src/banner/banner.ts +107 -0
- package/src/banner/index.ts +1 -0
- package/src/button/BaseButton.ts +14 -27
- package/src/button/button/button-colors.scss +14 -14
- package/src/button/button/button.ts +6 -5
- package/src/button/button-group/button-group.ts +3 -3
- package/src/button/icon-button/icon-button.ts +4 -11
- package/src/card/card.ts +41 -31
- package/src/chart-bar/chart-bar.ts +1 -1
- package/src/chart-bar/chart-stacked-bar.ts +3 -1
- package/src/chart-doughnut/chart-doughnut.ts +1 -1
- package/src/chart-pie/chart-pie.ts +1 -1
- package/src/checkbox/checkbox.ts +1 -1
- package/src/clock/clock.ts +1 -1
- package/src/code-editor/code-editor.ts +4 -4
- package/src/code-highlighter/code-highlighter.ts +2 -2
- package/src/date-picker/date-picker.ts +5 -2
- package/src/divider/divider.ts +3 -1
- package/src/empty-state/empty-state.scss +7 -9
- package/src/empty-state/empty-state.ts +1 -1
- package/src/fab/fab-colors.scss +49 -0
- package/src/fab/fab-sizes.scss +47 -0
- package/src/fab/fab.scss +137 -0
- package/src/fab/fab.ts +285 -0
- package/src/fab/index.ts +1 -0
- package/src/field/field.ts +3 -1
- package/src/icon/datasource.ts +1 -1
- package/src/icon/icon.ts +3 -1
- package/src/image/image.ts +3 -2
- package/src/index.ts +5 -0
- package/src/input/input.ts +5 -2
- package/src/link/link.ts +2 -15
- package/src/menu/menu/menu.scss +7 -0
- package/src/menu/menu/menu.ts +7 -4
- package/src/menu/sub-menu/sub-menu.ts +1 -0
- package/src/notification/index.ts +1 -0
- package/src/notification/notification.scss +201 -0
- package/src/notification/notification.ts +206 -0
- package/src/number-counter/number-counter.ts +3 -1
- package/src/number-field/number-field.ts +4 -2
- package/src/pagination/pagination.scss +33 -24
- package/src/pagination/pagination.ts +113 -60
- package/src/peacock-loader.ts +20 -0
- package/src/radio/radio.ts +3 -1
- package/src/search/index.ts +1 -0
- package/src/search/search-colors.scss +14 -0
- package/src/search/search.scss +204 -0
- package/src/search/search.ts +240 -0
- package/src/select/option.ts +1 -1
- package/src/select/select.scss +5 -0
- package/src/select/select.ts +71 -37
- package/src/slider/slider.scss +19 -0
- package/src/slider/slider.ts +30 -19
- package/src/snackbar/snackbar.scss +62 -31
- package/src/snackbar/snackbar.ts +92 -12
- package/src/switch/switch.ts +3 -1
- package/src/table/table.ts +3 -1
- package/src/tabs/tab.ts +6 -3
- package/src/textarea/textarea.ts +4 -2
- package/src/time-picker/time-picker.ts +4 -2
- package/src/toolbar/index.ts +1 -0
- package/src/toolbar/toolbar-colors.scss +16 -0
- package/src/toolbar/toolbar.scss +165 -0
- package/src/toolbar/toolbar.ts +137 -0
- package/dist/button-COYCtuA8.js.map +0 -1
- package/dist/button-group-DsXquZQn.js.map +0 -1
- package/dist/directive-Cuw6h7YA.js +0 -9
- package/dist/directive-Cuw6h7YA.js.map +0 -1
- package/dist/dispatch-event-utils-B4odODQf.js +0 -277
- package/dist/dispatch-event-utils-B4odODQf.js.map +0 -1
- package/dist/observe-theme-change-DKAIv5BB.js.map +0 -1
- package/dist/select-C3XAzenC.js.map +0 -1
- package/dist/src/styleMixins.css.d.ts +0 -9
- package/dist/src/utils.d.ts +0 -9
- package/src/styleMixins.css.ts +0 -55
- package/src/utils.ts +0 -193
- /package/dist/src/{spread.d.ts → __directive/spread.d.ts} +0 -0
- /package/dist/src/{utils → __utils}/copy-to-clipboard.d.ts +0 -0
- /package/dist/src/{utils → __utils}/dispatch-event-utils.d.ts +0 -0
- /package/dist/src/{utils → __utils}/observe-theme-change.d.ts +0 -0
- /package/src/{spread.ts → __directive/spread.ts} +0 -0
- /package/src/{utils → __utils}/copy-to-clipboard.ts +0 -0
- /package/src/{utils → __utils}/dispatch-event-utils.ts +0 -0
- /package/src/{utils → __utils}/observe-theme-change.ts +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { LitElement, html, nothing } from 'lit';
|
|
2
|
+
import { property, query, state } from 'lit/decorators.js';
|
|
3
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
4
|
+
import { live } from 'lit/directives/live.js';
|
|
5
|
+
import IndividualComponent from '@/IndividualComponent.js';
|
|
6
|
+
import styles from './search.scss';
|
|
7
|
+
import colorStyles from './search-colors.scss';
|
|
8
|
+
import { observerSlotChangesWithCallback } from '@/__utils/observe-slot-change.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @label Search
|
|
12
|
+
* @tag wc-search
|
|
13
|
+
* @rawTag search
|
|
14
|
+
*
|
|
15
|
+
* @summary A Material 3 search bar for filtering and finding content.
|
|
16
|
+
* @overview
|
|
17
|
+
* <p>The search component provides a text input designed for search interactions.
|
|
18
|
+
* It supports outlined and filled variants, an optional clear button, and leading/trailing icon slots.</p>
|
|
19
|
+
*
|
|
20
|
+
* @cssprop --search-container-shape - Border radius of the search bar. Defaults to full (pill shape).
|
|
21
|
+
* @cssprop --search-container-color - Background color of the search container.
|
|
22
|
+
* @cssprop --search-input-text-color - Color of the input text.
|
|
23
|
+
* @cssprop --search-placeholder-color - Color of the placeholder text.
|
|
24
|
+
* @cssprop --search-icon-color - Color of the leading and trailing icons.
|
|
25
|
+
* @cssprop --search-outline-color - Border color for the outlined variant.
|
|
26
|
+
* @cssprop --search-outline-width - Border width for the outlined variant.
|
|
27
|
+
*
|
|
28
|
+
* @fires {CustomEvent} input - Dispatched when the search value changes.
|
|
29
|
+
* @fires {CustomEvent} change - Dispatched when the search input loses focus or Enter is pressed.
|
|
30
|
+
* @fires {CustomEvent} clear - Dispatched when the clear button is activated.
|
|
31
|
+
* @fires {CustomEvent} search - Dispatched when the user submits the search (presses Enter).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```html
|
|
35
|
+
* <wc-search placeholder="Search..."></wc-search>
|
|
36
|
+
* ```
|
|
37
|
+
* @tags form
|
|
38
|
+
*/
|
|
39
|
+
@IndividualComponent
|
|
40
|
+
export class Search extends LitElement {
|
|
41
|
+
static styles = [styles, colorStyles];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Visual style variant.
|
|
45
|
+
* Possible values: `"outlined"`, `"filled"`. Defaults to `"filled"`.
|
|
46
|
+
*/
|
|
47
|
+
@property({ type: String, reflect: true })
|
|
48
|
+
variant: 'outlined' | 'filled' = 'filled';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Placeholder text shown when the input is empty.
|
|
52
|
+
*/
|
|
53
|
+
@property({ type: String })
|
|
54
|
+
placeholder: string = 'Search';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Current search value.
|
|
58
|
+
*/
|
|
59
|
+
@property({ type: String })
|
|
60
|
+
value: string = '';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the search bar is disabled.
|
|
64
|
+
*/
|
|
65
|
+
@property({ type: Boolean, reflect: true })
|
|
66
|
+
disabled: boolean = false;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether a clear button is shown when the input has a value.
|
|
70
|
+
*/
|
|
71
|
+
@property({ type: Boolean })
|
|
72
|
+
clearable: boolean = true;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Size of the search bar.
|
|
76
|
+
* Possible values: `"sm"`, `"md"`, `"lg"`. Defaults to `"md"`.
|
|
77
|
+
*/
|
|
78
|
+
@property({ type: String, reflect: true })
|
|
79
|
+
size: 'sm' | 'md' | 'lg' = 'md';
|
|
80
|
+
|
|
81
|
+
@state()
|
|
82
|
+
private focused: boolean = false;
|
|
83
|
+
|
|
84
|
+
@state()
|
|
85
|
+
private leadingSlotHasContent: boolean = false;
|
|
86
|
+
|
|
87
|
+
@query('.search-input')
|
|
88
|
+
private inputElement?: HTMLInputElement;
|
|
89
|
+
|
|
90
|
+
override firstUpdated() {
|
|
91
|
+
observerSlotChangesWithCallback(
|
|
92
|
+
this.renderRoot.querySelector('slot[name="leading"]'),
|
|
93
|
+
hasContent => {
|
|
94
|
+
this.leadingSlotHasContent = hasContent;
|
|
95
|
+
this.requestUpdate();
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Focuses the internal input element. */
|
|
101
|
+
override focus() {
|
|
102
|
+
this.inputElement?.focus();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Blurs the internal input element. */
|
|
106
|
+
override blur() {
|
|
107
|
+
this.inputElement?.blur();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private __handleInput(event: InputEvent) {
|
|
111
|
+
const input = event.target as HTMLInputElement;
|
|
112
|
+
this.value = input.value;
|
|
113
|
+
this.dispatchEvent(
|
|
114
|
+
new CustomEvent('input', {
|
|
115
|
+
detail: { value: this.value },
|
|
116
|
+
bubbles: true,
|
|
117
|
+
composed: true,
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private __handleChange(event: Event) {
|
|
123
|
+
const input = event.target as HTMLInputElement;
|
|
124
|
+
this.value = input.value;
|
|
125
|
+
this.dispatchEvent(
|
|
126
|
+
new CustomEvent('change', {
|
|
127
|
+
detail: { value: this.value },
|
|
128
|
+
bubbles: true,
|
|
129
|
+
composed: true,
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private __handleKeydown(event: KeyboardEvent) {
|
|
135
|
+
if (event.key === 'Enter') {
|
|
136
|
+
this.dispatchEvent(
|
|
137
|
+
new CustomEvent('search', {
|
|
138
|
+
detail: { value: this.value },
|
|
139
|
+
bubbles: true,
|
|
140
|
+
composed: true,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (event.key === 'Escape') {
|
|
145
|
+
this.__clearValue();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private __handleFocus() {
|
|
150
|
+
this.focused = true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private __handleBlur() {
|
|
154
|
+
this.focused = false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private __clearValue() {
|
|
158
|
+
this.value = '';
|
|
159
|
+
this.inputElement?.focus();
|
|
160
|
+
this.dispatchEvent(
|
|
161
|
+
new CustomEvent('clear', {
|
|
162
|
+
bubbles: true,
|
|
163
|
+
composed: true,
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
this.dispatchEvent(
|
|
167
|
+
new CustomEvent('input', {
|
|
168
|
+
detail: { value: '' },
|
|
169
|
+
bubbles: true,
|
|
170
|
+
composed: true,
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private __renderClearButton() {
|
|
176
|
+
if (!this.clearable || !this.value) return nothing;
|
|
177
|
+
|
|
178
|
+
return html`
|
|
179
|
+
<button
|
|
180
|
+
class="clear-button"
|
|
181
|
+
aria-label="Clear search"
|
|
182
|
+
tabindex="-1"
|
|
183
|
+
@click=${this.__clearValue}
|
|
184
|
+
?disabled=${this.disabled}
|
|
185
|
+
>
|
|
186
|
+
<wc-icon name="close"></wc-icon>
|
|
187
|
+
</button>
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private __renderLeadingIcon() {
|
|
192
|
+
return html`
|
|
193
|
+
<div class="leading-icon ${this.leadingSlotHasContent ? 'has-slot' : ''}">
|
|
194
|
+
<slot name="leading">
|
|
195
|
+
<wc-icon name="search"></wc-icon>
|
|
196
|
+
</slot>
|
|
197
|
+
</div>
|
|
198
|
+
`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
override render() {
|
|
202
|
+
const cssClasses = {
|
|
203
|
+
search: true,
|
|
204
|
+
[`variant-${this.variant}`]: true,
|
|
205
|
+
[`size-${this.size}`]: true,
|
|
206
|
+
focused: this.focused,
|
|
207
|
+
disabled: this.disabled,
|
|
208
|
+
'has-value': !!this.value,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return html`
|
|
212
|
+
<div class=${classMap(cssClasses)} role="search">
|
|
213
|
+
<div class="background"></div>
|
|
214
|
+
<div class="outline"></div>
|
|
215
|
+
|
|
216
|
+
${this.__renderLeadingIcon()}
|
|
217
|
+
|
|
218
|
+
<input
|
|
219
|
+
class="search-input"
|
|
220
|
+
type="search"
|
|
221
|
+
role="searchbox"
|
|
222
|
+
.value=${live(this.value)}
|
|
223
|
+
placeholder=${this.placeholder}
|
|
224
|
+
?disabled=${this.disabled}
|
|
225
|
+
aria-label=${this.placeholder}
|
|
226
|
+
@input=${this.__handleInput}
|
|
227
|
+
@change=${this.__handleChange}
|
|
228
|
+
@keydown=${this.__handleKeydown}
|
|
229
|
+
@focus=${this.__handleFocus}
|
|
230
|
+
@blur=${this.__handleBlur}
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<div class="trailing-actions">
|
|
234
|
+
${this.__renderClearButton()}
|
|
235
|
+
<slot name="trailing"></slot>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
`;
|
|
239
|
+
}
|
|
240
|
+
}
|
package/src/select/option.ts
CHANGED
|
@@ -96,7 +96,7 @@ export class SelectOptionElement extends LitElement {
|
|
|
96
96
|
${this.icon
|
|
97
97
|
? html`<wc-icon name=${this.icon} slot="leading-icon"></wc-icon>`
|
|
98
98
|
: nothing}
|
|
99
|
-
<slot
|
|
99
|
+
<slot>${this.value === '' ? 'None' : ''}</slot>
|
|
100
100
|
${this.selected && this.keepOpen
|
|
101
101
|
? html`<wc-icon
|
|
102
102
|
name="check"
|
package/src/select/select.scss
CHANGED
package/src/select/select.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface SelectOption {
|
|
|
38
38
|
export class Select extends BaseInput {
|
|
39
39
|
static styles = [styles];
|
|
40
40
|
|
|
41
|
+
private readonly _menuId = `wc-select-menu-${Math.random().toString(36).slice(2, 9)}`;
|
|
42
|
+
|
|
41
43
|
/**
|
|
42
44
|
* Array of options to display in the dropdown.
|
|
43
45
|
* Setting this property creates matching `<wc-option>` children automatically.
|
|
@@ -77,12 +79,6 @@ export class Select extends BaseInput {
|
|
|
77
79
|
@property({ type: String })
|
|
78
80
|
label: string = '';
|
|
79
81
|
|
|
80
|
-
/**
|
|
81
|
-
* Show a clear button in single-select mode when a value is selected.
|
|
82
|
-
*/
|
|
83
|
-
@property({ type: Boolean })
|
|
84
|
-
clearable: boolean = false;
|
|
85
|
-
|
|
86
82
|
/**
|
|
87
83
|
* Visual variant of the field.
|
|
88
84
|
*/
|
|
@@ -160,7 +156,7 @@ export class Select extends BaseInput {
|
|
|
160
156
|
const el = new SelectOptionElement();
|
|
161
157
|
el.value = opt.value;
|
|
162
158
|
if (opt.icon) el.icon = opt.icon;
|
|
163
|
-
el.textContent = opt.label;
|
|
159
|
+
el.textContent = opt.label || (opt.value === '' ? 'None' : '');
|
|
164
160
|
el.dataset.generated = '';
|
|
165
161
|
this.appendChild(el);
|
|
166
162
|
}
|
|
@@ -184,13 +180,13 @@ export class Select extends BaseInput {
|
|
|
184
180
|
const q = this._searchQuery.toLowerCase();
|
|
185
181
|
const label = opt.textContent?.trim() ?? '';
|
|
186
182
|
opt.filtered = !label.toLowerCase().includes(q);
|
|
187
|
-
if (!opt.filtered) visibleCount
|
|
183
|
+
if (!opt.filtered) visibleCount += 1;
|
|
188
184
|
} else {
|
|
189
185
|
opt.filtered = false;
|
|
190
|
-
visibleCount
|
|
186
|
+
visibleCount += 1;
|
|
191
187
|
}
|
|
192
188
|
}
|
|
193
|
-
this._noOptionsVisible =
|
|
189
|
+
this._noOptionsVisible = visibleCount === 0;
|
|
194
190
|
}
|
|
195
191
|
|
|
196
192
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
@@ -204,16 +200,25 @@ export class Select extends BaseInput {
|
|
|
204
200
|
}
|
|
205
201
|
|
|
206
202
|
private _isSelected(value: string): boolean {
|
|
203
|
+
if (!this.multiple) {
|
|
204
|
+
return this.value === value;
|
|
205
|
+
}
|
|
207
206
|
return this._selectedValues.includes(value);
|
|
208
207
|
}
|
|
209
208
|
|
|
210
209
|
/** Returns the display label for a given option value. */
|
|
211
210
|
private _getLabelForValue(val: string): string {
|
|
212
211
|
for (const opt of this.querySelectorAll<SelectOptionElement>('wc-option')) {
|
|
213
|
-
if (opt.value === val)
|
|
212
|
+
if (opt.value === val) {
|
|
213
|
+
const label = opt.textContent?.trim();
|
|
214
|
+
if (label) return label;
|
|
215
|
+
return val === '' ? 'None' : val;
|
|
216
|
+
}
|
|
214
217
|
}
|
|
215
218
|
// Fallback to options array (before wc-option children are created)
|
|
216
|
-
|
|
219
|
+
const programmaticLabel = this.options.find(o => o.value === val)?.label;
|
|
220
|
+
if (programmaticLabel) return programmaticLabel;
|
|
221
|
+
return val === '' ? 'None' : val;
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
private get _displayLabel(): string {
|
|
@@ -233,9 +238,16 @@ export class Select extends BaseInput {
|
|
|
233
238
|
if (this.disabled || this.readonly) return;
|
|
234
239
|
this._open = true;
|
|
235
240
|
this._focused = true;
|
|
241
|
+
this._triggerEl?.focus();
|
|
236
242
|
const menu = this._menu;
|
|
237
243
|
if (menu && this._triggerEl) {
|
|
238
244
|
menu.anchorElement = this._triggerEl;
|
|
245
|
+
const triggerWidth = this._triggerEl.getBoundingClientRect().width;
|
|
246
|
+
if (triggerWidth < 240) {
|
|
247
|
+
menu.style.setProperty('--menu-width', '240px');
|
|
248
|
+
} else {
|
|
249
|
+
menu.style.setProperty('--menu-width', `${Math.ceil(triggerWidth)}px`);
|
|
250
|
+
}
|
|
239
251
|
menu.show();
|
|
240
252
|
}
|
|
241
253
|
if (this.search) {
|
|
@@ -261,6 +273,7 @@ export class Select extends BaseInput {
|
|
|
261
273
|
// ── Event handlers ─────────────────────────────────────────────────────────
|
|
262
274
|
|
|
263
275
|
private _handleTriggerClick(event: MouseEvent) {
|
|
276
|
+
event.stopPropagation();
|
|
264
277
|
// Ignore clicks that originated inside the search input — those should not
|
|
265
278
|
// toggle the menu (the input needs to stay open so the user can type).
|
|
266
279
|
if (event.target instanceof HTMLInputElement) {
|
|
@@ -273,6 +286,29 @@ export class Select extends BaseInput {
|
|
|
273
286
|
}
|
|
274
287
|
}
|
|
275
288
|
|
|
289
|
+
private _handleFieldClick(event: MouseEvent) {
|
|
290
|
+
const eventPath = event.composedPath();
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
eventPath.includes(this._triggerEl as EventTarget) ||
|
|
294
|
+
eventPath.some(
|
|
295
|
+
target =>
|
|
296
|
+
target instanceof HTMLInputElement ||
|
|
297
|
+
(target instanceof HTMLElement &&
|
|
298
|
+
(target.closest('.clear-btn') != null ||
|
|
299
|
+
target.matches('wc-icon-button'))),
|
|
300
|
+
)
|
|
301
|
+
) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (this._open) {
|
|
306
|
+
this._closeMenu();
|
|
307
|
+
} else {
|
|
308
|
+
this._openMenu();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
276
312
|
private _handleTriggerKeyDown(event: KeyboardEvent) {
|
|
277
313
|
// When the typeahead search input is active, let the input handle its own
|
|
278
314
|
// keys (Space, Enter, etc.). Only intercept Escape to close the menu.
|
|
@@ -312,9 +348,11 @@ export class Select extends BaseInput {
|
|
|
312
348
|
if (!item) return;
|
|
313
349
|
|
|
314
350
|
const val = item.value;
|
|
315
|
-
|
|
351
|
+
|
|
352
|
+
if (val === undefined) return;
|
|
316
353
|
|
|
317
354
|
if (this.multiple) {
|
|
355
|
+
if (val === '') return;
|
|
318
356
|
const values = this._selectedValues;
|
|
319
357
|
const idx = values.indexOf(val);
|
|
320
358
|
if (idx >= 0) {
|
|
@@ -354,12 +392,6 @@ export class Select extends BaseInput {
|
|
|
354
392
|
}
|
|
355
393
|
}
|
|
356
394
|
|
|
357
|
-
private _handleClear(event: MouseEvent) {
|
|
358
|
-
event.stopPropagation();
|
|
359
|
-
this.value = '';
|
|
360
|
-
this._dispatchChange();
|
|
361
|
-
}
|
|
362
|
-
|
|
363
395
|
private _handleChipDismiss(event: CustomEvent, chipValue: string) {
|
|
364
396
|
event.stopPropagation();
|
|
365
397
|
const values = this._selectedValues.filter(v => v !== chipValue);
|
|
@@ -367,6 +399,21 @@ export class Select extends BaseInput {
|
|
|
367
399
|
this._dispatchChange();
|
|
368
400
|
}
|
|
369
401
|
|
|
402
|
+
private _renderEmptyState() {
|
|
403
|
+
const hasSearchQuery = this._searchQuery.trim().length > 0;
|
|
404
|
+
|
|
405
|
+
return html`
|
|
406
|
+
<wc-empty-state
|
|
407
|
+
class="select-empty-state content-center"
|
|
408
|
+
illustration="no-document"
|
|
409
|
+
headline=${hasSearchQuery ? 'No results found' : 'No options available'}
|
|
410
|
+
description=${hasSearchQuery
|
|
411
|
+
? 'Try a different search term.'
|
|
412
|
+
: 'There is nothing to select right now.'}
|
|
413
|
+
></wc-empty-state>
|
|
414
|
+
`;
|
|
415
|
+
}
|
|
416
|
+
|
|
370
417
|
// ── Render helpers ─────────────────────────────────────────────────────────
|
|
371
418
|
|
|
372
419
|
private _renderTriggerContent() {
|
|
@@ -406,22 +453,7 @@ export class Select extends BaseInput {
|
|
|
406
453
|
}
|
|
407
454
|
|
|
408
455
|
private _renderFieldEnd() {
|
|
409
|
-
const showClear =
|
|
410
|
-
this.clearable &&
|
|
411
|
-
!this.multiple &&
|
|
412
|
-
!!this.value &&
|
|
413
|
-
!this.disabled &&
|
|
414
|
-
!this.readonly;
|
|
415
456
|
return html`
|
|
416
|
-
${showClear
|
|
417
|
-
? html`<wc-icon-button
|
|
418
|
-
class="clear-btn"
|
|
419
|
-
variant="text"
|
|
420
|
-
size="sm"
|
|
421
|
-
name="close"
|
|
422
|
-
@click=${this._handleClear}
|
|
423
|
-
></wc-icon-button>`
|
|
424
|
-
: nothing}
|
|
425
457
|
<wc-icon
|
|
426
458
|
class=${classMap({
|
|
427
459
|
'dropdown-icon': true,
|
|
@@ -450,11 +482,14 @@ export class Select extends BaseInput {
|
|
|
450
482
|
?focused=${this._focused}
|
|
451
483
|
.host=${this}
|
|
452
484
|
class="select-field"
|
|
485
|
+
@click=${this._handleFieldClick}
|
|
453
486
|
>
|
|
454
487
|
<div
|
|
455
488
|
class="select-trigger"
|
|
456
489
|
tabindex=${this.disabled ? -1 : 0}
|
|
457
490
|
role="combobox"
|
|
491
|
+
aria-label=${this.label || this.placeholder || 'Select option'}
|
|
492
|
+
aria-controls=${this._menuId}
|
|
458
493
|
aria-expanded=${String(this._open)}
|
|
459
494
|
aria-haspopup="listbox"
|
|
460
495
|
@click=${this._handleTriggerClick}
|
|
@@ -469,6 +504,7 @@ export class Select extends BaseInput {
|
|
|
469
504
|
</wc-field>
|
|
470
505
|
|
|
471
506
|
<wc-menu
|
|
507
|
+
id=${this._menuId}
|
|
472
508
|
placement="bottom-start"
|
|
473
509
|
aria-label=${this.label || 'Options'}
|
|
474
510
|
@closed=${this._handleMenuClosed}
|
|
@@ -476,9 +512,7 @@ export class Select extends BaseInput {
|
|
|
476
512
|
this._handleMenuItemActivate(e)}
|
|
477
513
|
>
|
|
478
514
|
<slot></slot>
|
|
479
|
-
${this._noOptionsVisible
|
|
480
|
-
? html`<wc-menu-item disabled>No options</wc-menu-item>`
|
|
481
|
-
: nothing}
|
|
515
|
+
${this._noOptionsVisible ? this._renderEmptyState() : nothing}
|
|
482
516
|
</wc-menu>
|
|
483
517
|
`;
|
|
484
518
|
}
|
package/src/slider/slider.scss
CHANGED
|
@@ -15,6 +15,17 @@
|
|
|
15
15
|
touch-action: none; // Prevent scrolling while dragging
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
.slider {
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
gap: var(--spacing-100, 0.5rem);
|
|
22
|
+
width: 100%;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.slider.with-value .slider-container {
|
|
26
|
+
flex: 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
.slider-container {
|
|
19
30
|
position: relative;
|
|
20
31
|
display: flex;
|
|
@@ -29,6 +40,14 @@
|
|
|
29
40
|
}
|
|
30
41
|
}
|
|
31
42
|
|
|
43
|
+
.value-display {
|
|
44
|
+
min-width: 2.25rem;
|
|
45
|
+
text-align: end;
|
|
46
|
+
color: var(--color-on-surface-variant);
|
|
47
|
+
font-size: 0.875rem;
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
.track {
|
|
33
52
|
position: absolute;
|
|
34
53
|
width: 100%;
|
package/src/slider/slider.ts
CHANGED
|
@@ -51,9 +51,15 @@ export class Slider extends LitElement {
|
|
|
51
51
|
*/
|
|
52
52
|
@property({ type: Boolean }) labeled = true;
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Whether to show the current value beside the slider.
|
|
56
|
+
*/
|
|
57
|
+
@property({ type: Boolean, attribute: 'show-value' }) showValue = false;
|
|
58
|
+
|
|
54
59
|
@state() private isDragging = false;
|
|
55
60
|
|
|
56
61
|
@query('.slider-container') private container!: HTMLElement;
|
|
62
|
+
|
|
57
63
|
@query('.thumb') private thumbElement!: HTMLElement;
|
|
58
64
|
|
|
59
65
|
private handleInput(event: MouseEvent | TouchEvent) {
|
|
@@ -150,28 +156,33 @@ export class Slider extends LitElement {
|
|
|
150
156
|
const percentage = ((this.value - this.min) / (this.max - this.min)) * 100;
|
|
151
157
|
|
|
152
158
|
return html`
|
|
153
|
-
<div
|
|
154
|
-
class="slider-container ${this.disabled ? 'disabled' : ''}"
|
|
155
|
-
@mousedown=${this.onMouseDown}
|
|
156
|
-
@touchstart=${this.onMouseDown}
|
|
157
|
-
>
|
|
158
|
-
<div class="track">
|
|
159
|
-
<div class="track-active" style=${styleMap({ width: `${percentage}%` })}></div>
|
|
160
|
-
</div>
|
|
161
|
-
|
|
159
|
+
<div class="slider ${this.showValue ? 'with-value' : ''}">
|
|
162
160
|
<div
|
|
163
|
-
class="
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
aria-valuemin=${this.min}
|
|
167
|
-
aria-valuemax=${this.max}
|
|
168
|
-
aria-valuenow=${this.value}
|
|
169
|
-
aria-disabled=${this.disabled}
|
|
170
|
-
style=${styleMap({ left: `calc(${percentage}% - var(--thumb-half))` })}
|
|
171
|
-
@keydown=${this.handleKeyDown}
|
|
161
|
+
class="slider-container ${this.disabled ? 'disabled' : ''}"
|
|
162
|
+
@mousedown=${this.onMouseDown}
|
|
163
|
+
@touchstart=${this.onMouseDown}
|
|
172
164
|
>
|
|
173
|
-
|
|
165
|
+
<div class="track">
|
|
166
|
+
<div class="track-active" style=${styleMap({ width: `${percentage}%` })}></div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div
|
|
170
|
+
class="thumb"
|
|
171
|
+
role="slider"
|
|
172
|
+
aria-label="Slider"
|
|
173
|
+
tabindex="${this.disabled ? -1 : 0}"
|
|
174
|
+
aria-valuemin=${this.min}
|
|
175
|
+
aria-valuemax=${this.max}
|
|
176
|
+
aria-valuenow=${this.value}
|
|
177
|
+
aria-disabled=${this.disabled}
|
|
178
|
+
style=${styleMap({ left: `calc(${percentage}% - var(--thumb-half))` })}
|
|
179
|
+
@keydown=${this.handleKeyDown}
|
|
180
|
+
>
|
|
181
|
+
${this.labeled ? html`<div class="value-label">${this.value}</div>` : ''}
|
|
182
|
+
</div>
|
|
174
183
|
</div>
|
|
184
|
+
|
|
185
|
+
${this.showValue ? html`<output class="value-display" aria-live="polite">${this.value}</output>` : ''}
|
|
175
186
|
</div>
|
|
176
187
|
`;
|
|
177
188
|
}
|