@refinitiv-ui/elements 7.9.1 → 7.10.1

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.
@@ -0,0 +1,1431 @@
1
+ import { __decorate } from "tslib";
2
+ import { FormFieldElement, WarningNotice, css, html, nothing } from '@refinitiv-ui/core';
3
+ import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
4
+ import { property } from '@refinitiv-ui/core/decorators/property.js';
5
+ import { query } from '@refinitiv-ui/core/decorators/query.js';
6
+ import { state } from '@refinitiv-ui/core/decorators/state.js';
7
+ import { createRef, ref } from '@refinitiv-ui/core/directives/ref.js';
8
+ import { styleMap } from '@refinitiv-ui/core/directives/style-map.js';
9
+ import '@refinitiv-ui/phrasebook/locale/en/slider.js';
10
+ import { translate } from '@refinitiv-ui/translate';
11
+ import '../../number-field/index.js';
12
+ import { VERSION } from '../../version.js';
13
+ import { Direction, SliderDataName } from '../constants.js';
14
+ import { clamp, countDecimalPlace, isDecimalNumber, preventDefault } from '../utils.js';
15
+ import { SliderMarker } from './slider-marker.js';
16
+ /**
17
+ * Allows users to make selections from a range of values
18
+ *
19
+ * @attr {string} value - Value of slider. Not applicable in range mode.
20
+ * @prop {string} [value="0"] - Value of slider. Not applicable in range mode.
21
+ *
22
+ * @attr {boolean} readonly - Set readonly state
23
+ * @prop {boolean} [readonly=false] - Set readonly state
24
+ *
25
+ * @attr {boolean} disabled - Set disabled state
26
+ * @prop {boolean} [disabled=false] - Set disabled state
27
+ *
28
+ * @fires value-changed - Fired when the user commits a value change. The event is not triggered if `value` property is changed programmatically.
29
+ * @fires from-changed - Fired when the user changes from's value. The event is not triggered if `from` property is changed programmatically.
30
+ * @fires to-changed - Fired when the user changes to's value. The event is not triggered if `to` property is changed programmatically.
31
+ * @fires input - Fired with the value of the input in `e.detail.value` like another custom events when the user inputs a value by interacting with the slider or updating its input field.
32
+ * @fires from-input - Fired when the user inputs from's value by interacting with the slider or updating its input field.
33
+ * @fires to-input - Fired when the user inputs to's value by interacting with the slider or updating its input field.
34
+ */
35
+ let Slider = class Slider extends FormFieldElement {
36
+ /**
37
+ * Element version number
38
+ * @returns version number
39
+ */
40
+ static get version() {
41
+ return VERSION;
42
+ }
43
+ /**
44
+ * Define styles in a tagged template literal, using the css tag function.
45
+ * @returns CSS template
46
+ */
47
+ static get styles() {
48
+ return css `
49
+ :host {
50
+ display: flex;
51
+ position: relative;
52
+ }
53
+ [part='slider-wrapper'] {
54
+ position: relative;
55
+ width: 100%;
56
+ }
57
+ [part='slider'] {
58
+ width: 100%;
59
+ height: 100%;
60
+ display: inline-block;
61
+ }
62
+ :host(:not([disabled]):focus) {
63
+ outline: none;
64
+ }
65
+ :host([show-steps]) [part='track-wrapper']::after {
66
+ display: block;
67
+ position: absolute;
68
+ content: '';
69
+ right: 0;
70
+ }
71
+ [part='track-wrapper'] {
72
+ content: '';
73
+ position: absolute;
74
+ width: 100%;
75
+ top: 50%;
76
+ left: 0;
77
+ pointer-events: none;
78
+ }
79
+ [part='thumb-container'] {
80
+ outline: none;
81
+ position: absolute;
82
+ top: 0;
83
+ width: 100%;
84
+ }
85
+ [part='thumb'] {
86
+ position: absolute;
87
+ margin: 0 auto;
88
+ }
89
+ [part='pin'] {
90
+ display: flex;
91
+ position: absolute;
92
+ align-items: center;
93
+ justify-content: center;
94
+ z-index: 1;
95
+ }
96
+ :host([show-steps]) [part='step-container'] {
97
+ position: absolute;
98
+ left: 0;
99
+ width: 100%;
100
+ }
101
+ :host([show-steps]) [part='step'] {
102
+ width: 100%;
103
+ position: absolute;
104
+ left: 0;
105
+ }
106
+ [part='track-fill'] {
107
+ content: '';
108
+ position: absolute;
109
+ left: 0;
110
+ }
111
+ :host([show-steps][step='0']) [part='track-wrapper']::after {
112
+ width: 0;
113
+ }
114
+ `;
115
+ }
116
+ /**
117
+ * Converts value from string to number for calculations
118
+ * @returns value of input as a number
119
+ */
120
+ get valueNumber() {
121
+ const value = parseFloat(this.value);
122
+ if (!this.value || isNaN(value)) {
123
+ return 0;
124
+ }
125
+ return value;
126
+ }
127
+ /**
128
+ * Converts min value from string to number for calculations
129
+ * @returns minimum value of slider as a number
130
+ */
131
+ get minNumber() {
132
+ const min = parseFloat(this.min);
133
+ if (!this.min || isNaN(min)) {
134
+ return 0;
135
+ }
136
+ return min;
137
+ }
138
+ /**
139
+ * Converts max value from string to number for calculations
140
+ * @returns maximum value of slider as a number
141
+ */
142
+ get maxNumber() {
143
+ const max = parseFloat(this.max);
144
+ if (!this.max || isNaN(max)) {
145
+ return 100;
146
+ }
147
+ return max;
148
+ }
149
+ /**
150
+ * Converts step value from string to number for calculations
151
+ * @returns step value of slider as a number
152
+ */
153
+ get stepNumber() {
154
+ const step = parseFloat(this.step);
155
+ if (!this.step || isNaN(step)) {
156
+ return 1;
157
+ }
158
+ return step;
159
+ }
160
+ /**
161
+ * Compute and normalise step value for calculations
162
+ * @returns step value that should be inside the min / max boundary
163
+ */
164
+ get stepRange() {
165
+ const step = Math.abs(this.stepNumber);
166
+ if (step > this.maxNumber - this.minNumber && !isDecimalNumber(step)) {
167
+ // new step shouldn't be larger than slider
168
+ return Math.abs(this.maxNumber - this.minNumber);
169
+ }
170
+ return step;
171
+ }
172
+ /**
173
+ * Converts from value from string to number for calculations
174
+ * @returns from value of slider as a number
175
+ */
176
+ get fromNumber() {
177
+ const from = parseFloat(this.from);
178
+ if (!this.from || isNaN(from)) {
179
+ return this.minNumber;
180
+ }
181
+ return from;
182
+ }
183
+ /**
184
+ * Converts to value from string to number for calculations
185
+ * @returns to value of slider as a number
186
+ */
187
+ get toNumber() {
188
+ const to = parseFloat(this.to);
189
+ if (!this.to || isNaN(to)) {
190
+ return this.maxNumber;
191
+ }
192
+ return to;
193
+ }
194
+ /**
195
+ * Converts min-range from string to number for calculations
196
+ * @returns min-range of input as a number
197
+ */
198
+ get minRangeNumber() {
199
+ const minRange = parseFloat(this.minRange);
200
+ if (!this.minRange || isNaN(minRange)) {
201
+ return 0;
202
+ }
203
+ return minRange;
204
+ }
205
+ /**
206
+ * Number of decimal places used for displaying value
207
+ * Based on step or min decimal places
208
+ */
209
+ get decimalPlace() {
210
+ if (isDecimalNumber(this.stepRange) || isDecimalNumber(this.minNumber)) {
211
+ const stepDecimal = countDecimalPlace(this.stepRange);
212
+ const minDecimal = countDecimalPlace(this.minNumber);
213
+ return stepDecimal > minDecimal ? stepDecimal : minDecimal;
214
+ }
215
+ return 0;
216
+ }
217
+ /**
218
+ * Return hide/show input field state
219
+ * @returns true if showInputField value is exist
220
+ */
221
+ get isShowInputField() {
222
+ return this.showInputField !== null && this.showInputField !== undefined;
223
+ }
224
+ constructor() {
225
+ super();
226
+ /**
227
+ * Whether if the thumb is being drag
228
+ */
229
+ this.dragging = false;
230
+ this.valuePrevious = '';
231
+ this.fromPrevious = '';
232
+ this.toPrevious = '';
233
+ this.valuePreviousInput = ''; // dynamically accessed
234
+ this.fromPreviousInput = ''; // dynamically accessed
235
+ this.toPreviousInput = ''; // dynamically accessed
236
+ /** Aria label for 'to' and 'from' value thumb, resolved based on locale. */
237
+ this.toAriaLabel = SliderDataName.to;
238
+ this.fromAriaLabel = SliderDataName.from;
239
+ /**
240
+ * Specified size of increment or decrement jump between value.
241
+ */
242
+ this.step = '1';
243
+ /**
244
+ * Set minimum value of slider.
245
+ */
246
+ this.min = '0';
247
+ /**
248
+ * Set maximum value of slider.
249
+ */
250
+ this.max = '100';
251
+ /**
252
+ * Uses with `range`. Low value of slider in range mode.
253
+ */
254
+ this.from = '0';
255
+ /**
256
+ * Uses with `range`. High value of slider in range mode
257
+ */
258
+ this.to = '100';
259
+ /**
260
+ * Set slider appearances to show pin mode.
261
+ * @ignore
262
+ * NOTE: Pin isn't applicable in Halo. Hide this from document
263
+ */
264
+ this.pin = false;
265
+ /**
266
+ * Set slider to range mode. Instead of a single value, slider will provide `from` and `to`.
267
+ */
268
+ this.range = false;
269
+ /**
270
+ * Show steps marker on slider.
271
+ */
272
+ this.showSteps = false;
273
+ /**
274
+ * Show input number field.
275
+ */
276
+ this.showInputField = null;
277
+ /**
278
+ * Uses with `range`. Set minimum allowance value (distance) between `from` and `to`.
279
+ */
280
+ this.minRange = '0';
281
+ /**
282
+ * Slider element reference
283
+ */
284
+ this.sliderRef = createRef();
285
+ /**
286
+ * Slider's track reference
287
+ */
288
+ this.trackRef = createRef();
289
+ /**
290
+ * From value thumb reference, rendered only in range mode
291
+ */
292
+ this.fromThumbRef = createRef();
293
+ /**
294
+ * To value thumb reference, rendered only in range mode
295
+ */
296
+ this.toThumbRef = createRef();
297
+ /**
298
+ * Value thumb reference
299
+ */
300
+ this.valueThumbRef = createRef();
301
+ /**
302
+ * Current focused thumb
303
+ */
304
+ this.activeThumb = null;
305
+ /**
306
+ * Thumb that may involves data changes
307
+ */
308
+ this.changedThumb = null;
309
+ /**
310
+ * @ignore
311
+ */
312
+ this.onDrag = this.onDrag.bind(this);
313
+ /**
314
+ * @ignore
315
+ */
316
+ this.onDragStart = this.onDragStart.bind(this);
317
+ /**
318
+ * @ignore
319
+ */
320
+ this.onDragEnd = this.onDragEnd.bind(this);
321
+ /**
322
+ * @ignore
323
+ */
324
+ this.onKeyDown = this.onKeyDown.bind(this);
325
+ }
326
+ /**
327
+ * On first updated lifecycle
328
+ * @param changedProperties changed properties
329
+ * @returns {void}
330
+ */
331
+ firstUpdated(changedProperties) {
332
+ super.firstUpdated(changedProperties);
333
+ this.prepareValues();
334
+ this.prepareThumbs();
335
+ this.prepareSliderTrack();
336
+ }
337
+ /**
338
+ * Perform asynchronous update
339
+ * @returns promise
340
+ */
341
+ async performUpdate() {
342
+ [this.toAriaLabel, this.fromAriaLabel] = await Promise.all([
343
+ this.labelTPromise(SliderDataName.to.toUpperCase()),
344
+ this.labelTPromise(SliderDataName.from.toUpperCase())
345
+ ]);
346
+ void super.performUpdate();
347
+ }
348
+ /**
349
+ * Gets Slider Marker elements from the slot.
350
+ * @returns Array of Slider Marker elements.
351
+ */
352
+ getMarkerElements() {
353
+ const markers = [];
354
+ for (const child of this.children) {
355
+ if (child instanceof SliderMarker) {
356
+ markers.push(child);
357
+ }
358
+ }
359
+ return markers;
360
+ }
361
+ /**
362
+ * Gets the active marker based on the provided marker value.
363
+ * @param value - The marker value.
364
+ * @returns The active marker element.
365
+ */
366
+ getActiveMarker(value) {
367
+ for (const child of this.children) {
368
+ if (child instanceof SliderMarker && child.value === value) {
369
+ return child;
370
+ }
371
+ }
372
+ return null;
373
+ }
374
+ /**
375
+ * Finds the closest ancestor ef-slider-marker in the DOM tree.
376
+ * @param startingElement The HTML element to start searching from.
377
+ * @returns The found marker, or null if not found.
378
+ */
379
+ findClosestMarker(startingElement) {
380
+ if (!startingElement) {
381
+ return null;
382
+ }
383
+ let currentNode = startingElement;
384
+ const markers = this.getMarkerElements();
385
+ while (currentNode && currentNode !== this) {
386
+ if (currentNode instanceof SliderMarker && markers.includes(currentNode)) {
387
+ return currentNode;
388
+ }
389
+ currentNode = currentNode.parentNode;
390
+ }
391
+ return null;
392
+ }
393
+ /**
394
+ * Handles the slot change event by updating the position of markers.
395
+ * @returns {void}
396
+ */
397
+ onSlotChange() {
398
+ const markerList = this.getMarkerElements();
399
+ if (markerList.length < 1) {
400
+ return;
401
+ }
402
+ markerList.forEach((marker) => {
403
+ const markerValue = Number(marker.value);
404
+ const markerPosition = this.calculatePosition(markerValue);
405
+ marker.style.setProperty('left', `${markerPosition}%`);
406
+ if (!this.isShowInputField && !marker.labelAlign) {
407
+ if (markerPosition === 0) {
408
+ marker.labelAlign = 'left';
409
+ }
410
+ else if (markerPosition === 100) {
411
+ marker.labelAlign = 'right';
412
+ }
413
+ else {
414
+ marker.labelAlign = null;
415
+ }
416
+ }
417
+ if (markerPosition === 100) {
418
+ // Move the marker back by its own width
419
+ marker.style.setProperty('transform', 'translateX(-100%)');
420
+ }
421
+ });
422
+ }
423
+ /**
424
+ * @ignore
425
+ * On willUpdate lifecycle
426
+ * @param changedProperties changed properties
427
+ * @returns {void}
428
+ */
429
+ willUpdate(changedProperties) {
430
+ super.willUpdate(changedProperties);
431
+ if ((changedProperties.has('disabled') && changedProperties.get('disabled') !== undefined) ||
432
+ (changedProperties.has('readonly') && changedProperties.get('readonly') !== undefined)) {
433
+ this.prepareSliderTrack();
434
+ }
435
+ changedProperties.forEach((_, changedProperty) => {
436
+ if (['value', 'min', 'max', 'from', 'to', 'step', 'minRange'].includes(changedProperty)) {
437
+ this.showWarningInvalidProperty(changedProperty);
438
+ }
439
+ });
440
+ }
441
+ /**
442
+ * On updated lifecycle
443
+ * @param changedProperties changed properties
444
+ * @returns {void}
445
+ */
446
+ updated(changedProperties) {
447
+ super.updated(changedProperties);
448
+ if (changedProperties.has('value')) {
449
+ this.onValueChange();
450
+ }
451
+ if (changedProperties.has('min')) {
452
+ this.onMinChange(changedProperties.get('min'));
453
+ }
454
+ if (changedProperties.has('max')) {
455
+ this.onMaxChange(changedProperties.get('max'));
456
+ }
457
+ if (this.range) {
458
+ if (changedProperties.has('from')) {
459
+ this.onFromValueChange();
460
+ }
461
+ if (changedProperties.has('to')) {
462
+ this.onToValueChange();
463
+ }
464
+ }
465
+ if (changedProperties.has('step')) {
466
+ this.onStepChange();
467
+ }
468
+ if (changedProperties.has('minRange')) {
469
+ this.onMinRangeChange();
470
+ }
471
+ if (changedProperties.has('range')) {
472
+ this.prepareValues();
473
+ this.prepareThumbs();
474
+ }
475
+ }
476
+ /**
477
+ * Update the ARIA value text for a given thumb.
478
+ *
479
+ * @param thumbRef - The reference to the thumb element.
480
+ * @param markerValue - The value associated with the marker.
481
+ * @returns {void}
482
+ */
483
+ updateAriaValueText(thumbRef, markerValue) {
484
+ const thumbElement = thumbRef.value;
485
+ if (!thumbElement) {
486
+ return;
487
+ }
488
+ const activeMarker = this.getActiveMarker(markerValue);
489
+ const markerLabel = activeMarker?.textContent;
490
+ const ariaValueText = markerLabel ? `${markerLabel} ${markerValue}` : null;
491
+ if (ariaValueText) {
492
+ thumbElement.setAttribute('aria-valuetext', ariaValueText);
493
+ }
494
+ else {
495
+ thumbElement.removeAttribute('aria-valuetext');
496
+ }
497
+ }
498
+ /**
499
+ * Show Warning a warning message invalid property
500
+ * @param propName value for checking
501
+ * @returns {void}
502
+ */
503
+ showWarningInvalidProperty(propName) {
504
+ let isValid = true;
505
+ let message = '';
506
+ if (propName === 'value') {
507
+ isValid = this.isValueInBoundary(this.valueNumber, '');
508
+ message = 'value should be between min and max.';
509
+ }
510
+ else if (propName === 'min') {
511
+ isValid = this.minNumber <= this.maxNumber;
512
+ message = 'value should be less than max.';
513
+ }
514
+ else if (propName === 'max') {
515
+ isValid = this.maxNumber >= this.minNumber;
516
+ message = 'value should be more than min.';
517
+ }
518
+ else if (propName === 'from' && this.range) {
519
+ isValid = this.fromNumber >= this.minNumber && this.fromNumber <= this.toNumber;
520
+ message = 'value should be more than min and less than to.';
521
+ }
522
+ else if (propName === 'to' && this.range) {
523
+ isValid = this.toNumber <= this.maxNumber && this.toNumber >= this.fromNumber;
524
+ message = 'value should be less than max and more than from.';
525
+ }
526
+ else if (propName === 'step') {
527
+ isValid = this.maxNumber - this.minNumber >= this.stepNumber;
528
+ message = 'value should be between min and max.';
529
+ }
530
+ else if (propName === 'minRange' && this.minRangeNumber > 0) {
531
+ const distanceFromTo = Math.abs(this.toNumber - this.fromNumber);
532
+ const distanceMinMax = Math.abs(this.maxNumber - this.minNumber);
533
+ isValid = distanceMinMax >= this.minRangeNumber && distanceFromTo >= this.minRangeNumber;
534
+ message = 'value should be less than distance from and to, min and max.';
535
+ }
536
+ if (!isValid) {
537
+ new WarningNotice(`${this.localName}: Invalid ${propName} provided, The correct ${propName} ${message}`).show();
538
+ }
539
+ }
540
+ /**
541
+ * Initialises slider value properties
542
+ * @returns {void}
543
+ */
544
+ prepareValues() {
545
+ if (this.minNumber !== this.maxNumber) {
546
+ if (this.range) {
547
+ if (this.minRangeNumber) {
548
+ const distanceFromTo = Math.abs(this.toNumber - this.fromNumber);
549
+ const clampValueFrom = this.toNumber - this.minRangeNumber;
550
+ if (this.minRangeNumber > distanceFromTo) {
551
+ if (clampValueFrom < this.minNumber) {
552
+ this.to = (this.fromNumber + this.minRangeNumber).toString();
553
+ }
554
+ else {
555
+ this.from = clampValueFrom.toString();
556
+ }
557
+ }
558
+ }
559
+ else {
560
+ this.from = clamp(this.fromNumber, this.minNumber, this.toNumber);
561
+ this.to = clamp(this.toNumber, this.fromNumber, this.maxNumber);
562
+ }
563
+ }
564
+ else {
565
+ this.value = clamp(this.valueNumber, this.minNumber, this.maxNumber);
566
+ }
567
+ }
568
+ else if (this.range) {
569
+ this.from = this.min;
570
+ this.to = this.max;
571
+ }
572
+ else {
573
+ this.value = this.min;
574
+ }
575
+ this.valuePrevious = this.value;
576
+ this.toPrevious = this.to;
577
+ this.fromPrevious = this.from;
578
+ }
579
+ /**
580
+ * Add event listeners to thumbs depending on mode
581
+ * @returns {void}
582
+ */
583
+ prepareThumbs() {
584
+ if (this.range) {
585
+ this.fromThumbRef.value?.addEventListener('keydown', this.onKeyDown);
586
+ this.fromThumbRef.value?.addEventListener('drag', preventDefault);
587
+ this.fromThumbRef.value?.addEventListener('dragstart', preventDefault);
588
+ this.fromThumbRef.value?.addEventListener('dragend', preventDefault);
589
+ this.toThumbRef.value?.addEventListener('keydown', this.onKeyDown);
590
+ this.toThumbRef.value?.addEventListener('drag', preventDefault);
591
+ this.toThumbRef.value?.addEventListener('dragstart', preventDefault);
592
+ this.toThumbRef.value?.addEventListener('dragend', preventDefault);
593
+ }
594
+ else {
595
+ this.valueThumbRef.value?.addEventListener('keydown', this.onKeyDown);
596
+ this.valueThumbRef.value?.addEventListener('drag', preventDefault);
597
+ this.valueThumbRef.value?.addEventListener('dragstart', preventDefault);
598
+ this.valueThumbRef.value?.addEventListener('dragend', preventDefault);
599
+ }
600
+ }
601
+ /**
602
+ * Add or remove event listener on slider track depending on slider disabled and readonly state
603
+ * @returns {void}
604
+ */
605
+ prepareSliderTrack() {
606
+ if (this.disabled || this.readonly) {
607
+ this.sliderRef.value?.removeEventListener('mousedown', this.onDragStart);
608
+ this.sliderRef.value?.removeEventListener('touchstart', this.onDragStart);
609
+ }
610
+ else {
611
+ this.sliderRef.value?.addEventListener('mousedown', this.onDragStart, { passive: true });
612
+ this.sliderRef.value?.addEventListener('touchstart', this.onDragStart, { passive: true });
613
+ }
614
+ }
615
+ /**
616
+ * Get slider data name from keyboard event target
617
+ * @param target target element
618
+ * @returns Slider data name
619
+ */
620
+ getThumbName(target) {
621
+ switch (target) {
622
+ case this.fromThumbRef.value:
623
+ return SliderDataName.from;
624
+ case this.toThumbRef.value:
625
+ return SliderDataName.to;
626
+ case this.valueThumbRef.value:
627
+ return SliderDataName.value;
628
+ default:
629
+ return null;
630
+ }
631
+ }
632
+ /**
633
+ * Handles key down event on thumbs
634
+ * @param event Keyboard event
635
+ * @returns {void}
636
+ */
637
+ onKeyDown(event) {
638
+ if (this.readonly || event.defaultPrevented || this.minNumber >= this.maxNumber) {
639
+ return;
640
+ }
641
+ // Ignore special keys
642
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
643
+ return;
644
+ }
645
+ const thumbName = this.getThumbName(event.target);
646
+ if (!thumbName) {
647
+ return;
648
+ }
649
+ this.changedThumb = event.target;
650
+ switch (event.key) {
651
+ case 'ArrowDown':
652
+ case 'ArrowLeft':
653
+ this.onApplyStep(Direction.Down, thumbName);
654
+ break;
655
+ case 'ArrowUp':
656
+ case 'ArrowRight':
657
+ this.onApplyStep(Direction.Up, thumbName);
658
+ break;
659
+ case 'Home':
660
+ this.onApplyMin(thumbName);
661
+ break;
662
+ case 'End':
663
+ this.onApplyMax(thumbName);
664
+ break;
665
+ default:
666
+ return;
667
+ }
668
+ event.preventDefault();
669
+ }
670
+ /**
671
+ * Set thumb to minimum value possible
672
+ * @param data type of data to change
673
+ * @returns {void}
674
+ */
675
+ onApplyMin(data) {
676
+ let position;
677
+ if (data === SliderDataName.from || data === SliderDataName.value) {
678
+ position = this.calculatePosition(this.minNumber, 1);
679
+ }
680
+ else {
681
+ position = this.calculatePosition(this.fromNumber + this.minRangeNumber, 1);
682
+ }
683
+ const possibleValue = this.getNearestPossibleValue(position);
684
+ const value = this.getValueFromPosition(possibleValue);
685
+ this.persistChangedData(value);
686
+ this.dispatchDataChangedEvent();
687
+ }
688
+ /**
689
+ * Set thumb to maximum value possible
690
+ * @param data type of data to change
691
+ * @returns {void}
692
+ */
693
+ onApplyMax(data) {
694
+ let position;
695
+ if (data === SliderDataName.to || data === SliderDataName.value) {
696
+ position = this.calculatePosition(this.maxNumber, 1);
697
+ }
698
+ else {
699
+ position = this.calculatePosition(this.toNumber - this.minRangeNumber, 1);
700
+ }
701
+ const possibleValue = this.getNearestPossibleValue(position);
702
+ const value = this.getValueFromPosition(possibleValue);
703
+ this.persistChangedData(value);
704
+ this.dispatchDataChangedEvent();
705
+ }
706
+ /**
707
+ * Increase or decrease value depending on direction
708
+ * Then fires value change event
709
+ * @param direction Up or Down
710
+ * @param data type of data to change
711
+ * @returns {void}
712
+ */
713
+ onApplyStep(direction, data) {
714
+ // Get current thumb position and step in percentage format
715
+ const thumbPosition = this.calculatePosition(this[`${data}Number`], 1);
716
+ const step = this.calculatePosition(this.minNumber + this.stepRange, 1);
717
+ const possibleValue = direction === Direction.Up ? thumbPosition + step : thumbPosition - step;
718
+ const nearestPossibleValue = this.getNearestPossibleValue(possibleValue);
719
+ const value = this.getValueFromPosition(nearestPossibleValue);
720
+ this.persistChangedData(value);
721
+ this.dispatchDataChangedEvent();
722
+ }
723
+ /**
724
+ * Calculate thumb position based on value and multiplier
725
+ * @param value decimal fraction value
726
+ * @param multiplier defaults to 100
727
+ * @returns thumb position as a fraction of 100
728
+ */
729
+ calculatePosition(value, multiplier = 100) {
730
+ const position = Math.abs(((value - this.minNumber) / (this.maxNumber - this.minNumber)) * multiplier);
731
+ if (position > multiplier) {
732
+ return multiplier;
733
+ }
734
+ return position;
735
+ }
736
+ /**
737
+ * Adds active attribute used in styling
738
+ * @param event focus event
739
+ * @returns {void}
740
+ */
741
+ onThumbFocus(event) {
742
+ this.activeThumb = event.target;
743
+ }
744
+ /**
745
+ * Removes active attribute used in styling
746
+ * @param event focus event
747
+ * @returns {void}
748
+ */
749
+ onThumbBlur() {
750
+ this.activeThumb = null;
751
+ }
752
+ /**
753
+ * On number-field blur
754
+ * @param event focus event
755
+ * @returns {void}
756
+ */
757
+ onNumberFieldBlur(event) {
758
+ if (this.readonly) {
759
+ return;
760
+ }
761
+ const { value, name } = event.target;
762
+ const currentData = name;
763
+ const previousData = `${name}Previous`;
764
+ if (value && this[currentData] !== value) {
765
+ this.updateNotifyProperty(currentData, value);
766
+ this[previousData] = value;
767
+ }
768
+ event.preventDefault();
769
+ }
770
+ /**
771
+ * On number-field input
772
+ * @param event input event
773
+ * @returns {void}
774
+ */
775
+ onNumberFieldInput(event) {
776
+ if (this.readonly) {
777
+ return;
778
+ }
779
+ const { value, name } = event.target;
780
+ const currentData = name;
781
+ this.notifyPropertyInput(currentData, value);
782
+ event.preventDefault();
783
+ event.stopPropagation();
784
+ }
785
+ /**
786
+ * On number-field keydown
787
+ * @param event keyboard event
788
+ * @returns {void}
789
+ */
790
+ onNumberFieldKeyDown(event) {
791
+ if (this.readonly || this.disabled) {
792
+ return;
793
+ }
794
+ if (event.key === ' ' || event.key === 'Enter') {
795
+ event.target.blur();
796
+ }
797
+ }
798
+ /**
799
+ * Update notify property by input name attribute
800
+ * @param name name input attribute
801
+ * @param value input value
802
+ * @returns {void}
803
+ */
804
+ updateNotifyProperty(name, value) {
805
+ let shouldUpdate = false;
806
+ if (name === SliderDataName.to) {
807
+ shouldUpdate = this.isValueInBoundary(Number(value), SliderDataName.to);
808
+ }
809
+ else {
810
+ shouldUpdate = this.isValueInBoundary(Number(value), '');
811
+ }
812
+ if (shouldUpdate) {
813
+ this[name] = value;
814
+ this.notifyPropertyChange(name, value);
815
+ }
816
+ else {
817
+ const inputName = `${name}Input`;
818
+ this[inputName].value = this[name];
819
+ }
820
+ }
821
+ /**
822
+ * Dispatch data {value, from, to} changed event
823
+ * @returns {void}
824
+ */
825
+ dispatchDataChangedEvent() {
826
+ const name = this.changedThumb?.getAttribute('name') || '';
827
+ const currentData = name;
828
+ const previousData = `${name}Previous`;
829
+ // Dispatch event only when value or from or to changed
830
+ if (this[previousData] !== this[currentData]) {
831
+ this.notifyPropertyChange(name, this[currentData]);
832
+ this[previousData] = this[currentData];
833
+ }
834
+ }
835
+ /**
836
+ * Dispatch data {input, from-input, to-input} changing event
837
+ * @returns {void}
838
+ */
839
+ dispatchDataInputEvent() {
840
+ const name = this.changedThumb?.getAttribute('name') || '';
841
+ const currentData = name;
842
+ const previousDataInput = `${name}PreviousInput`;
843
+ // Dispatch event only when changing the input value
844
+ if (this[previousDataInput] !== this[currentData]) {
845
+ this.notifyPropertyInput(name, this[currentData]);
846
+ this[previousDataInput] = this[currentData];
847
+ }
848
+ }
849
+ /**
850
+ * Start dragging event on slider
851
+ * @param event event dragstart
852
+ * @returns {void}
853
+ */
854
+ onDragStart(event) {
855
+ this.dragging = true;
856
+ this.classList.add('grabbable');
857
+ if (this.range) {
858
+ const mousePosition = this.getMousePosition(event);
859
+ const relativeMousePosition = (this.maxNumber - this.minNumber) * mousePosition + this.minNumber;
860
+ const distanceFrom = Math.abs(relativeMousePosition - this.fromNumber);
861
+ const distanceTo = Math.abs(relativeMousePosition - this.toNumber);
862
+ if (distanceFrom < distanceTo) {
863
+ this.changedThumb = this.fromThumbRef.value;
864
+ this.fromPreviousInput = this.from;
865
+ }
866
+ else if (distanceFrom > distanceTo) {
867
+ this.changedThumb = this.toThumbRef.value;
868
+ this.toPreviousInput = this.to;
869
+ }
870
+ // When from === to, use latest value of changedThumb and z-index will determine thumb on top
871
+ }
872
+ else {
873
+ this.changedThumb = this.valueThumbRef.value;
874
+ this.valuePreviousInput = this.value;
875
+ }
876
+ this.onDrag(event);
877
+ this.validateNumberField();
878
+ if (event.changedTouches) {
879
+ this.addEventListener('touchmove', this.onDrag);
880
+ this.addEventListener('touchend', this.onDragEnd);
881
+ }
882
+ else {
883
+ window.addEventListener('mousemove', this.onDrag);
884
+ window.addEventListener('mouseup', this.onDragEnd);
885
+ }
886
+ }
887
+ /**
888
+ * Get mouse position in percentage value
889
+ * @param event event mousemove and touchmove
890
+ * @returns mouse position by percentage
891
+ */
892
+ getMousePosition(event) {
893
+ const sliderRect = this.trackRef.value?.getBoundingClientRect();
894
+ if (!sliderRect) {
895
+ return 1;
896
+ }
897
+ // check drag desktop or mobile
898
+ const pageX = event.changedTouches
899
+ ? event.changedTouches[0].pageX
900
+ : event.pageX;
901
+ const positionSize = pageX - sliderRect.left;
902
+ if (positionSize <= sliderRect.width) {
903
+ return Math.min(Math.max((pageX - sliderRect.left) / sliderRect.width, 0), 1);
904
+ }
905
+ else {
906
+ return 1;
907
+ }
908
+ }
909
+ /**
910
+ * Dragging after on dragging start event
911
+ * @param event event mousemove and touchmove
912
+ * @returns {void}
913
+ */
914
+ onDrag(event) {
915
+ if (this.minNumber === this.maxNumber) {
916
+ return;
917
+ }
918
+ const marker = this.findClosestMarker(event.target);
919
+ const thumbPosition = marker
920
+ ? this.calculatePosition(parseFloat(marker.value), 1)
921
+ : this.getMousePosition(event);
922
+ const nearestValue = this.getNearestPossibleValue(thumbPosition);
923
+ if (nearestValue > 1) {
924
+ return;
925
+ }
926
+ const newThumbPosition = this.stepRange !== 0 ? nearestValue : thumbPosition;
927
+ const value = this.getValueFromPosition(newThumbPosition);
928
+ this.persistChangedData(value);
929
+ this.dispatchDataInputEvent();
930
+ }
931
+ /**
932
+ * Saves changed data into correct field
933
+ * @param value value of changed data
934
+ * @returns {void}
935
+ */
936
+ persistChangedData(value) {
937
+ const newValue = this.format(value);
938
+ if (this.range) {
939
+ if (this.changedThumb === this.fromThumbRef.value) {
940
+ this.from = this.validateFrom(Number(newValue)).toString();
941
+ }
942
+ else {
943
+ this.to = this.validateTo(Number(newValue)).toString();
944
+ }
945
+ }
946
+ else {
947
+ this.value = newValue;
948
+ }
949
+ }
950
+ /**
951
+ * Validate and return FROM value within available range
952
+ * @param value from value
953
+ * @returns validated from value
954
+ */
955
+ validateFrom(value) {
956
+ const valueFrom = value + this.minRangeNumber;
957
+ if (valueFrom < this.toNumber && valueFrom >= this.minNumber) {
958
+ return value;
959
+ }
960
+ return this.toNumber - this.minRangeNumber;
961
+ }
962
+ /**
963
+ * Validate and return TO value within available range
964
+ * @param value to value
965
+ * @returns validated to value.
966
+ */
967
+ validateTo(value) {
968
+ const valueTo = value - this.minRangeNumber;
969
+ if (valueTo > this.fromNumber && valueTo <= this.maxNumber) {
970
+ return value;
971
+ }
972
+ return this.fromNumber + this.minRangeNumber;
973
+ }
974
+ /**
975
+ * Validate number field from changed thumb
976
+ * @returns {void}
977
+ */
978
+ validateNumberField() {
979
+ if (this.isShowInputField) {
980
+ const name = this.changedThumb?.getAttribute('name');
981
+ const numberField = this[`${name}Input`];
982
+ requestAnimationFrame(() => numberField.reportValidity());
983
+ }
984
+ }
985
+ /**
986
+ * Calculate the nearest possible step value depending on step interval
987
+ * @param thumbPosition current thumb position in fraction
988
+ * @returns nearest available slider step in fraction
989
+ */
990
+ getNearestPossibleValue(thumbPosition) {
991
+ const stepSize = this.calculatePosition(this.minNumber + this.stepRange, 1);
992
+ const nearestValue = Math.round(thumbPosition / stepSize) * stepSize;
993
+ if (thumbPosition <= nearestValue + stepSize / 2) {
994
+ if (nearestValue <= 1) {
995
+ return nearestValue;
996
+ }
997
+ return nearestValue - stepSize;
998
+ }
999
+ return nearestValue + stepSize;
1000
+ }
1001
+ /**
1002
+ * Get slider value from thumb position
1003
+ * @param position thumb position
1004
+ * @returns calculated value
1005
+ */
1006
+ getValueFromPosition(position) {
1007
+ const value = this.minNumber + position * (this.maxNumber - this.minNumber);
1008
+ // if value is outside boundary, set to boundary
1009
+ if (value >= this.maxNumber) {
1010
+ return this.maxNumber;
1011
+ }
1012
+ else if (value <= this.minNumber) {
1013
+ return this.minNumber;
1014
+ }
1015
+ else {
1016
+ return value;
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Format value to display in both integer and fraction cases
1021
+ * @param value value before use display
1022
+ * @returns formatted value
1023
+ */
1024
+ format(value) {
1025
+ if (isDecimalNumber(value) && countDecimalPlace(value) > this.decimalPlace) {
1026
+ return value.toFixed(this.decimalPlace);
1027
+ }
1028
+ return value.toString();
1029
+ }
1030
+ /**
1031
+ * End dragging event and remove dragging event
1032
+ * @param event event mouseup and touchmove
1033
+ * @returns {void}
1034
+ */
1035
+ onDragEnd(event) {
1036
+ if (this.dragging) {
1037
+ this.dragging = false;
1038
+ const touchEvent = event;
1039
+ if (touchEvent.changedTouches) {
1040
+ this.removeEventListener('touchmove', this.onDrag);
1041
+ this.removeEventListener('touchend', this.onDragEnd);
1042
+ }
1043
+ else {
1044
+ window.removeEventListener('mousemove', this.onDrag);
1045
+ window.removeEventListener('mouseup', this.onDragEnd);
1046
+ }
1047
+ this.classList.remove('grabbable');
1048
+ if (this.classList.length === 0) {
1049
+ this.removeAttribute('class');
1050
+ }
1051
+ if (!touchEvent.changedTouches) {
1052
+ event.preventDefault();
1053
+ }
1054
+ this.dispatchDataChangedEvent();
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Value observer
1059
+ * @returns {void}
1060
+ */
1061
+ onValueChange() {
1062
+ if (this.readonly) {
1063
+ const thumbPosition = this.calculatePosition(this.valueNumber, 1);
1064
+ const nearestPossibleValue = this.getNearestPossibleValue(thumbPosition);
1065
+ const value = this.getValueFromPosition(this.stepRange === 0 ? thumbPosition : nearestPossibleValue);
1066
+ this.value = this.format(value);
1067
+ }
1068
+ else if (this.isValueInBoundary(this.valueNumber, '')) {
1069
+ this.value = this.format(this.valueNumber);
1070
+ }
1071
+ else if (this.valueNumber < this.minNumber) {
1072
+ this.value = this.min;
1073
+ }
1074
+ else if (this.valueNumber > this.maxNumber) {
1075
+ this.value = this.max;
1076
+ }
1077
+ if (!this.dragging) {
1078
+ // Update internal `valuePrevious` when `value` was programmatically set by user.
1079
+ this.valuePrevious = this.value;
1080
+ }
1081
+ this.updateAriaValueText(this.valueThumbRef, this.value);
1082
+ }
1083
+ /**
1084
+ * From value observer
1085
+ * @returns {void}
1086
+ */
1087
+ onFromValueChange() {
1088
+ if (this.isValueInBoundary(this.fromNumber, SliderDataName.from)) {
1089
+ this.from = this.format(this.fromNumber);
1090
+ }
1091
+ else {
1092
+ // if value is outside boundary, set to boundary
1093
+ this.from = clamp(this.fromNumber, this.minNumber, this.maxNumber);
1094
+ if (this.fromNumber > this.toNumber) {
1095
+ this.from = this.to;
1096
+ }
1097
+ if (this.minRangeNumber) {
1098
+ const distanceFromTo = Math.abs(this.toNumber - this.fromNumber);
1099
+ const distanceMin = this.toNumber - this.minRangeNumber;
1100
+ if (this.minRangeNumber > distanceFromTo && distanceMin > this.minNumber) {
1101
+ this.from = distanceMin.toString();
1102
+ }
1103
+ }
1104
+ }
1105
+ if (!this.dragging) {
1106
+ this.fromPrevious = this.from;
1107
+ }
1108
+ this.updateAriaValueText(this.fromThumbRef, this.from);
1109
+ }
1110
+ /**
1111
+ * Check if value is inside min / max boundary
1112
+ * @param value value is checking
1113
+ * @param valueFor notation variable binding if range === true
1114
+ * @returns true if value and step inside a boundary
1115
+ */
1116
+ isValueInBoundary(value, valueFor) {
1117
+ if (this.minNumber > this.maxNumber) {
1118
+ return false;
1119
+ }
1120
+ // Check if value is in range
1121
+ if (value < this.minNumber || value > this.maxNumber) {
1122
+ return false;
1123
+ }
1124
+ if (this.range) {
1125
+ if (valueFor === SliderDataName.to && value < this.fromNumber + this.minRangeNumber) {
1126
+ return false;
1127
+ }
1128
+ else if (valueFor === SliderDataName.from && value > this.toNumber - this.minRangeNumber) {
1129
+ return false;
1130
+ }
1131
+ }
1132
+ return true;
1133
+ }
1134
+ /**
1135
+ * To value observer
1136
+ * @returns {void}
1137
+ */
1138
+ onToValueChange() {
1139
+ if (this.isValueInBoundary(this.toNumber, SliderDataName.to)) {
1140
+ this.to = this.format(this.toNumber);
1141
+ }
1142
+ else {
1143
+ // if value is outside boundary, set to boundary
1144
+ this.to = clamp(this.toNumber, this.minNumber, this.maxNumber);
1145
+ if (this.toNumber < this.fromNumber) {
1146
+ this.to = this.from;
1147
+ }
1148
+ if (this.minRangeNumber) {
1149
+ const distanceFromTo = Math.abs(this.toNumber - this.fromNumber);
1150
+ const distanceMax = this.fromNumber + this.minRangeNumber;
1151
+ if (this.minRangeNumber > distanceFromTo && distanceMax < this.maxNumber) {
1152
+ this.to = distanceMax.toString();
1153
+ }
1154
+ }
1155
+ }
1156
+ if (!this.dragging) {
1157
+ this.toPrevious = this.to;
1158
+ }
1159
+ this.updateAriaValueText(this.toThumbRef, this.to);
1160
+ }
1161
+ /**
1162
+ * Step observer
1163
+ * @returns {void}
1164
+ */
1165
+ onStepChange() {
1166
+ this.step = this.stepNumber.toString();
1167
+ }
1168
+ /**
1169
+ * Min range observer
1170
+ * @returns {void}
1171
+ */
1172
+ onMinRangeChange() {
1173
+ const valueMinRange = Math.abs(this.minRangeNumber);
1174
+ const maximumRangeMinMax = Math.abs(this.maxNumber - this.minNumber);
1175
+ if (valueMinRange && valueMinRange >= this.stepNumber) {
1176
+ if (valueMinRange <= maximumRangeMinMax) {
1177
+ this.minRange = valueMinRange.toString();
1178
+ }
1179
+ else {
1180
+ this.minRange = maximumRangeMinMax.toString();
1181
+ this.from = this.min;
1182
+ this.to = this.max;
1183
+ }
1184
+ }
1185
+ else {
1186
+ // Reset min-range when min-range less step
1187
+ this.minRange = '0';
1188
+ }
1189
+ }
1190
+ /**
1191
+ * Min observer
1192
+ * @param oldValue old value of min property
1193
+ * @returns {void}
1194
+ */
1195
+ onMinChange(oldValue) {
1196
+ this.min = this.minNumber.toString();
1197
+ if (this.minNumber > this.maxNumber) {
1198
+ this.min = this.max;
1199
+ return;
1200
+ }
1201
+ if (this.range) {
1202
+ if (this.minNumber <= this.toNumber - this.minRangeNumber) {
1203
+ this.from = clamp(this.fromNumber, this.minNumber, this.toNumber);
1204
+ }
1205
+ else if (oldValue) {
1206
+ this.min = oldValue;
1207
+ }
1208
+ }
1209
+ else {
1210
+ this.value = clamp(this.valueNumber, this.minNumber, this.maxNumber);
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Max observer
1215
+ * @param oldValue old value of max property
1216
+ * @returns {void}
1217
+ */
1218
+ onMaxChange(oldValue) {
1219
+ this.max = this.maxNumber.toString();
1220
+ if (this.maxNumber < this.minNumber) {
1221
+ this.max = this.min;
1222
+ return;
1223
+ }
1224
+ if (this.range) {
1225
+ if (this.maxNumber >= this.fromNumber + this.minRangeNumber) {
1226
+ this.to = clamp(this.toNumber, this.fromNumber, this.maxNumber);
1227
+ }
1228
+ else if (oldValue) {
1229
+ this.max = oldValue;
1230
+ }
1231
+ }
1232
+ else {
1233
+ this.value = clamp(this.valueNumber, this.minNumber, this.maxNumber);
1234
+ }
1235
+ }
1236
+ /**
1237
+ * Implement `render` Track template.
1238
+ * @param range show range slider
1239
+ * @returns Track template
1240
+ */
1241
+ renderTrack(range) {
1242
+ const stepContainerSize = this.calculatePosition(this.minNumber + this.stepNumber);
1243
+ const translateX = stepContainerSize / 2;
1244
+ const stepsStyle = {
1245
+ transform: `translateX(${translateX}%)`,
1246
+ backgroundSize: `${stepContainerSize}% 100%`
1247
+ };
1248
+ const stepContainerStyle = { transform: `translateX(-${translateX}%)` };
1249
+ const trackFillStyle = range
1250
+ ? {
1251
+ width: `${this.calculatePosition(this.toNumber) - this.calculatePosition(this.fromNumber)}%`,
1252
+ left: `${this.calculatePosition(this.fromNumber)}%`
1253
+ }
1254
+ : { width: `${this.calculatePosition(Number(this.value))}%` };
1255
+ return html `
1256
+ <div part="track-wrapper" ${ref(this.trackRef)}>
1257
+ <div part="step-container" style=${styleMap(stepContainerStyle)}>
1258
+ <div part="step" style=${styleMap(stepsStyle)}></div>
1259
+ </div>
1260
+ <div part="track-fill" style=${styleMap(trackFillStyle)}></div>
1261
+ </div>
1262
+ `;
1263
+ }
1264
+ /**
1265
+ * Implement `render` Thumb template.
1266
+ * @param value thumb value in track
1267
+ * @param thumbPosition thumb position in track
1268
+ * @param name name of thumb to render
1269
+ * @returns Thumb template
1270
+ */
1271
+ thumbTemplate(value, thumbPosition, name) {
1272
+ const isActive = this.activeThumb?.getAttribute('name') === name;
1273
+ const isChanged = this.changedThumb?.getAttribute('name') === name;
1274
+ let valueNow = this.value;
1275
+ let valueMin = this.min;
1276
+ let valueMax = this.max;
1277
+ let label = this.inputAriaLabel || '';
1278
+ if (this.range) {
1279
+ if (name === SliderDataName.from) {
1280
+ valueNow = this.from;
1281
+ valueMax = String(this.toNumber - this.minRangeNumber);
1282
+ label = label ? `${label} ${this.fromAriaLabel}` : this.fromAriaLabel;
1283
+ }
1284
+ else {
1285
+ valueNow = this.to;
1286
+ valueMin = String(this.fromNumber + this.minRangeNumber);
1287
+ label = label ? `${label} ${this.toAriaLabel}` : this.toAriaLabel;
1288
+ }
1289
+ }
1290
+ const thumbZIndex = this.range ? (isChanged ? '4' : '3') : null;
1291
+ const thumbStyle = { left: `${thumbPosition}%`, zIndex: thumbZIndex };
1292
+ return html `
1293
+ <div
1294
+ ${ref(this[`${name}ThumbRef`])}
1295
+ @focus=${this.onThumbFocus}
1296
+ @blur=${this.onThumbBlur}
1297
+ active=${isActive || nothing}
1298
+ name="${name}"
1299
+ role="slider"
1300
+ aria-label="${label || nothing}"
1301
+ tabindex="1"
1302
+ aria-valuemin=${valueMin}
1303
+ aria-valuemax=${valueMax}
1304
+ aria-valuenow=${valueNow}
1305
+ part="thumb-container"
1306
+ style=${styleMap(thumbStyle)}
1307
+ >
1308
+ <div part="pin">
1309
+ <span part="pin-value-marker">${value}</span>
1310
+ </div>
1311
+ <div part="thumb" draggable="true"></div>
1312
+ </div>
1313
+ `;
1314
+ }
1315
+ /**
1316
+ * Renders thumb template depending on parameter
1317
+ * @param from thumb value start in track
1318
+ * @param to thumb value end in track (optional)
1319
+ * @returns Thumbs template
1320
+ */
1321
+ renderThumb(from, to) {
1322
+ return html `
1323
+ ${this.thumbTemplate(from, this.calculatePosition(from), to !== undefined ? SliderDataName.from : SliderDataName.value)}
1324
+ ${to !== undefined ? this.thumbTemplate(to, this.calculatePosition(to), SliderDataName.to) : nothing}
1325
+ `;
1326
+ }
1327
+ /**
1328
+ * Implement `render` number field has template.
1329
+ * @param value value in the slider for binding in the input value
1330
+ * @param name name input value
1331
+ * @returns {TemplateResult} number field template
1332
+ */
1333
+ renderNumberField(value, name) {
1334
+ /**
1335
+ * Hiding number-field from screen reader and tabbing sequence because it's redundant,
1336
+ * and complicate the accessibility implementation.
1337
+ */
1338
+ return html `
1339
+ <ef-number-field
1340
+ tabindex="-1"
1341
+ aria-hidden="true"
1342
+ @blur=${this.onNumberFieldBlur}
1343
+ @keydown=${this.onNumberFieldKeyDown}
1344
+ @input=${this.onNumberFieldInput}
1345
+ part="input"
1346
+ name="${name}"
1347
+ no-spinner
1348
+ .value="${value}"
1349
+ min="${this.min}"
1350
+ max="${this.max}"
1351
+ step="${this.step}"
1352
+ ?disabled="${this.disabled}"
1353
+ ?readonly="${this.readonly || this.showInputField === 'readonly'}"
1354
+ ></ef-number-field>
1355
+ `;
1356
+ }
1357
+ /**
1358
+ * Implement `render` slider template.
1359
+ * @returns Slider template
1360
+ */
1361
+ render() {
1362
+ return html `
1363
+ ${this.range && this.isShowInputField ? this.renderNumberField(this.from, SliderDataName.from) : null}
1364
+ <div part="slider-wrapper">
1365
+ <div part="slider" ${ref(this.sliderRef)}>
1366
+ ${this.renderTrack(this.range)}
1367
+ <slot @slotchange=${this.onSlotChange}></slot>
1368
+ ${this.range
1369
+ ? this.renderThumb(this.fromNumber, this.toNumber)
1370
+ : this.renderThumb(this.valueNumber)}
1371
+ </div>
1372
+ </div>
1373
+ ${this.range && this.isShowInputField ? this.renderNumberField(this.to, SliderDataName.to) : null}
1374
+ ${!this.range && this.isShowInputField
1375
+ ? this.renderNumberField(this.value, SliderDataName.value)
1376
+ : null}
1377
+ `;
1378
+ }
1379
+ };
1380
+ __decorate([
1381
+ property({ type: String })
1382
+ ], Slider.prototype, "step", void 0);
1383
+ __decorate([
1384
+ property({ type: String })
1385
+ ], Slider.prototype, "min", void 0);
1386
+ __decorate([
1387
+ property({ type: String })
1388
+ ], Slider.prototype, "max", void 0);
1389
+ __decorate([
1390
+ property({ type: String })
1391
+ ], Slider.prototype, "from", void 0);
1392
+ __decorate([
1393
+ property({ type: String })
1394
+ ], Slider.prototype, "to", void 0);
1395
+ __decorate([
1396
+ property({ type: Boolean, reflect: true })
1397
+ ], Slider.prototype, "pin", void 0);
1398
+ __decorate([
1399
+ property({ type: Boolean, reflect: true })
1400
+ ], Slider.prototype, "range", void 0);
1401
+ __decorate([
1402
+ property({ type: Boolean, reflect: true, attribute: 'show-steps' })
1403
+ ], Slider.prototype, "showSteps", void 0);
1404
+ __decorate([
1405
+ property({ type: String, reflect: true, attribute: 'show-input-field' })
1406
+ ], Slider.prototype, "showInputField", void 0);
1407
+ __decorate([
1408
+ property({ type: String, attribute: 'min-range' })
1409
+ ], Slider.prototype, "minRange", void 0);
1410
+ __decorate([
1411
+ translate({ mode: 'promise', scope: 'ef-slider' })
1412
+ ], Slider.prototype, "labelTPromise", void 0);
1413
+ __decorate([
1414
+ query('ef-number-field[name=value]')
1415
+ ], Slider.prototype, "valueInput", void 0);
1416
+ __decorate([
1417
+ query('ef-number-field[name=from]')
1418
+ ], Slider.prototype, "fromInput", void 0);
1419
+ __decorate([
1420
+ query('ef-number-field[name=to]')
1421
+ ], Slider.prototype, "toInput", void 0);
1422
+ __decorate([
1423
+ state()
1424
+ ], Slider.prototype, "activeThumb", void 0);
1425
+ __decorate([
1426
+ state()
1427
+ ], Slider.prototype, "changedThumb", void 0);
1428
+ Slider = __decorate([
1429
+ customElement('ef-slider')
1430
+ ], Slider);
1431
+ export { Slider };