@crowdstrike/glide-core 0.29.2 → 0.30.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/accordion.js +240 -1
- package/dist/accordion.styles.js +13 -7
- package/dist/button-group.button.js +143 -1
- package/dist/button-group.button.styles.js +43 -15
- package/dist/button-group.js +249 -1
- package/dist/button-group.styles.js +10 -5
- package/dist/button.js +206 -1
- package/dist/button.styles.js +12 -7
- package/dist/checkbox-group.js +479 -14
- package/dist/checkbox-group.styles.js +5 -2
- package/dist/checkbox.js +519 -32
- package/dist/checkbox.styles.js +10 -5
- package/dist/drawer.js +168 -1
- package/dist/drawer.styles.js +5 -2
- package/dist/dropdown.js +2423 -123
- package/dist/dropdown.option.js +536 -1
- package/dist/dropdown.option.styles.js +5 -2
- package/dist/dropdown.styles.js +14 -7
- package/dist/form-controls-layout.js +102 -1
- package/dist/form-controls-layout.styles.js +5 -2
- package/dist/icon-button.js +139 -1
- package/dist/icon-button.styles.js +19 -7
- package/dist/icons/checked.js +28 -1
- package/dist/icons/chevron.js +21 -1
- package/dist/icons/magnifying-glass.js +23 -1
- package/dist/icons/pencil.js +21 -1
- package/dist/icons/severity-critical.js +20 -1
- package/dist/icons/severity-informational.js +20 -1
- package/dist/icons/severity-medium.js +20 -1
- package/dist/icons/x.js +21 -1
- package/dist/inline-alert.js +118 -1
- package/dist/inline-alert.styles.js +5 -2
- package/dist/input.d.ts +8 -2
- package/dist/input.js +505 -41
- package/dist/input.styles.js +25 -4
- package/dist/label.js +303 -1
- package/dist/label.styles.js +11 -5
- package/dist/library/assert-slot.js +136 -1
- package/dist/library/expect-unhandled-rejection.js +14 -1
- package/dist/library/expect-window-error.js +26 -1
- package/dist/library/final.js +18 -1
- package/dist/library/form-control.js +1 -1
- package/dist/library/localize.js +10 -1
- package/dist/library/mouse.js +35 -1
- package/dist/library/on-resize.js +24 -1
- package/dist/library/required.js +35 -1
- package/dist/library/shadow-root-mode.js +4 -1
- package/dist/library/unique-id.js +3 -1
- package/dist/link.js +92 -1
- package/dist/link.styles.js +10 -5
- package/dist/menu.d.ts +3 -2
- package/dist/menu.js +1259 -1
- package/dist/menu.styles.js +34 -17
- package/dist/modal.d.ts +4 -0
- package/dist/modal.icon-button.js +60 -1
- package/dist/modal.icon-button.styles.js +5 -2
- package/dist/modal.js +473 -1
- package/dist/modal.styles.js +71 -22
- package/dist/option.d.ts +74 -0
- package/dist/option.js +498 -0
- package/dist/option.styles.js +140 -0
- package/dist/{menu.options.d.ts → options.d.ts} +5 -6
- package/dist/options.js +130 -0
- package/dist/options.styles.js +21 -0
- package/dist/popover.js +620 -1
- package/dist/popover.styles.js +11 -5
- package/dist/radio-group.js +624 -17
- package/dist/radio-group.radio.js +211 -1
- package/dist/radio-group.radio.styles.js +9 -4
- package/dist/radio-group.styles.js +5 -2
- package/dist/slider.js +1040 -61
- package/dist/slider.styles.js +9 -4
- package/dist/spinner.js +60 -1
- package/dist/spinner.styles.js +5 -2
- package/dist/split-button.js +116 -1
- package/dist/split-button.primary-button.js +100 -1
- package/dist/split-button.primary-button.styles.js +13 -6
- package/dist/split-button.primary-link.js +102 -1
- package/dist/split-button.secondary-button.d.ts +2 -3
- package/dist/split-button.secondary-button.js +121 -1
- package/dist/split-button.secondary-button.styles.js +12 -7
- package/dist/split-button.styles.js +9 -4
- package/dist/styles/focus-outline.js +9 -3
- package/dist/styles/fonts.css +6 -1
- package/dist/styles/opacity-and-scale-animation.js +6 -3
- package/dist/styles/skeleton.js +6 -3
- package/dist/styles/variables.css +410 -1
- package/dist/styles/visually-hidden.js +6 -3
- package/dist/tab.group.js +386 -1
- package/dist/tab.group.styles.js +5 -2
- package/dist/tab.js +133 -1
- package/dist/tab.panel.js +93 -1
- package/dist/tab.panel.styles.js +11 -5
- package/dist/tab.styles.js +9 -4
- package/dist/tag.js +207 -1
- package/dist/tag.styles.js +10 -5
- package/dist/textarea.js +353 -19
- package/dist/textarea.styles.js +23 -4
- package/dist/toast.js +130 -1
- package/dist/toast.toasts.js +248 -25
- package/dist/toast.toasts.styles.js +9 -4
- package/dist/toggle.js +178 -1
- package/dist/toggle.styles.js +25 -5
- package/dist/tooltip.container.js +130 -1
- package/dist/tooltip.container.styles.js +5 -2
- package/dist/tooltip.js +484 -1
- package/dist/tooltip.styles.js +21 -5
- package/dist/translations/en.js +36 -1
- package/dist/translations/fr.js +37 -1
- package/dist/translations/ja.js +37 -1
- package/package.json +8 -12
- package/dist/menu.button.d.ts +0 -42
- package/dist/menu.button.js +0 -1
- package/dist/menu.button.styles.js +0 -32
- package/dist/menu.link.d.ts +0 -44
- package/dist/menu.link.js +0 -1
- package/dist/menu.link.styles.js +0 -35
- package/dist/menu.options.js +0 -1
- package/dist/menu.options.styles.d.ts +0 -2
- package/dist/menu.options.styles.js +0 -20
- /package/dist/{menu.button.styles.d.ts → option.styles.d.ts} +0 -0
- /package/dist/{menu.link.styles.d.ts → options.styles.d.ts} +0 -0
package/dist/slider.js
CHANGED
@@ -1,114 +1,523 @@
|
|
1
|
-
var __decorate
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
|
+
};
|
7
|
+
import './label.js';
|
8
|
+
import { html, LitElement } from 'lit';
|
9
|
+
import { classMap } from 'lit/directives/class-map.js';
|
10
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
11
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
12
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
13
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
14
|
+
import { when } from 'lit/directives/when.js';
|
15
|
+
import packageJson from '../package.json' with { type: 'json' };
|
16
|
+
import { LocalizeController } from './library/localize.js';
|
17
|
+
import styles from './slider.styles.js';
|
18
|
+
import shadowRootMode from './library/shadow-root-mode.js';
|
19
|
+
import final from './library/final.js';
|
20
|
+
import required from './library/required.js';
|
21
|
+
/**
|
22
|
+
* @attr {string} label
|
23
|
+
* @attr {boolean} [disabled=false]
|
24
|
+
* @attr {boolean} [hide-label=false]
|
25
|
+
* @attr {number} [max=100]
|
26
|
+
* @attr {number} [min=0]
|
27
|
+
* @attr {boolean} [multiple=false]
|
28
|
+
* @attr {string} [name='']
|
29
|
+
* @attr {'horizontal'|'vertical'} [orientation='horizontal']
|
30
|
+
* @attr {boolean} [readonly=false]
|
31
|
+
* @attr {boolean} [required=false]
|
32
|
+
* @attr {number} [step=1]
|
33
|
+
* @attr {string} [tooltip]
|
34
|
+
* @attr {number[]} [value=[]]
|
35
|
+
*
|
36
|
+
* @readonly
|
37
|
+
* @attr {string} [version]
|
38
|
+
*
|
39
|
+
* @slot {Element | string} [description] - Additional information or context
|
40
|
+
*
|
41
|
+
* @fires {Event} change
|
42
|
+
* @fires {Event} input
|
43
|
+
* @fires {Event} invalid
|
44
|
+
*
|
45
|
+
* @readonly
|
46
|
+
* @prop {HTMLFormElement | null} form
|
47
|
+
*
|
48
|
+
* @readonly
|
49
|
+
* @prop {ValidityState} validity
|
50
|
+
*
|
51
|
+
* @method checkValidity
|
52
|
+
* @returns boolean
|
53
|
+
*
|
54
|
+
* @method formAssociatedCallback
|
55
|
+
* @method formResetCallback
|
56
|
+
*
|
57
|
+
* @method reportValidity
|
58
|
+
* @returns boolean
|
59
|
+
*
|
60
|
+
* @method resetValidityFeedback
|
61
|
+
*
|
62
|
+
* @method setCustomValidity
|
63
|
+
* @param {string} message
|
64
|
+
*
|
65
|
+
* @method setValidity
|
66
|
+
* @param {ValidityStateFlags} [flags]
|
67
|
+
* @param {string} [message]
|
68
|
+
*/
|
69
|
+
let Slider = class Slider extends LitElement {
|
70
|
+
static { this.formAssociated = true; }
|
71
|
+
static { this.shadowRootOptions = {
|
72
|
+
...LitElement.shadowRootOptions,
|
73
|
+
mode: shadowRootMode,
|
74
|
+
delegatesFocus: true,
|
75
|
+
}; }
|
76
|
+
static { this.styles = styles; }
|
77
|
+
// Intentionally not reflected to match native.
|
78
|
+
/**
|
79
|
+
* @default []
|
80
|
+
*/
|
81
|
+
get value() {
|
82
|
+
if (this.multiple &&
|
83
|
+
this.minimumValue !== undefined &&
|
84
|
+
this.maximumValue !== undefined) {
|
85
|
+
return [this.minimumValue, this.maximumValue];
|
86
|
+
}
|
87
|
+
if (this.minimumValue !== undefined) {
|
88
|
+
return [this.minimumValue];
|
89
|
+
}
|
90
|
+
return [];
|
91
|
+
}
|
92
|
+
set value(value) {
|
93
|
+
if (value.length === 0) {
|
94
|
+
const rangeSize = this.max - this.min;
|
95
|
+
// To match native, when the value is emptied, we create one.
|
96
|
+
// Native sets it to 50% of the max, but we have a design
|
97
|
+
// requirement for a 25/75% split of the range size.
|
98
|
+
this.minimumValue = this.min + Math.floor(rangeSize * 0.25);
|
99
|
+
this.maximumValue = this.multiple
|
100
|
+
? this.min + Math.ceil(rangeSize * 0.75)
|
101
|
+
: undefined;
|
102
|
+
this.#updateHandlesAndTrack();
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
if (this.multiple &&
|
106
|
+
value.length === 2 &&
|
107
|
+
value[0] !== undefined &&
|
108
|
+
value[1] !== undefined) {
|
109
|
+
if (value[0] > value[1]) {
|
110
|
+
throw new Error('The first value must be less than the second.');
|
111
|
+
}
|
112
|
+
// Normalize values to snap to the closest valid step
|
113
|
+
// increment, even if a developer sets a value between
|
114
|
+
// steps.
|
115
|
+
//
|
116
|
+
// Doing so creates consistent behavior between programmatic
|
117
|
+
// updates and user interactions. Users can select values that
|
118
|
+
// align with steps when dragging the handles or when entering
|
119
|
+
// values into the inputs. This ensures programmatic updates
|
120
|
+
// follow the same rules.
|
121
|
+
//
|
122
|
+
// It also prevents the Slider from ending up in a state where
|
123
|
+
// the position visually doesn't match where a user would
|
124
|
+
// expect based on the step configuration.
|
125
|
+
const normalizedMinimum = Math.round(value[0] / this.step) * this.step;
|
126
|
+
const normalizedMaximum = Math.round(value[1] / this.step) * this.step;
|
127
|
+
// Clamp the normalized values to the allowed ranges.
|
128
|
+
this.minimumValue = Math.max(normalizedMinimum, this.min);
|
129
|
+
this.maximumValue = Math.min(normalizedMaximum, this.max);
|
130
|
+
this.#updateHandlesAndTrack();
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
if (this.multiple && value.length > 2) {
|
134
|
+
throw new Error('Only two values are allowed when `multiple`.');
|
135
|
+
}
|
136
|
+
if (!this.multiple && value.length > 0 && value[0] !== undefined) {
|
137
|
+
// Normalize the value to snap to the closest valid step
|
138
|
+
// increment, even if a developer sets a value between steps.
|
139
|
+
//
|
140
|
+
// Doing so creates consistent behavior between programmatic
|
141
|
+
// updates and user interactions. Users can select values that
|
142
|
+
// align with step when dragging the handle or when entering
|
143
|
+
// values into the input. This ensures programmatic updates
|
144
|
+
// follow the same rules.
|
145
|
+
//
|
146
|
+
// It also prevents the Slider from ending up in a state where
|
147
|
+
// the position visually doesn't match where a user would
|
148
|
+
// expect based on the step configuration.
|
149
|
+
const normalizedValue = Math.round(value[0] / this.step) * this.step;
|
150
|
+
// Clamp the normalized value to the allowed range.
|
151
|
+
this.minimumValue = Math.max(Math.min(normalizedValue, this.max), this.min);
|
152
|
+
// When not in multiple mode, we clear the maximumValue to
|
153
|
+
// ensure the Slider won't get in an odd state as the
|
154
|
+
// minimumValue is adjusted. If a consumers add the multiple
|
155
|
+
// attribute, we recalculate what the maximumValue should be
|
156
|
+
// based on the current position of the minimum handle with
|
157
|
+
// respect to max and step.
|
158
|
+
this.maximumValue = undefined;
|
159
|
+
this.#updateHandlesAndTrack();
|
160
|
+
return;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
/**
|
164
|
+
* @default 100
|
165
|
+
*/
|
166
|
+
get max() {
|
167
|
+
return this.#max;
|
168
|
+
}
|
169
|
+
set max(max) {
|
170
|
+
this.#max = max;
|
171
|
+
this.maximumValue = Math.min(this.maximumValue ?? this.#max, max);
|
172
|
+
// Prevents Slider from reaching an unusable state when max
|
173
|
+
// changes.
|
174
|
+
//
|
175
|
+
// If max decreases below minimumValue, we need to adjust the
|
176
|
+
// value to maintain usability. Without this adjustment, the
|
177
|
+
// handle could become stuck beyond the visible track, leaving
|
178
|
+
// users unable to interact with it.
|
179
|
+
//
|
180
|
+
// Setting minimumValue maintains functionality to respect the
|
181
|
+
// relationship between handles in multiple mode. In single mode,
|
182
|
+
// it ensures Slider remains accessible within the valid range.
|
183
|
+
if (this.minimumValue !== undefined && this.minimumValue > max) {
|
184
|
+
this.minimumValue =
|
185
|
+
this.multiple && this.maximumValue !== undefined
|
186
|
+
? Math.min(max, this.maximumValue - this.step)
|
187
|
+
: max;
|
188
|
+
}
|
189
|
+
this.#updateHandlesAndTrack();
|
190
|
+
}
|
191
|
+
/**
|
192
|
+
* @default 0
|
193
|
+
*/
|
194
|
+
get min() {
|
195
|
+
return this.#min;
|
196
|
+
}
|
197
|
+
set min(min) {
|
198
|
+
this.#min = min;
|
199
|
+
this.minimumValue = Math.max(this.minimumValue ?? this.#min, min);
|
200
|
+
if (this.multiple) {
|
201
|
+
// Prevents the multi-handle Slider from becoming unusable
|
202
|
+
// when min increases.
|
203
|
+
//
|
204
|
+
// If min increases above the maximumValue, the maximum handle
|
205
|
+
// would become inaccessible and be outside of the visible
|
206
|
+
// track. This adjustment ensures both handles remain
|
207
|
+
// functional within the valid range while maintaining proper
|
208
|
+
// step separation between them.
|
209
|
+
if (this.maximumValue !== undefined && this.maximumValue < min) {
|
210
|
+
this.maximumValue = Math.max(min, this.minimumValue + this.step);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
else {
|
214
|
+
// In single mode, we intentionally clear maximumValue when
|
215
|
+
// min changes to maintain component state consistency and
|
216
|
+
// prevent edge cases where maximumValue could unexpectedly
|
217
|
+
// influence Slider's behavior when switching between single
|
218
|
+
// and multiple modes.
|
219
|
+
this.maximumValue = undefined;
|
220
|
+
}
|
221
|
+
this.#updateHandlesAndTrack();
|
222
|
+
}
|
223
|
+
/**
|
224
|
+
* @default false
|
225
|
+
*/
|
226
|
+
get multiple() {
|
227
|
+
return this.#multiple;
|
228
|
+
}
|
229
|
+
set multiple(multiple) {
|
230
|
+
const oldValue = this.#multiple;
|
231
|
+
this.#multiple = multiple;
|
232
|
+
if (oldValue && !multiple) {
|
233
|
+
// Similar to the min setter above, clear out maximumValue to
|
234
|
+
// prevent state inconsistencies and UX issues when switching
|
235
|
+
// from multiple to single.
|
236
|
+
this.maximumValue = undefined;
|
237
|
+
this.updateComplete.then(() => {
|
238
|
+
this.#updateHandlesAndTrack();
|
239
|
+
});
|
240
|
+
return;
|
241
|
+
}
|
242
|
+
if (!oldValue && multiple && this.minimumValue !== undefined) {
|
243
|
+
const rangeSize = this.max - this.min;
|
244
|
+
// Design calls for positioning the maximum handle at 75% of
|
245
|
+
// the range size.
|
246
|
+
const desiredMaximumValue = this.min + Math.ceil(rangeSize * 0.75);
|
247
|
+
// There may be cases where the current minimum value now
|
248
|
+
// exceeds the 75% range size threshold. We attempt to account
|
249
|
+
// for this case by maxing out maximumValue.
|
250
|
+
if (this.minimumValue >= desiredMaximumValue) {
|
251
|
+
this.maximumValue = this.max;
|
252
|
+
// If the minimum value is still larger, we lower it to help
|
253
|
+
// prevent the component from getting into an invalid state.
|
254
|
+
// This is hopefully an edge case, but one we should account
|
255
|
+
// for.
|
256
|
+
if (this.minimumValue >= this.maximumValue) {
|
257
|
+
this.minimumValue = this.maximumValue - this.step;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
else {
|
261
|
+
this.maximumValue = desiredMaximumValue;
|
262
|
+
}
|
263
|
+
this.updateComplete.then(() => {
|
264
|
+
this.#updateHandlesAndTrack();
|
265
|
+
});
|
266
|
+
}
|
267
|
+
}
|
268
|
+
/**
|
269
|
+
* @default 1
|
270
|
+
*/
|
271
|
+
get step() {
|
272
|
+
return this.#step;
|
273
|
+
}
|
274
|
+
set step(step) {
|
275
|
+
this.#step = step > 0 ? step : 1;
|
276
|
+
}
|
277
|
+
get form() {
|
278
|
+
return this.#internals.form;
|
279
|
+
}
|
280
|
+
get validity() {
|
281
|
+
return this.#internals.validity;
|
282
|
+
}
|
283
|
+
checkValidity() {
|
284
|
+
this.isCheckingValidity = true;
|
285
|
+
const isValid = this.#internals.checkValidity();
|
286
|
+
this.isCheckingValidity = false;
|
287
|
+
return isValid;
|
288
|
+
}
|
289
|
+
disconnectedCallback() {
|
290
|
+
super.disconnectedCallback();
|
291
|
+
this.form?.removeEventListener('formdata', this.#onFormdata);
|
292
|
+
document.removeEventListener('mousemove', this.#onDraggingMousemove);
|
293
|
+
document.removeEventListener('mouseup', this.#onDraggingMouseup);
|
294
|
+
}
|
295
|
+
firstUpdated() {
|
296
|
+
if (!this.multiple && this.value.length === 1) {
|
297
|
+
this.minimumValue = this.value.at(0);
|
298
|
+
this.#initialValue = this.value;
|
299
|
+
this.#updateHandlesAndTrack();
|
300
|
+
return;
|
301
|
+
}
|
302
|
+
if (this.multiple && this.value.length === 2) {
|
303
|
+
this.minimumValue = this.value.at(0);
|
304
|
+
this.maximumValue = this.value.at(1);
|
305
|
+
if (this.minimumValue !== undefined && this.maximumValue !== undefined) {
|
306
|
+
this.#initialValue = [this.minimumValue, this.maximumValue];
|
307
|
+
}
|
308
|
+
this.#updateHandlesAndTrack();
|
309
|
+
return;
|
310
|
+
}
|
311
|
+
if (!this.value || this.value.length === 0) {
|
312
|
+
const rangeSize = this.max - this.min;
|
313
|
+
// When the native range input is not provided a value,
|
314
|
+
// it defaults value to 50% of the max. Our design
|
315
|
+
// requirements are different, in that they want the single
|
316
|
+
// slider or minimum handle in multiple mode to be at 25% of
|
317
|
+
// the range size, and the maximum handle in multiple mode to
|
318
|
+
// be at 75% of the range size.
|
319
|
+
this.minimumValue = this.min + Math.floor(rangeSize * 0.25);
|
320
|
+
this.maximumValue = this.multiple
|
321
|
+
? this.min + Math.ceil(rangeSize * 0.75)
|
322
|
+
: undefined;
|
323
|
+
this.#initialValue =
|
324
|
+
this.multiple && this.maximumValue !== undefined
|
325
|
+
? [this.minimumValue, this.maximumValue]
|
326
|
+
: [this.minimumValue];
|
327
|
+
this.#updateHandlesAndTrack();
|
328
|
+
}
|
329
|
+
}
|
330
|
+
formAssociatedCallback() {
|
331
|
+
this.form?.addEventListener('formdata', this.#onFormdata.bind(this));
|
332
|
+
}
|
333
|
+
formResetCallback() {
|
334
|
+
this.value = this.#initialValue;
|
335
|
+
}
|
336
|
+
render() {
|
337
|
+
// The Slider track has a click handler for convenience to allow
|
338
|
+
// users to click anywhere on the track and have a handle move
|
339
|
+
// to that position. This behavior is an ehancement rather than
|
340
|
+
// essential functionality and is a situation where the linter
|
341
|
+
// is being overzealous without considering the full context of
|
342
|
+
// the component.
|
343
|
+
//
|
344
|
+
// A keyboard user can already update the component via the
|
345
|
+
// input elements directly or via the handles. Exposing the
|
346
|
+
// track via keyboard wouldn't bring any real value in this
|
347
|
+
// instance.
|
348
|
+
/* eslint-disable lit-a11y/click-events-have-key-events */
|
349
|
+
return html `
|
2
350
|
<glide-core-private-label
|
3
|
-
class=${classMap({
|
351
|
+
class=${classMap({
|
352
|
+
left: this.privateSplit === 'left',
|
353
|
+
middle: this.privateSplit === 'middle',
|
354
|
+
})}
|
4
355
|
label=${ifDefined(this.label)}
|
5
356
|
orientation=${this.orientation}
|
6
|
-
split=${ifDefined(this.privateSplit??
|
357
|
+
split=${ifDefined(this.privateSplit ?? undefined)}
|
7
358
|
tooltip=${ifDefined(this.tooltip)}
|
8
359
|
?disabled=${this.disabled}
|
9
|
-
?error=${this.#
|
360
|
+
?error=${this.#isShowValidationFeedback}
|
10
361
|
?hide=${this.hideLabel}
|
11
362
|
?required=${this.required}
|
12
363
|
>
|
13
364
|
<label>${this.label}</label>
|
14
365
|
|
15
366
|
<div
|
16
|
-
class=${classMap({
|
367
|
+
class=${classMap({
|
368
|
+
'slider-container': true,
|
369
|
+
disabled: this.disabled,
|
370
|
+
readonly: this.readonly && !this.disabled,
|
371
|
+
error: this.#isShowValidationFeedback,
|
372
|
+
})}
|
17
373
|
slot="control"
|
18
374
|
>
|
19
|
-
${when(this.multiple,(
|
375
|
+
${when(this.multiple, () => html `<input
|
20
376
|
aria-describedby="meta"
|
21
|
-
aria-label=${this.#
|
22
|
-
|
23
|
-
|
377
|
+
aria-label=${this.#localize.term('setMinimum',
|
378
|
+
// `this.label` is always defined because it's a
|
379
|
+
// required attribute.
|
380
|
+
//
|
381
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
382
|
+
this.label)}
|
383
|
+
aria-invalid=${this.#isShowValidationFeedback}
|
384
|
+
class=${classMap({
|
385
|
+
input: true,
|
386
|
+
disabled: this.disabled,
|
387
|
+
error: this.#isShowValidationFeedback,
|
388
|
+
readonly: this.readonly && !this.disabled,
|
389
|
+
})}
|
24
390
|
data-test="minimum-input"
|
25
|
-
max=${ifDefined(this.maximumValue
|
391
|
+
max=${ifDefined(this.maximumValue
|
392
|
+
? this.maximumValue - this.step
|
393
|
+
: undefined)}
|
26
394
|
min=${this.min}
|
27
395
|
step=${this.step}
|
28
396
|
type="number"
|
29
|
-
.value=${this.minimumValue?.toString()??
|
397
|
+
.value=${this.minimumValue?.toString() ?? ''}
|
30
398
|
?disabled=${this.disabled}
|
31
399
|
?readonly=${this.readonly}
|
32
|
-
@change=${this.#
|
33
|
-
@input=${this.#
|
34
|
-
@keydown=${this.#
|
35
|
-
${ref(this.#
|
400
|
+
@change=${this.#onMinimumInputChange.bind(this)}
|
401
|
+
@input=${this.#onMinimumInputInput}
|
402
|
+
@keydown=${this.#onInputKeydown}
|
403
|
+
${ref(this.#minimumInputElementRef)}
|
36
404
|
/>
|
37
405
|
|
38
406
|
<div class="track-container">
|
39
407
|
<div
|
40
|
-
class=${classMap({
|
408
|
+
class=${classMap({
|
409
|
+
'unfilled-track': true,
|
410
|
+
disabled: this.disabled,
|
411
|
+
})}
|
41
412
|
data-test="slider"
|
42
413
|
role="group"
|
43
|
-
@click=${this.#
|
44
|
-
${ref(this.#
|
414
|
+
@click=${this.#onTrackClick.bind(this)}
|
415
|
+
${ref(this.#sliderElementRef)}
|
45
416
|
>
|
46
417
|
<div
|
47
|
-
class=${classMap({
|
48
|
-
|
418
|
+
class=${classMap({
|
419
|
+
'filled-track': true,
|
420
|
+
disabled: this.disabled,
|
421
|
+
})}
|
422
|
+
${ref(this.#sliderFillElementRef)}
|
49
423
|
></div>
|
50
424
|
|
51
425
|
<div
|
52
426
|
aria-disabled=${this.disabled}
|
53
|
-
aria-label=${this.#
|
427
|
+
aria-label=${this.#localize.term('minimum',
|
428
|
+
// `this.label` is always defined because
|
429
|
+
// it's a required attribute.
|
430
|
+
//
|
431
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
432
|
+
this.label)}
|
54
433
|
aria-readonly=${this.readonly}
|
55
434
|
aria-valuemin=${this.min}
|
56
435
|
aria-valuemax=${this.max}
|
57
436
|
aria-valuenow=${ifDefined(this.minimumValue)}
|
58
|
-
class=${classMap({
|
437
|
+
class=${classMap({
|
438
|
+
handle: true,
|
439
|
+
disabled: this.disabled,
|
440
|
+
readonly: this.readonly && !this.disabled,
|
441
|
+
})}
|
59
442
|
data-test="minimum-handle"
|
60
443
|
role="slider"
|
61
444
|
tabindex="0"
|
62
|
-
@mousedown=${this.#
|
63
|
-
@keydown=${this.#
|
64
|
-
${ref(this.#
|
445
|
+
@mousedown=${this.#onMinimumHandleMousedown.bind(this)}
|
446
|
+
@keydown=${this.#onMinimumHandleKeydown.bind(this)}
|
447
|
+
${ref(this.#minimumHandleElementRef)}
|
65
448
|
></div>
|
66
449
|
|
67
450
|
<div
|
68
451
|
aria-disabled=${this.disabled}
|
69
|
-
aria-label=${this.#
|
452
|
+
aria-label=${this.#localize.term('maximum',
|
453
|
+
// `this.label` is always defined because
|
454
|
+
// it's a required attribute.
|
455
|
+
//
|
456
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
457
|
+
this.label)}
|
70
458
|
aria-readonly=${this.readonly}
|
71
459
|
aria-valuemin=${this.min}
|
72
460
|
aria-valuemax=${this.max}
|
73
461
|
aria-valuenow=${ifDefined(this.maximumValue)}
|
74
|
-
class=${classMap({
|
462
|
+
class=${classMap({
|
463
|
+
handle: true,
|
464
|
+
disabled: this.disabled,
|
465
|
+
readonly: this.readonly && !this.disabled,
|
466
|
+
})}
|
75
467
|
data-test="maximum-handle"
|
76
468
|
role="slider"
|
77
469
|
tabindex="0"
|
78
|
-
@mousedown=${this.#
|
79
|
-
@keydown=${this.#
|
80
|
-
${ref(this
|
470
|
+
@mousedown=${this.#onMaximumHandleMousedown.bind(this)}
|
471
|
+
@keydown=${this.#onMaximumHandleKeydown.bind(this)}
|
472
|
+
${ref(this.#maximumHandleElementRef)}
|
81
473
|
></div>
|
82
474
|
</div>
|
83
475
|
</div>
|
84
476
|
|
85
477
|
<input
|
86
|
-
aria-label=${this.#
|
87
|
-
|
88
|
-
|
478
|
+
aria-label=${this.#localize.term('setMaximum',
|
479
|
+
// `this.label` is always defined because it's a required attribute.
|
480
|
+
//
|
481
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
482
|
+
this.label)}
|
483
|
+
aria-invalid=${this.#isShowValidationFeedback}
|
484
|
+
class=${classMap({
|
485
|
+
input: true,
|
486
|
+
disabled: this.disabled,
|
487
|
+
error: this.#isShowValidationFeedback,
|
488
|
+
readonly: this.readonly && !this.disabled,
|
489
|
+
})}
|
89
490
|
data-test="maximum-input"
|
90
491
|
max=${this.max}
|
91
|
-
min=${ifDefined(this.minimumValue
|
492
|
+
min=${ifDefined(this.minimumValue
|
493
|
+
? this.minimumValue + this.step
|
494
|
+
: undefined)}
|
92
495
|
step=${this.step}
|
93
496
|
type="number"
|
94
|
-
.value=${this.maximumValue?.toString()??
|
497
|
+
.value=${this.maximumValue?.toString() ?? ''}
|
95
498
|
?disabled=${this.disabled}
|
96
499
|
?readonly=${this.readonly}
|
97
|
-
@change=${this.#
|
98
|
-
@input=${this.#
|
99
|
-
@keydown=${this.#
|
100
|
-
${ref(this.#
|
101
|
-
|
500
|
+
@change=${this.#onMaximumInputChange.bind(this)}
|
501
|
+
@input=${this.#onMaximumInputInput}
|
502
|
+
@keydown=${this.#onInputKeydown}
|
503
|
+
${ref(this.#maximumInputElementRef)}
|
504
|
+
/>`, () => html `<div class="track-container single">
|
102
505
|
<div
|
103
|
-
class=${classMap({
|
506
|
+
class=${classMap({
|
507
|
+
'unfilled-track': true,
|
508
|
+
disabled: this.disabled,
|
509
|
+
})}
|
104
510
|
data-test="slider"
|
105
511
|
role="group"
|
106
|
-
@click=${this.#
|
107
|
-
${ref(this.#
|
512
|
+
@click=${this.#onTrackClick.bind(this)}
|
513
|
+
${ref(this.#sliderElementRef)}
|
108
514
|
>
|
109
515
|
<div
|
110
|
-
class=${classMap({
|
111
|
-
|
516
|
+
class=${classMap({
|
517
|
+
'filled-track': true,
|
518
|
+
disabled: this.disabled,
|
519
|
+
})}
|
520
|
+
${ref(this.#sliderFillElementRef)}
|
112
521
|
></div>
|
113
522
|
|
114
523
|
<div
|
@@ -119,39 +528,51 @@ var __decorate=this&&this.__decorate||function(e,i,t,a){var s,n=arguments.length
|
|
119
528
|
aria-valuemin=${this.min}
|
120
529
|
aria-valuemax=${this.max}
|
121
530
|
aria-valuenow=${ifDefined(this.minimumValue)}
|
122
|
-
class=${classMap({
|
531
|
+
class=${classMap({
|
532
|
+
handle: true,
|
533
|
+
disabled: this.disabled,
|
534
|
+
readonly: this.readonly && !this.disabled,
|
535
|
+
})}
|
123
536
|
data-test="single-handle"
|
124
537
|
role="slider"
|
125
538
|
tabindex="0"
|
126
|
-
@mousedown=${this.#
|
127
|
-
@keydown=${this.#
|
128
|
-
${ref(this.#
|
539
|
+
@mousedown=${this.#onSingleHandleMousedown.bind(this)}
|
540
|
+
@keydown=${this.#onSingleHandleKeyDown.bind(this)}
|
541
|
+
${ref(this.#singleHandleElementRef)}
|
129
542
|
></div>
|
130
543
|
</div>
|
131
544
|
</div>
|
132
545
|
|
133
546
|
<input
|
134
547
|
aria-label=${ifDefined(this.label)}
|
135
|
-
aria-invalid=${this.#
|
136
|
-
class=${classMap({
|
548
|
+
aria-invalid=${this.#isShowValidationFeedback}
|
549
|
+
class=${classMap({
|
550
|
+
input: true,
|
551
|
+
disabled: this.disabled,
|
552
|
+
error: this.#isShowValidationFeedback,
|
553
|
+
readonly: this.readonly && !this.disabled,
|
554
|
+
})}
|
137
555
|
data-test="single-input"
|
138
556
|
max=${this.max}
|
139
557
|
min=${this.min}
|
140
558
|
step=${this.step}
|
141
559
|
type="number"
|
142
|
-
.value=${this.minimumValue?.toString()??
|
560
|
+
.value=${this.minimumValue?.toString() ?? ''}
|
143
561
|
?disabled=${this.disabled}
|
144
562
|
?readonly=${this.readonly}
|
145
|
-
@change=${this.#
|
146
|
-
@input=${this.#
|
147
|
-
@keydown=${this.#
|
148
|
-
${ref(this.#
|
149
|
-
/>`)
|
563
|
+
@change=${this.#onSingleInputChange.bind(this)}
|
564
|
+
@input=${this.#onSingleInputInput.bind(this)}
|
565
|
+
@keydown=${this.#onInputKeydown}
|
566
|
+
${ref(this.#singleInputElementRef)}
|
567
|
+
/>`)}
|
150
568
|
</div>
|
151
569
|
|
152
570
|
<div class="meta" id="meta" slot="description">
|
153
571
|
<slot
|
154
|
-
class=${classMap({
|
572
|
+
class=${classMap({
|
573
|
+
description: true,
|
574
|
+
hidden: Boolean(this.#isShowValidationFeedback && this.validityMessage),
|
575
|
+
})}
|
155
576
|
name="description"
|
156
577
|
>
|
157
578
|
<!--
|
@@ -160,9 +581,567 @@ var __decorate=this&&this.__decorate||function(e,i,t,a){var s,n=arguments.length
|
|
160
581
|
-->
|
161
582
|
</slot>
|
162
583
|
|
163
|
-
${when(this.#
|
584
|
+
${when(this.#isShowValidationFeedback && this.validityMessage, () => html `<span class="validity-message" data-test="validity-message"
|
164
585
|
>${unsafeHTML(this.validityMessage)}</span
|
165
|
-
>`)
|
586
|
+
>`)}
|
166
587
|
</div>
|
167
588
|
</glide-core-private-label>
|
168
|
-
|
589
|
+
`;
|
590
|
+
}
|
591
|
+
reportValidity() {
|
592
|
+
this.isReportValidityOrSubmit = true;
|
593
|
+
const isValid = this.#internals.reportValidity();
|
594
|
+
// Ensures that getters referencing `this.validity.valid` are updated.
|
595
|
+
this.requestUpdate();
|
596
|
+
return isValid;
|
597
|
+
}
|
598
|
+
resetValidityFeedback() {
|
599
|
+
this.isReportValidityOrSubmit = false;
|
600
|
+
}
|
601
|
+
setCustomValidity(message) {
|
602
|
+
this.validityMessage = message;
|
603
|
+
if (message === '') {
|
604
|
+
this.#internals.setValidity({ customError: false }, '', this.#inputElementRef.value);
|
605
|
+
}
|
606
|
+
else {
|
607
|
+
// A validation message is required but unused because we
|
608
|
+
// disable native validation feedback. And an empty string
|
609
|
+
// isn't allowed. Thus a single space.
|
610
|
+
this.#internals.setValidity({
|
611
|
+
customError: true,
|
612
|
+
patternMismatch: this.#internals.validity.patternMismatch,
|
613
|
+
valueMissing: this.#internals.validity.valueMissing,
|
614
|
+
}, ' ', this.#inputElementRef.value);
|
615
|
+
}
|
616
|
+
}
|
617
|
+
setValidity(flags, message) {
|
618
|
+
this.validityMessage = message;
|
619
|
+
// A validation message is required but unused because we
|
620
|
+
// disable native validation feedback. And an empty string isn't
|
621
|
+
// allowed. Thus a single space.
|
622
|
+
this.#internals.setValidity(flags, ' ', this.#inputElementRef.value);
|
623
|
+
}
|
624
|
+
constructor() {
|
625
|
+
super();
|
626
|
+
this.name = '';
|
627
|
+
this.orientation = 'horizontal';
|
628
|
+
this.hideLabel = false;
|
629
|
+
this.required = false;
|
630
|
+
this.readonly = false;
|
631
|
+
this.disabled = false;
|
632
|
+
this.version = packageJson.version;
|
633
|
+
this.isBlurring = false;
|
634
|
+
this.isCheckingValidity = false;
|
635
|
+
this.isReportValidityOrSubmit = false;
|
636
|
+
this.#hasCompletedDrag = false;
|
637
|
+
this.#initialValue = [];
|
638
|
+
this.#inputElementRef = createRef();
|
639
|
+
this.#localize = new LocalizeController(this);
|
640
|
+
this.#max = 100;
|
641
|
+
this.#maximumHandleElementRef = createRef();
|
642
|
+
this.#maximumInputElementRef = createRef();
|
643
|
+
this.#min = 0;
|
644
|
+
this.#minimumHandleElementRef = createRef();
|
645
|
+
this.#minimumInputElementRef = createRef();
|
646
|
+
this.#multiple = false;
|
647
|
+
this.#singleHandleElementRef = createRef();
|
648
|
+
this.#singleInputElementRef = createRef();
|
649
|
+
this.#sliderElementRef = createRef();
|
650
|
+
this.#sliderFillElementRef = createRef();
|
651
|
+
this.#step = 1;
|
652
|
+
// An arrow function field instead of a method so `this` is closed
|
653
|
+
// over and set to the component instead of `document`.
|
654
|
+
this.#onDraggingMousemove = (mouseEvent) => {
|
655
|
+
mouseEvent.preventDefault();
|
656
|
+
const slider = this.#sliderElementRef.value;
|
657
|
+
if (slider && this.#draggingHandleElement) {
|
658
|
+
const { clientX } = mouseEvent;
|
659
|
+
if (clientX !== undefined) {
|
660
|
+
const sliderRect = slider.getBoundingClientRect();
|
661
|
+
this.#onDrag(clientX, this.#draggingHandleElement, sliderRect);
|
662
|
+
}
|
663
|
+
}
|
664
|
+
};
|
665
|
+
// An arrow function field instead of a method so `this` is closed
|
666
|
+
// over and set to the component instead of `document`.
|
667
|
+
this.#onDraggingMouseup = () => {
|
668
|
+
if (this.#draggingHandleElement) {
|
669
|
+
document.removeEventListener('mousemove', this.#onDraggingMousemove);
|
670
|
+
document.removeEventListener('mouseup', this.#onDraggingMouseup);
|
671
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
672
|
+
this.#hasCompletedDrag = true;
|
673
|
+
this.#draggingHandleElement = undefined;
|
674
|
+
// Track clicks can inadvertently occur when letting go of
|
675
|
+
// a handle. To help prevent click events from dispatching,
|
676
|
+
// this variable keeps track of when we are still processing
|
677
|
+
// the drag event.
|
678
|
+
//
|
679
|
+
// Resetting the state in the next frame allows the drag to
|
680
|
+
// fully complete before accepting track click events again.
|
681
|
+
setTimeout(() => {
|
682
|
+
this.#hasCompletedDrag = false;
|
683
|
+
});
|
684
|
+
}
|
685
|
+
};
|
686
|
+
this.#internals = this.attachInternals();
|
687
|
+
// Event handlers on the host aren't great because consumers can
|
688
|
+
// remove them. Unfortunately, the host is the only thing on
|
689
|
+
// which this event is dispatched because it's the host that is
|
690
|
+
// form-associated.
|
691
|
+
this.addEventListener('invalid', (event) => {
|
692
|
+
event?.preventDefault(); // Canceled so a native validation message isn't shown.
|
693
|
+
// We only want to focus the slider if the invalid event
|
694
|
+
// resulted from either:
|
695
|
+
// 1. Form submission
|
696
|
+
// 2. a call to reportValidity that did NOT result from
|
697
|
+
// the slider blur event
|
698
|
+
if (this.isCheckingValidity || this.isBlurring) {
|
699
|
+
return;
|
700
|
+
}
|
701
|
+
this.isReportValidityOrSubmit = true;
|
702
|
+
const isFirstInvalidFormElement = this.form?.querySelector(':invalid') === this;
|
703
|
+
if (isFirstInvalidFormElement) {
|
704
|
+
this.focus();
|
705
|
+
}
|
706
|
+
});
|
707
|
+
}
|
708
|
+
#draggingHandleElement;
|
709
|
+
#hasCompletedDrag;
|
710
|
+
#initialValue;
|
711
|
+
#inputElementRef;
|
712
|
+
#internals;
|
713
|
+
#localize;
|
714
|
+
#max;
|
715
|
+
#maximumHandleElementRef;
|
716
|
+
#maximumInputElementRef;
|
717
|
+
#min;
|
718
|
+
#minimumHandleElementRef;
|
719
|
+
#minimumInputElementRef;
|
720
|
+
#multiple;
|
721
|
+
#singleHandleElementRef;
|
722
|
+
#singleInputElementRef;
|
723
|
+
#sliderElementRef;
|
724
|
+
#sliderFillElementRef;
|
725
|
+
#step;
|
726
|
+
get #isShowValidationFeedback() {
|
727
|
+
return (!this.disabled && !this.validity?.valid && this.isReportValidityOrSubmit);
|
728
|
+
}
|
729
|
+
// An arrow function field instead of a method so `this` is closed
|
730
|
+
// over and set to the component instead of `document`.
|
731
|
+
#onDraggingMousemove;
|
732
|
+
// An arrow function field instead of a method so `this` is closed
|
733
|
+
// over and set to the component instead of `document`.
|
734
|
+
#onDraggingMouseup;
|
735
|
+
#onDrag(clientX, handle, sliderRect) {
|
736
|
+
const filledPercentage = (clientX - sliderRect.left) / sliderRect.width;
|
737
|
+
const clampedPosition = filledPercentage * (this.max - this.min) + this.min;
|
738
|
+
// Ensures the calculated value aligns with the Slider's step
|
739
|
+
// configuration, rounding to the nearest valid step increment.
|
740
|
+
const snappedValue = Math.round(clampedPosition / this.step) * this.step;
|
741
|
+
if (!this.multiple) {
|
742
|
+
const newValue = Math.min(Math.max(snappedValue, this.min), this.max);
|
743
|
+
// To align with native, we only dispatch the input event when
|
744
|
+
// the value is actually changed.
|
745
|
+
if (newValue !== this.minimumValue) {
|
746
|
+
this.minimumValue = newValue;
|
747
|
+
this.#updateHandlesAndTrack();
|
748
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
749
|
+
}
|
750
|
+
return;
|
751
|
+
}
|
752
|
+
if (handle === this.#minimumHandleElementRef.value &&
|
753
|
+
this.maximumValue !== undefined) {
|
754
|
+
const newValue = Math.min(Math.max(snappedValue, this.min), this.maximumValue - this.step);
|
755
|
+
// To align with native, we only dispatch the input event when
|
756
|
+
// the value is actually changed.
|
757
|
+
if (newValue !== this.minimumValue) {
|
758
|
+
this.minimumValue = newValue;
|
759
|
+
this.#updateHandlesAndTrack();
|
760
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
761
|
+
return;
|
762
|
+
}
|
763
|
+
}
|
764
|
+
if (handle === this.#maximumHandleElementRef.value &&
|
765
|
+
this.minimumValue !== undefined) {
|
766
|
+
const newValue = Math.min(Math.max(snappedValue, this.minimumValue + this.step), this.max);
|
767
|
+
// To align with native, we only dispatch the input event when
|
768
|
+
// the value is actually changed.
|
769
|
+
if (newValue !== this.maximumValue) {
|
770
|
+
this.maximumValue = newValue;
|
771
|
+
this.#updateHandlesAndTrack();
|
772
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
773
|
+
return;
|
774
|
+
}
|
775
|
+
}
|
776
|
+
}
|
777
|
+
#onFormdata(event) {
|
778
|
+
if (this.name && this.value && !this.disabled) {
|
779
|
+
event.formData.append(this.name, JSON.stringify(this.value));
|
780
|
+
}
|
781
|
+
}
|
782
|
+
#onInputKeydown(event) {
|
783
|
+
if (event.key === 'Enter') {
|
784
|
+
this.form?.requestSubmit();
|
785
|
+
}
|
786
|
+
}
|
787
|
+
#onMaximumHandleKeydown(event) {
|
788
|
+
if (this.#maximumHandleElementRef.value) {
|
789
|
+
this.#onMultipleHandleKeydown(event, this.#maximumHandleElementRef.value);
|
790
|
+
}
|
791
|
+
}
|
792
|
+
#onMaximumHandleMousedown(event) {
|
793
|
+
if (this.#maximumHandleElementRef.value) {
|
794
|
+
this.#startDragging(event, this.#maximumHandleElementRef.value);
|
795
|
+
}
|
796
|
+
}
|
797
|
+
#onMaximumInputChange() {
|
798
|
+
if (this.#maximumInputElementRef.value && this.minimumValue !== undefined) {
|
799
|
+
const inputValue = Number(this.#maximumInputElementRef.value.value);
|
800
|
+
const normalizedValue = Math.round(inputValue / this.step) * this.step;
|
801
|
+
this.maximumValue = Math.min(Math.max(normalizedValue, this.minimumValue + this.step), this.max);
|
802
|
+
this.#updateHandlesAndTrack();
|
803
|
+
// Unlike "input" events, "change" events aren't composed. So
|
804
|
+
// we have to manually dispatch them.
|
805
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
806
|
+
}
|
807
|
+
}
|
808
|
+
#onMaximumInputInput() {
|
809
|
+
if (this.#maximumInputElementRef.value) {
|
810
|
+
const inputValue = Number(this.#maximumInputElementRef.value.value);
|
811
|
+
if (this.minimumValue !== undefined && inputValue >= this.minimumValue) {
|
812
|
+
this.maximumValue = Math.min(inputValue, this.max);
|
813
|
+
this.#updateHandlesAndTrack();
|
814
|
+
}
|
815
|
+
}
|
816
|
+
}
|
817
|
+
#onMinimumHandleKeydown(event) {
|
818
|
+
if (this.#minimumHandleElementRef.value) {
|
819
|
+
this.#onMultipleHandleKeydown(event, this.#minimumHandleElementRef.value);
|
820
|
+
}
|
821
|
+
}
|
822
|
+
#onMinimumHandleMousedown(event) {
|
823
|
+
if (this.#minimumHandleElementRef.value) {
|
824
|
+
this.#startDragging(event, this.#minimumHandleElementRef.value);
|
825
|
+
}
|
826
|
+
}
|
827
|
+
#onMinimumInputChange() {
|
828
|
+
if (this.#minimumInputElementRef.value && this.maximumValue !== undefined) {
|
829
|
+
const inputValue = Number(this.#minimumInputElementRef.value.value);
|
830
|
+
const normalizedValue = Math.round(inputValue / this.step) * this.step;
|
831
|
+
this.minimumValue = Math.min(Math.max(normalizedValue, this.min),
|
832
|
+
// Ensures the minimum value always stays at least one step
|
833
|
+
// below the maximum value, maintaining the required
|
834
|
+
// separation. Without this, users could set the minimum
|
835
|
+
// value equal to or higher than the maximum value, creating
|
836
|
+
// issues, both visually and operationally.
|
837
|
+
//
|
838
|
+
// Consider the case where maximumValue is 75 and step is 5.
|
839
|
+
// Without this, a user could set the value to 80, which
|
840
|
+
// would be invalid. With this constraint, the minimum value
|
841
|
+
// can't go higher than 70 (75-5).
|
842
|
+
this.maximumValue - this.step);
|
843
|
+
this.#updateHandlesAndTrack();
|
844
|
+
// Unlike "input" events, "change" events aren't composed. So
|
845
|
+
// we have to manually dispatch them.
|
846
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
847
|
+
}
|
848
|
+
}
|
849
|
+
#onMinimumInputInput() {
|
850
|
+
if (this.#minimumInputElementRef.value) {
|
851
|
+
const inputValue = Number(this.#minimumInputElementRef.value.value);
|
852
|
+
if (this.maximumValue !== undefined && inputValue <= this.maximumValue) {
|
853
|
+
this.minimumValue = Math.max(inputValue, this.min);
|
854
|
+
this.#updateHandlesAndTrack();
|
855
|
+
}
|
856
|
+
}
|
857
|
+
}
|
858
|
+
#onMultipleHandleKeydown(event, handle) {
|
859
|
+
if (this.disabled || this.readonly) {
|
860
|
+
return;
|
861
|
+
}
|
862
|
+
if (this.minimumValue !== undefined && this.maximumValue !== undefined) {
|
863
|
+
const isMinimumHandle = handle === this.#minimumHandleElementRef.value;
|
864
|
+
let value = isMinimumHandle ? this.minimumValue : this.maximumValue;
|
865
|
+
switch (event.key) {
|
866
|
+
case 'ArrowLeft':
|
867
|
+
case 'ArrowDown': {
|
868
|
+
value = value - this.step;
|
869
|
+
break;
|
870
|
+
}
|
871
|
+
case 'ArrowRight':
|
872
|
+
case 'ArrowUp': {
|
873
|
+
value = value + this.step;
|
874
|
+
break;
|
875
|
+
}
|
876
|
+
case 'PageDown': {
|
877
|
+
value = value - this.step * 10;
|
878
|
+
break;
|
879
|
+
}
|
880
|
+
case 'PageUp': {
|
881
|
+
value = value + this.step * 10;
|
882
|
+
break;
|
883
|
+
}
|
884
|
+
case 'Home': {
|
885
|
+
value = isMinimumHandle ? this.min : this.minimumValue + this.step;
|
886
|
+
break;
|
887
|
+
}
|
888
|
+
case 'End': {
|
889
|
+
value = isMinimumHandle ? this.maximumValue - this.step : this.max;
|
890
|
+
break;
|
891
|
+
}
|
892
|
+
case 'Enter': {
|
893
|
+
this.form?.requestSubmit();
|
894
|
+
return;
|
895
|
+
}
|
896
|
+
default: {
|
897
|
+
return;
|
898
|
+
}
|
899
|
+
}
|
900
|
+
// Prevent page scroll.
|
901
|
+
event.preventDefault();
|
902
|
+
if (isMinimumHandle) {
|
903
|
+
this.minimumValue = Math.min(Math.max(value, this.min), this.maximumValue - this.step);
|
904
|
+
}
|
905
|
+
else {
|
906
|
+
this.maximumValue = Math.min(Math.max(value, this.minimumValue + this.step), this.max);
|
907
|
+
}
|
908
|
+
this.#updateHandlesAndTrack();
|
909
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
910
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
911
|
+
}
|
912
|
+
}
|
913
|
+
#onSingleHandleKeyDown(event) {
|
914
|
+
if (this.disabled || this.readonly) {
|
915
|
+
return;
|
916
|
+
}
|
917
|
+
const handle = this.#singleHandleElementRef.value;
|
918
|
+
if (handle && this.minimumValue !== undefined) {
|
919
|
+
let value = this.minimumValue;
|
920
|
+
switch (event.key) {
|
921
|
+
case 'ArrowLeft':
|
922
|
+
case 'ArrowDown': {
|
923
|
+
value -= this.step;
|
924
|
+
break;
|
925
|
+
}
|
926
|
+
case 'ArrowRight':
|
927
|
+
case 'ArrowUp': {
|
928
|
+
value += this.step;
|
929
|
+
break;
|
930
|
+
}
|
931
|
+
case 'PageDown': {
|
932
|
+
value -= this.step * 10;
|
933
|
+
break;
|
934
|
+
}
|
935
|
+
case 'PageUp': {
|
936
|
+
value += this.step * 10;
|
937
|
+
break;
|
938
|
+
}
|
939
|
+
case 'Home': {
|
940
|
+
value = this.min;
|
941
|
+
break;
|
942
|
+
}
|
943
|
+
case 'End': {
|
944
|
+
value = this.max;
|
945
|
+
break;
|
946
|
+
}
|
947
|
+
case 'Enter': {
|
948
|
+
this.form?.requestSubmit();
|
949
|
+
return;
|
950
|
+
}
|
951
|
+
default: {
|
952
|
+
return;
|
953
|
+
}
|
954
|
+
}
|
955
|
+
event.preventDefault();
|
956
|
+
this.minimumValue = Math.min(Math.max(value, this.min), this.max);
|
957
|
+
this.#updateHandlesAndTrack();
|
958
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
959
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
960
|
+
}
|
961
|
+
}
|
962
|
+
#onSingleHandleMousedown(event) {
|
963
|
+
if (this.#singleHandleElementRef.value) {
|
964
|
+
this.#startDragging(event, this.#singleHandleElementRef.value);
|
965
|
+
}
|
966
|
+
}
|
967
|
+
#onSingleInputChange() {
|
968
|
+
if (this.#singleInputElementRef.value) {
|
969
|
+
const inputValue = Number(this.#singleInputElementRef.value.value);
|
970
|
+
const normalizedValue = Math.round(inputValue / this.step) * this.step;
|
971
|
+
this.minimumValue = Math.min(Math.max(normalizedValue, this.min), this.max);
|
972
|
+
this.#updateHandlesAndTrack();
|
973
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
974
|
+
}
|
975
|
+
}
|
976
|
+
#onSingleInputInput() {
|
977
|
+
if (this.#singleInputElementRef.value) {
|
978
|
+
const inputValue = Number(this.#singleInputElementRef.value.value);
|
979
|
+
if (inputValue >= 0) {
|
980
|
+
this.minimumValue = Math.min(inputValue, this.max);
|
981
|
+
this.#updateHandlesAndTrack();
|
982
|
+
}
|
983
|
+
}
|
984
|
+
}
|
985
|
+
#onTrackClick(event) {
|
986
|
+
if (this.disabled || this.readonly) {
|
987
|
+
return;
|
988
|
+
}
|
989
|
+
if (event.target === this.#minimumHandleElementRef.value ||
|
990
|
+
event.target === this.#maximumHandleElementRef.value ||
|
991
|
+
event.target === this.#singleHandleElementRef.value ||
|
992
|
+
this.#hasCompletedDrag) {
|
993
|
+
return;
|
994
|
+
}
|
995
|
+
const slider = this.#sliderElementRef.value;
|
996
|
+
if (slider) {
|
997
|
+
const sliderRect = slider.getBoundingClientRect();
|
998
|
+
const clickPosition = (event.clientX - sliderRect.left) / sliderRect.width;
|
999
|
+
const clampedPosition = clickPosition * (this.max - this.min) + this.min;
|
1000
|
+
// Similar to when dragging, when clicking the track we need
|
1001
|
+
// to ensure the calculated value aligns with the Slider's
|
1002
|
+
// step configuration, rounding to the nearest valid step
|
1003
|
+
// increment.
|
1004
|
+
const snappedValue = Math.round(clampedPosition / this.step) * this.step;
|
1005
|
+
if (this.multiple &&
|
1006
|
+
this.minimumValue !== undefined &&
|
1007
|
+
this.maximumValue !== undefined) {
|
1008
|
+
// Calculate the distance to each handle to determine which
|
1009
|
+
// one to move.
|
1010
|
+
const minimumDistance = Math.abs(snappedValue - this.minimumValue);
|
1011
|
+
const maximumDistance = Math.abs(snappedValue - this.maximumValue);
|
1012
|
+
// Move the closest handle.
|
1013
|
+
if (minimumDistance <= maximumDistance) {
|
1014
|
+
this.minimumValue = Math.min(Math.max(snappedValue, this.min), this.maximumValue - this.step);
|
1015
|
+
}
|
1016
|
+
else {
|
1017
|
+
this.maximumValue = Math.min(Math.max(snappedValue, this.minimumValue + this.step), this.max);
|
1018
|
+
}
|
1019
|
+
this.#updateHandlesAndTrack();
|
1020
|
+
// Native fires both events in this case.
|
1021
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
1022
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
1023
|
+
return;
|
1024
|
+
}
|
1025
|
+
this.minimumValue = Math.min(Math.max(snappedValue, this.min), this.max);
|
1026
|
+
this.#updateHandlesAndTrack();
|
1027
|
+
// Native fires both events in this case.
|
1028
|
+
this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
1029
|
+
this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
1030
|
+
}
|
1031
|
+
}
|
1032
|
+
#startDragging(event, handle) {
|
1033
|
+
if (this.disabled || this.readonly) {
|
1034
|
+
return;
|
1035
|
+
}
|
1036
|
+
event.preventDefault();
|
1037
|
+
const slider = this.#sliderElementRef.value;
|
1038
|
+
if (slider) {
|
1039
|
+
this.#draggingHandleElement = handle;
|
1040
|
+
// We want document-level event listeners so that the user can
|
1041
|
+
// drag and release the handle outside of the component and it
|
1042
|
+
// update properly. Not all users will drag the handles though,
|
1043
|
+
// so rather than adding event listeners in connectedCallback()
|
1044
|
+
// and having them fire any time a user moves their mouse,
|
1045
|
+
// even if they aren't interacting directly with the Slider,
|
1046
|
+
// we add these event listeners only when the user initiates
|
1047
|
+
// dragging.
|
1048
|
+
document.addEventListener('mousemove', this.#onDraggingMousemove);
|
1049
|
+
document.addEventListener('mouseup', this.#onDraggingMouseup);
|
1050
|
+
this.#onDrag(event.clientX, handle, slider.getBoundingClientRect());
|
1051
|
+
}
|
1052
|
+
}
|
1053
|
+
#updateHandlesAndTrack() {
|
1054
|
+
const sliderFill = this.#sliderFillElementRef.value;
|
1055
|
+
const singleHandle = this.#singleHandleElementRef.value;
|
1056
|
+
const minimumHandle = this.#minimumHandleElementRef.value;
|
1057
|
+
const maximumHandle = this.#maximumHandleElementRef.value;
|
1058
|
+
if (sliderFill && this.minimumValue !== undefined) {
|
1059
|
+
if (!this.multiple && singleHandle) {
|
1060
|
+
const range = this.max - this.min;
|
1061
|
+
const calculatedPosition = ((this.minimumValue - this.min) / range) * 100;
|
1062
|
+
singleHandle.style.left = `${calculatedPosition}%`;
|
1063
|
+
sliderFill.style.left = '0';
|
1064
|
+
sliderFill.style.width = `${calculatedPosition}%`;
|
1065
|
+
return;
|
1066
|
+
}
|
1067
|
+
if (this.maximumValue !== undefined && minimumHandle && maximumHandle) {
|
1068
|
+
const range = this.max - this.min;
|
1069
|
+
const minimumPosition = ((this.minimumValue - this.min) / range) * 100;
|
1070
|
+
const maximumPosition = ((this.maximumValue - this.min) / range) * 100;
|
1071
|
+
minimumHandle.style.left = `${minimumPosition}%`;
|
1072
|
+
maximumHandle.style.left = `${maximumPosition}%`;
|
1073
|
+
sliderFill.style.left = `${minimumPosition}%`;
|
1074
|
+
sliderFill.style.width = `${maximumPosition - minimumPosition}%`;
|
1075
|
+
}
|
1076
|
+
}
|
1077
|
+
}
|
1078
|
+
};
|
1079
|
+
__decorate([
|
1080
|
+
property({ reflect: true, useDefault: true })
|
1081
|
+
], Slider.prototype, "name", void 0);
|
1082
|
+
__decorate([
|
1083
|
+
property({ type: Array })
|
1084
|
+
], Slider.prototype, "value", null);
|
1085
|
+
__decorate([
|
1086
|
+
property({ reflect: true }),
|
1087
|
+
required
|
1088
|
+
], Slider.prototype, "label", void 0);
|
1089
|
+
__decorate([
|
1090
|
+
property({ reflect: true, useDefault: true })
|
1091
|
+
], Slider.prototype, "orientation", void 0);
|
1092
|
+
__decorate([
|
1093
|
+
property({ attribute: 'hide-label', type: Boolean })
|
1094
|
+
], Slider.prototype, "hideLabel", void 0);
|
1095
|
+
__decorate([
|
1096
|
+
property({ reflect: true, type: Boolean })
|
1097
|
+
], Slider.prototype, "required", void 0);
|
1098
|
+
__decorate([
|
1099
|
+
property({ type: Boolean })
|
1100
|
+
], Slider.prototype, "readonly", void 0);
|
1101
|
+
__decorate([
|
1102
|
+
property({ reflect: true, type: Boolean })
|
1103
|
+
], Slider.prototype, "disabled", void 0);
|
1104
|
+
__decorate([
|
1105
|
+
property({ reflect: true, type: Number })
|
1106
|
+
], Slider.prototype, "max", null);
|
1107
|
+
__decorate([
|
1108
|
+
property({ reflect: true, type: Number })
|
1109
|
+
], Slider.prototype, "min", null);
|
1110
|
+
__decorate([
|
1111
|
+
property({ type: Boolean })
|
1112
|
+
], Slider.prototype, "multiple", null);
|
1113
|
+
__decorate([
|
1114
|
+
property({ reflect: true, type: Number, useDefault: true })
|
1115
|
+
], Slider.prototype, "step", null);
|
1116
|
+
__decorate([
|
1117
|
+
property()
|
1118
|
+
], Slider.prototype, "privateSplit", void 0);
|
1119
|
+
__decorate([
|
1120
|
+
property({ reflect: true })
|
1121
|
+
], Slider.prototype, "version", void 0);
|
1122
|
+
__decorate([
|
1123
|
+
property({ reflect: true })
|
1124
|
+
], Slider.prototype, "tooltip", void 0);
|
1125
|
+
__decorate([
|
1126
|
+
state()
|
1127
|
+
], Slider.prototype, "isBlurring", void 0);
|
1128
|
+
__decorate([
|
1129
|
+
state()
|
1130
|
+
], Slider.prototype, "isCheckingValidity", void 0);
|
1131
|
+
__decorate([
|
1132
|
+
state()
|
1133
|
+
], Slider.prototype, "isReportValidityOrSubmit", void 0);
|
1134
|
+
__decorate([
|
1135
|
+
state()
|
1136
|
+
], Slider.prototype, "maximumValue", void 0);
|
1137
|
+
__decorate([
|
1138
|
+
state()
|
1139
|
+
], Slider.prototype, "minimumValue", void 0);
|
1140
|
+
__decorate([
|
1141
|
+
state()
|
1142
|
+
], Slider.prototype, "validityMessage", void 0);
|
1143
|
+
Slider = __decorate([
|
1144
|
+
customElement('glide-core-slider'),
|
1145
|
+
final
|
1146
|
+
], Slider);
|
1147
|
+
export default Slider;
|