@refinitiv-ui/elements 6.0.0-next.2 → 6.0.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.
Files changed (122) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +3 -15
  3. package/lib/autosuggest/custom-elements.json +5 -15
  4. package/lib/autosuggest/custom-elements.md +14 -14
  5. package/lib/autosuggest/helpers/renderer.d.ts +8 -0
  6. package/lib/autosuggest/helpers/renderer.js +35 -0
  7. package/lib/autosuggest/helpers/types.d.ts +101 -1
  8. package/lib/autosuggest/helpers/utils.d.ts +1 -8
  9. package/lib/autosuggest/helpers/utils.js +0 -27
  10. package/lib/autosuggest/index.d.ts +33 -32
  11. package/lib/autosuggest/index.js +246 -202
  12. package/lib/autosuggest/themes/halo/dark/index.js +1 -1
  13. package/lib/autosuggest/themes/halo/light/index.js +1 -1
  14. package/lib/autosuggest/themes/solar/charcoal/index.js +1 -1
  15. package/lib/autosuggest/themes/solar/pearl/index.js +1 -1
  16. package/lib/button/themes/halo/dark/index.js +1 -1
  17. package/lib/button/themes/halo/light/index.js +1 -1
  18. package/lib/calendar/themes/halo/dark/index.js +1 -1
  19. package/lib/calendar/themes/halo/light/index.js +1 -1
  20. package/lib/calendar/themes/solar/charcoal/index.js +1 -1
  21. package/lib/calendar/themes/solar/pearl/index.js +1 -1
  22. package/lib/card/themes/halo/dark/index.js +1 -1
  23. package/lib/card/themes/halo/light/index.js +1 -1
  24. package/lib/chart/plugins/doughnut-center-label.js +1 -1
  25. package/lib/chart/themes/halo/dark/index.js +1 -1
  26. package/lib/chart/themes/halo/light/index.js +1 -1
  27. package/lib/clock/custom-elements.json +10 -10
  28. package/lib/clock/custom-elements.md +1 -1
  29. package/lib/clock/index.d.ts +44 -16
  30. package/lib/clock/index.js +178 -61
  31. package/lib/clock/themes/halo/dark/index.js +1 -1
  32. package/lib/clock/themes/halo/light/index.js +1 -1
  33. package/lib/clock/themes/solar/charcoal/index.js +1 -1
  34. package/lib/clock/themes/solar/pearl/index.js +1 -1
  35. package/lib/collapse/themes/halo/dark/index.js +1 -1
  36. package/lib/collapse/themes/halo/light/index.js +1 -1
  37. package/lib/combo-box/custom-elements.json +0 -22
  38. package/lib/combo-box/custom-elements.md +0 -7
  39. package/lib/combo-box/themes/halo/dark/index.js +1 -1
  40. package/lib/combo-box/themes/halo/light/index.js +1 -1
  41. package/lib/counter/themes/halo/dark/index.js +1 -1
  42. package/lib/counter/themes/halo/light/index.js +1 -1
  43. package/lib/datetime-field/custom-elements.json +0 -75
  44. package/lib/datetime-field/custom-elements.md +27 -36
  45. package/lib/datetime-picker/themes/halo/dark/index.js +1 -1
  46. package/lib/datetime-picker/themes/halo/light/index.js +1 -1
  47. package/lib/dialog/themes/halo/dark/index.js +1 -1
  48. package/lib/dialog/themes/halo/light/index.js +1 -1
  49. package/lib/flag/utils/FlagLoader.d.ts +2 -32
  50. package/lib/flag/utils/FlagLoader.js +2 -69
  51. package/lib/heatmap/themes/halo/dark/index.js +1 -1
  52. package/lib/heatmap/themes/halo/light/index.js +1 -1
  53. package/lib/icon/utils/IconLoader.d.ts +2 -37
  54. package/lib/icon/utils/IconLoader.js +2 -76
  55. package/lib/interactive-chart/themes/halo/dark/index.js +1 -1
  56. package/lib/interactive-chart/themes/halo/light/index.js +1 -1
  57. package/lib/item/themes/halo/dark/index.js +1 -1
  58. package/lib/item/themes/halo/light/index.js +1 -1
  59. package/lib/list/themes/halo/dark/index.js +1 -1
  60. package/lib/list/themes/halo/light/index.js +1 -1
  61. package/lib/multi-input/themes/halo/dark/index.js +1 -1
  62. package/lib/multi-input/themes/halo/light/index.js +1 -1
  63. package/lib/number-field/custom-elements.json +0 -48
  64. package/lib/number-field/custom-elements.md +20 -26
  65. package/lib/number-field/themes/halo/dark/index.js +1 -1
  66. package/lib/number-field/themes/halo/light/index.js +1 -1
  67. package/lib/overlay/themes/halo/dark/index.js +1 -1
  68. package/lib/overlay/themes/halo/light/index.js +1 -1
  69. package/lib/overlay-menu/themes/halo/dark/index.js +1 -1
  70. package/lib/overlay-menu/themes/halo/light/index.js +1 -1
  71. package/lib/password-field/custom-elements.json +0 -7
  72. package/lib/password-field/custom-elements.md +0 -6
  73. package/lib/pill/themes/halo/dark/index.js +1 -1
  74. package/lib/pill/themes/halo/light/index.js +1 -1
  75. package/lib/rating/custom-elements.json +4 -4
  76. package/lib/rating/custom-elements.md +2 -2
  77. package/lib/rating/index.d.ts +84 -32
  78. package/lib/rating/index.js +209 -80
  79. package/lib/rating/themes/halo/dark/index.js +1 -1
  80. package/lib/rating/themes/halo/light/index.js +1 -1
  81. package/lib/rating/themes/solar/charcoal/index.js +1 -1
  82. package/lib/rating/themes/solar/pearl/index.js +1 -1
  83. package/lib/rating/utils.d.ts +9 -0
  84. package/lib/rating/utils.js +11 -0
  85. package/lib/search-field/custom-elements.json +0 -7
  86. package/lib/search-field/custom-elements.md +0 -6
  87. package/lib/select/themes/halo/dark/index.js +1 -1
  88. package/lib/select/themes/halo/light/index.js +1 -1
  89. package/lib/sidebar-layout/themes/halo/dark/index.js +1 -1
  90. package/lib/sidebar-layout/themes/halo/light/index.js +1 -1
  91. package/lib/slider/constants.d.ts +5 -1
  92. package/lib/slider/constants.js +6 -1
  93. package/lib/slider/index.d.ts +112 -49
  94. package/lib/slider/index.js +331 -182
  95. package/lib/slider/themes/halo/dark/index.js +1 -1
  96. package/lib/slider/themes/halo/light/index.js +1 -1
  97. package/lib/slider/themes/solar/charcoal/index.js +1 -1
  98. package/lib/slider/themes/solar/pearl/index.js +1 -1
  99. package/lib/slider/utils.d.ts +1 -9
  100. package/lib/slider/utils.js +1 -18
  101. package/lib/sparkline/themes/halo/dark/index.js +1 -1
  102. package/lib/sparkline/themes/halo/light/index.js +1 -1
  103. package/lib/tab/themes/halo/dark/index.js +1 -1
  104. package/lib/tab/themes/halo/light/index.js +1 -1
  105. package/lib/tab-bar/themes/halo/dark/index.js +1 -1
  106. package/lib/tab-bar/themes/halo/light/index.js +1 -1
  107. package/lib/text-field/custom-elements.json +0 -22
  108. package/lib/text-field/custom-elements.md +0 -7
  109. package/lib/toggle/themes/halo/dark/index.js +1 -1
  110. package/lib/toggle/themes/halo/light/index.js +1 -1
  111. package/lib/tree/elements/tree-item.js +1 -2
  112. package/lib/tree/elements/tree.d.ts +6 -0
  113. package/lib/tree/elements/tree.js +11 -1
  114. package/lib/tree/themes/halo/dark/index.js +2 -2
  115. package/lib/tree/themes/halo/light/index.js +2 -2
  116. package/lib/tree-select/index.js +15 -5
  117. package/lib/tree-select/themes/halo/dark/index.js +1 -1
  118. package/lib/tree-select/themes/halo/light/index.js +1 -1
  119. package/lib/version.js +1 -1
  120. package/package.json +19 -18
  121. package/lib/clock/utils/timestamps.d.ts +0 -6
  122. package/lib/clock/utils/timestamps.js +0 -6
@@ -3,11 +3,16 @@ import { html, css, ControlElement, WarningNotice } from '@refinitiv-ui/core';
3
3
  import { customElement } from '@refinitiv-ui/core/decorators/custom-element.js';
4
4
  import { property } from '@refinitiv-ui/core/decorators/property.js';
5
5
  import { query } from '@refinitiv-ui/core/decorators/query.js';
6
+ import { state } from '@refinitiv-ui/core/decorators/state.js';
7
+ import { ifDefined } from '@refinitiv-ui/core/directives/if-defined.js';
6
8
  import { createRef, ref } from '@refinitiv-ui/core/directives/ref.js';
7
9
  import { styleMap } from '@refinitiv-ui/core/directives/style-map.js';
10
+ import { translate } from '@refinitiv-ui/translate';
11
+ import '@refinitiv-ui/phrasebook/locale/en/slider.js';
8
12
  import { VERSION } from '../version.js';
9
13
  import '../number-field/index.js';
10
- import { clamp, preventDefault, validateNumber, isDecimalNumber, countDecimalPlace } from './utils.js';
14
+ import { SliderDataName, Direction } from './constants.js';
15
+ import { clamp, preventDefault, isDecimalNumber, countDecimalPlace } from './utils.js';
11
16
  /**
12
17
  * Allows users to make selections from a range of values
13
18
  *
@@ -27,10 +32,6 @@ import { clamp, preventDefault, validateNumber, isDecimalNumber, countDecimalPla
27
32
  let Slider = class Slider extends ControlElement {
28
33
  constructor() {
29
34
  super();
30
- /**
31
- * An array of slider thumbs
32
- */
33
- this.thumbs = [];
34
35
  /**
35
36
  * Whether if the thumb is being drag
36
37
  */
@@ -88,6 +89,26 @@ let Slider = class Slider extends ControlElement {
88
89
  * Slider's track reference
89
90
  */
90
91
  this.trackRef = createRef();
92
+ /**
93
+ * From value thumb reference, rendered only in range mode
94
+ */
95
+ this.fromThumbRef = createRef();
96
+ /**
97
+ * To value thumb reference, rendered only in range mode
98
+ */
99
+ this.toThumbRef = createRef();
100
+ /**
101
+ * Value thumb reference
102
+ */
103
+ this.valueThumbRef = createRef();
104
+ /**
105
+ * Current focused thumb
106
+ */
107
+ this.activeThumb = null;
108
+ /**
109
+ * Thumb that may involves data changes
110
+ */
111
+ this.changedThumb = null;
91
112
  /**
92
113
  * @ignore
93
114
  */
@@ -100,6 +121,10 @@ let Slider = class Slider extends ControlElement {
100
121
  * @ignore
101
122
  */
102
123
  this.onDragEnd = this.onDragEnd.bind(this);
124
+ /**
125
+ * @ignore
126
+ */
127
+ this.onKeyDown = this.onKeyDown.bind(this);
103
128
  }
104
129
  /**
105
130
  * Element version number
@@ -144,10 +169,11 @@ let Slider = class Slider extends ControlElement {
144
169
  pointer-events: none;
145
170
  }
146
171
  [part=thumb-container]{
172
+ outline: none;
147
173
  position: absolute;
148
174
  top: 0;
149
175
  width: 100%;
150
- z-index: 5;
176
+ z-index: 3;
151
177
  }
152
178
  [part=thumb]{
153
179
  position: absolute;
@@ -284,7 +310,7 @@ let Slider = class Slider extends ControlElement {
284
310
  }
285
311
  /**
286
312
  * Return hide/show input field state
287
- * @returns {boolean} true if showInputField value is exist
313
+ * @returns true if showInputField value is exist
288
314
  */
289
315
  get isShowInputField() {
290
316
  return this.showInputField !== null && this.showInputField !== undefined;
@@ -401,7 +427,6 @@ let Slider = class Slider extends ControlElement {
401
427
  prepareValues() {
402
428
  if (this.minNumber !== this.maxNumber) {
403
429
  if (this.range) {
404
- this.activeThumb = this.thumbs[1];
405
430
  if (this.minRangeNumber) {
406
431
  const distanceFromTo = Math.abs(this.toNumber - this.fromNumber);
407
432
  const clampValueFrom = this.toNumber - this.minRangeNumber;
@@ -435,19 +460,26 @@ let Slider = class Slider extends ControlElement {
435
460
  this.fromPrevious = this.from;
436
461
  }
437
462
  /**
438
- * Query and assigned thumbs depending on slider mode and add event listeners to it
463
+ * Add event listeners to thumbs depending on mode
439
464
  * @returns {void}
440
465
  */
441
466
  prepareThumbs() {
442
- this.thumbs = [];
443
- const thumbs = this.shadowRoot?.querySelectorAll('[part~=thumb-container]') || [];
444
- this.thumbs = this.range ? Array.from(thumbs) : [thumbs[0]];
445
- this.activeThumb = this.thumbs[0];
446
- this.thumbs.forEach((thumb) => {
447
- thumb.addEventListener('drag', preventDefault);
448
- thumb.addEventListener('dragstart', preventDefault);
449
- thumb.addEventListener('dragend', preventDefault);
450
- });
467
+ if (this.range) {
468
+ this.fromThumbRef.value?.addEventListener('keydown', this.onKeyDown);
469
+ this.fromThumbRef.value?.addEventListener('drag', preventDefault);
470
+ this.fromThumbRef.value?.addEventListener('dragstart', preventDefault);
471
+ this.fromThumbRef.value?.addEventListener('dragend', preventDefault);
472
+ this.toThumbRef.value?.addEventListener('keydown', this.onKeyDown);
473
+ this.toThumbRef.value?.addEventListener('drag', preventDefault);
474
+ this.toThumbRef.value?.addEventListener('dragstart', preventDefault);
475
+ this.toThumbRef.value?.addEventListener('dragend', preventDefault);
476
+ }
477
+ else {
478
+ this.valueThumbRef.value?.addEventListener('keydown', this.onKeyDown);
479
+ this.valueThumbRef.value?.addEventListener('drag', preventDefault);
480
+ this.valueThumbRef.value?.addEventListener('dragstart', preventDefault);
481
+ this.valueThumbRef.value?.addEventListener('dragend', preventDefault);
482
+ }
451
483
  }
452
484
  /**
453
485
  * Add or remove event listener on slider track depending on slider disabled and readonly state
@@ -464,28 +496,152 @@ let Slider = class Slider extends ControlElement {
464
496
  }
465
497
  }
466
498
  /**
467
- * Calculate percentage from value, min and max
468
- * @param value value to be calculated
469
- * @returns percentage
499
+ * Get slider data name from keyboard event target
500
+ * @param target target element
501
+ * @returns Slider data name
470
502
  */
471
- calculatePercentage(value) {
472
- const percentage = Math.abs((((value || 0) - this.minNumber) / (this.maxNumber - this.minNumber)) * 100);
473
- if (percentage > 100) {
474
- return 100;
503
+ getThumbName(target) {
504
+ switch (target) {
505
+ case this.fromThumbRef.value:
506
+ return SliderDataName.from;
507
+ case this.toThumbRef.value:
508
+ return SliderDataName.to;
509
+ case this.valueThumbRef.value:
510
+ return SliderDataName.value;
511
+ default:
512
+ return null;
475
513
  }
476
- else if (percentage < 0) {
477
- return 0;
514
+ }
515
+ /**
516
+ * Handles key down event on thumbs
517
+ * @param event Keyboard event
518
+ * @returns {void}
519
+ */
520
+ onKeyDown(event) {
521
+ if (this.readonly || event.defaultPrevented || this.minNumber >= this.maxNumber) {
522
+ return;
523
+ }
524
+ // Ignore special keys
525
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) {
526
+ return;
527
+ }
528
+ const thumbName = this.getThumbName(event.target);
529
+ if (!thumbName) {
530
+ return;
531
+ }
532
+ this.changedThumb = event.target;
533
+ switch (event.key) {
534
+ case 'ArrowDown':
535
+ case 'Down':
536
+ case 'ArrowLeft':
537
+ case 'Left':
538
+ this.onApplyStep(Direction.Down, thumbName);
539
+ break;
540
+ case 'ArrowUp':
541
+ case 'Up':
542
+ case 'ArrowRight':
543
+ case 'Right':
544
+ this.onApplyStep(Direction.Up, thumbName);
545
+ break;
546
+ case 'Home':
547
+ this.onApplyMin(thumbName);
548
+ break;
549
+ case 'End':
550
+ this.onApplyMax(thumbName);
551
+ break;
552
+ default:
553
+ return;
554
+ }
555
+ event.preventDefault();
556
+ }
557
+ /**
558
+ * Set thumb to minimum value possible
559
+ * @param data type of data to change
560
+ * @returns {void}
561
+ */
562
+ onApplyMin(data) {
563
+ let position;
564
+ if (data === SliderDataName.from || data === SliderDataName.value) {
565
+ position = this.calculatePosition(this.minNumber, 1);
478
566
  }
479
567
  else {
480
- return percentage;
568
+ position = this.calculatePosition(this.fromNumber + this.minRangeNumber, 1);
481
569
  }
570
+ const possibleValue = this.getNearestPossibleValue(position);
571
+ const value = this.getValueFromPosition(possibleValue);
572
+ this.persistChangedData(value);
573
+ this.dispatchDataChangedEvent();
482
574
  }
483
575
  /**
484
- * Input number event blur and notify property
485
- * @param event event blur input number field
576
+ * Set thumb to maximum value possible
577
+ * @param data type of data to change
486
578
  * @returns {void}
487
579
  */
488
- onBlur(event) {
580
+ onApplyMax(data) {
581
+ let position;
582
+ if (data === SliderDataName.to || data === SliderDataName.value) {
583
+ position = this.calculatePosition(this.maxNumber, 1);
584
+ }
585
+ else {
586
+ position = this.calculatePosition(this.toNumber - this.minRangeNumber, 1);
587
+ }
588
+ const possibleValue = this.getNearestPossibleValue(position);
589
+ const value = this.getValueFromPosition(possibleValue);
590
+ this.persistChangedData(value);
591
+ this.dispatchDataChangedEvent();
592
+ }
593
+ /**
594
+ * Increase or decrease value depending on direction
595
+ * Then fires value change event
596
+ * @param direction Up or Down
597
+ * @param data type of data to change
598
+ * @returns {void}
599
+ */
600
+ onApplyStep(direction, data) {
601
+ // Get current thumb position and step in percentage format
602
+ const thumbPosition = this.calculatePosition(this[`${data}Number`], 1);
603
+ const step = this.calculatePosition(this.minNumber + this.stepRange, 1);
604
+ const possibleValue = direction === Direction.Up ? thumbPosition + step : thumbPosition - step;
605
+ const nearestPossibleValue = this.getNearestPossibleValue(possibleValue);
606
+ const value = this.getValueFromPosition(nearestPossibleValue);
607
+ this.persistChangedData(value);
608
+ this.dispatchDataChangedEvent();
609
+ }
610
+ /**
611
+ * Calculate thumb position based on value and multiplier
612
+ * @param value decimal fraction value
613
+ * @param multiplier defaults to 100
614
+ * @returns thumb position as a fraction of 100
615
+ */
616
+ calculatePosition(value, multiplier = 100) {
617
+ const position = Math.abs(((value - this.minNumber) / (this.maxNumber - this.minNumber)) * multiplier);
618
+ if (position > multiplier) {
619
+ return multiplier;
620
+ }
621
+ return position;
622
+ }
623
+ /**
624
+ * Adds active attribute used in styling
625
+ * @param event focus event
626
+ * @returns {void}
627
+ */
628
+ onThumbFocus(event) {
629
+ this.activeThumb = event.target;
630
+ }
631
+ /**
632
+ * Removes active attribute used in styling
633
+ * @param event focus event
634
+ * @returns {void}
635
+ */
636
+ onThumbBlur() {
637
+ this.activeThumb = null;
638
+ }
639
+ /**
640
+ * On number-field blur
641
+ * @param event focus event
642
+ * @returns {void}
643
+ */
644
+ onNumberFieldBlur(event) {
489
645
  if (this.readonly) {
490
646
  return;
491
647
  }
@@ -499,11 +655,11 @@ let Slider = class Slider extends ControlElement {
499
655
  event.preventDefault();
500
656
  }
501
657
  /**
502
- * Input number event keydown y
503
- * @param event event keydown
658
+ * On number-field keydown
659
+ * @param event keyboard event
504
660
  * @returns {void}
505
661
  */
506
- onKeydown(event) {
662
+ onNumberFieldKeyDown(event) {
507
663
  if (this.readonly || this.disabled) {
508
664
  return;
509
665
  }
@@ -519,12 +675,11 @@ let Slider = class Slider extends ControlElement {
519
675
  */
520
676
  updateNotifyProperty(name, value) {
521
677
  let shouldUpdate = false;
522
- const validatedValue = Number(validateNumber(Number(value), 0));
523
- if (name === 'to') {
524
- shouldUpdate = this.isValueInBoundary(validatedValue, 'to');
678
+ if (name === SliderDataName.to) {
679
+ shouldUpdate = this.isValueInBoundary(Number(value), SliderDataName.to);
525
680
  }
526
681
  else {
527
- shouldUpdate = this.isValueInBoundary(validatedValue, '');
682
+ shouldUpdate = this.isValueInBoundary(Number(value), '');
528
683
  }
529
684
  if (shouldUpdate) {
530
685
  (this[name]) = value;
@@ -540,7 +695,7 @@ let Slider = class Slider extends ControlElement {
540
695
  * @returns {void}
541
696
  */
542
697
  dispatchDataChangedEvent() {
543
- const name = this.activeThumb.getAttribute('name') || '';
698
+ const name = this.changedThumb?.getAttribute('name') || '';
544
699
  const currentData = name;
545
700
  const previousData = `${name}Previous`;
546
701
  // Dispatch event only when value or from or to changed
@@ -549,17 +704,6 @@ let Slider = class Slider extends ControlElement {
549
704
  this[previousData] = this[currentData];
550
705
  }
551
706
  }
552
- /**
553
- * Set focus to input from state
554
- * @param name number field's name
555
- * @param focusState state of focus
556
- * @returns {void}
557
- */
558
- toggleFocusField(name, focusState) {
559
- if (name) {
560
- this[`${name}Input`].setAttribute('tabindex', `${focusState ? 1 : 0}`);
561
- }
562
- }
563
707
  /**
564
708
  * Start dragging event on slider
565
709
  * @param event event dragstart
@@ -569,27 +713,20 @@ let Slider = class Slider extends ControlElement {
569
713
  this.dragging = true;
570
714
  this.classList.add('grabbable');
571
715
  if (this.range) {
572
- const percentage = this.getMousePosition(event);
573
- const mousePosition = ((this.maxNumber - this.minNumber) * percentage) + this.minNumber;
574
- const distanceFrom = Math.abs(mousePosition - this.fromNumber);
575
- const distanceTo = Math.abs(mousePosition - this.toNumber);
716
+ const mousePosition = this.getMousePosition(event);
717
+ const relativeMousePosition = ((this.maxNumber - this.minNumber) * mousePosition) + this.minNumber;
718
+ const distanceFrom = Math.abs(relativeMousePosition - this.fromNumber);
719
+ const distanceTo = Math.abs(relativeMousePosition - this.toNumber);
576
720
  if (distanceFrom < distanceTo) {
577
- this.activeThumb = this.thumbs[0];
721
+ this.changedThumb = this.fromThumbRef.value;
578
722
  }
579
723
  else if (distanceFrom > distanceTo) {
580
- this.activeThumb = this.thumbs[1];
581
- }
582
- this.thumbs.forEach((el) => {
583
- el.style.zIndex = '5';
584
- });
585
- this.activeThumb.style.zIndex = '10';
586
- // Set focus to input when drag.
587
- if (this.isShowInputField) {
588
- this.toggleFocusField(this.activeThumb.getAttribute('name') || '', true);
724
+ this.changedThumb = this.toThumbRef.value;
589
725
  }
726
+ // When from === to, use latest value of changedThumb and z-index will determine thumb on top
590
727
  }
591
728
  else {
592
- this.activeThumb = this.thumbs[0];
729
+ this.changedThumb = this.valueThumbRef.value;
593
730
  }
594
731
  this.onDrag(event);
595
732
  if (event.changedTouches) {
@@ -602,6 +739,7 @@ let Slider = class Slider extends ControlElement {
602
739
  }
603
740
  }
604
741
  /**
742
+ * Get mouse position in percentage value
605
743
  * @param event event mousemove and touchmove
606
744
  * @returns mouse position by percentage
607
745
  */
@@ -630,42 +768,48 @@ let Slider = class Slider extends ControlElement {
630
768
  return;
631
769
  }
632
770
  const thumbPosition = this.getMousePosition(event);
633
- const nearestStep = this.calculateStep(thumbPosition);
634
- if (nearestStep > 1) {
771
+ const nearestValue = this.getNearestPossibleValue(thumbPosition);
772
+ if (nearestValue > 1) {
635
773
  return;
636
774
  }
637
- const thumbLeft = this.stepRange !== 0 ? nearestStep : thumbPosition;
638
- const computedStepValue = this.calculateValue(thumbLeft);
639
- const displayedValue = Number(this.displayValue(computedStepValue));
775
+ const newThumbPosition = this.stepRange !== 0 ? nearestValue : thumbPosition;
776
+ const value = this.getValueFromPosition(newThumbPosition);
777
+ this.persistChangedData(value);
778
+ }
779
+ /**
780
+ * Saves changed data into correct field
781
+ * @param value value of changed data
782
+ * @returns {void}
783
+ */
784
+ persistChangedData(value) {
785
+ const newValue = this.format(value);
640
786
  if (this.range) {
641
- if (this.activeThumb === this.thumbs[1]) {
642
- this.to = this.validateTo(displayedValue).toString();
787
+ if (this.changedThumb === this.fromThumbRef.value) {
788
+ this.from = this.validateFrom(Number(newValue)).toString();
643
789
  }
644
790
  else {
645
- this.from = this.validateFrom(displayedValue).toString();
791
+ this.to = this.validateTo(Number(newValue)).toString();
646
792
  }
647
793
  }
648
794
  else {
649
- this.value = displayedValue.toString();
795
+ this.value = newValue;
650
796
  }
651
797
  }
652
798
  /**
653
- * Handle 'from' value on drag out of boundary.
654
- * @param value value from change
655
- * @returns validated from value.
799
+ * Validate and return FROM value within available range
800
+ * @param value from value
801
+ * @returns validated from value
656
802
  */
657
803
  validateFrom(value) {
658
804
  const valueFrom = value + this.minRangeNumber;
659
805
  if (valueFrom < this.toNumber && valueFrom >= this.minNumber) {
660
806
  return value;
661
807
  }
662
- else {
663
- return this.toNumber - this.minRangeNumber;
664
- }
808
+ return this.toNumber - this.minRangeNumber;
665
809
  }
666
810
  /**
667
- * Handle 'To' value on drag out of boundary.
668
- * @param value value to change
811
+ * Validate and return TO value within available range
812
+ * @param value to value
669
813
  * @returns validated to value.
670
814
  */
671
815
  validateTo(value) {
@@ -673,38 +817,31 @@ let Slider = class Slider extends ControlElement {
673
817
  if (valueTo > this.fromNumber && valueTo <= this.maxNumber) {
674
818
  return value;
675
819
  }
676
- else {
677
- return this.fromNumber + this.minRangeNumber;
678
- }
820
+ return this.fromNumber + this.minRangeNumber;
679
821
  }
680
822
  /**
681
- * Calculate the nearest step interval
682
- * @param thumbPosition thumb position dragging on slider
683
- * @returns position step on slider
823
+ * Calculate the nearest possible step value depending on step interval
824
+ * @param thumbPosition current thumb position in fraction
825
+ * @returns nearest available slider step in fraction
684
826
  */
685
- calculateStep(thumbPosition) {
686
- const stepSize = this.calculatePercentage(this.minNumber + this.stepRange) / 100;
687
- // calculate step to current point to next point
688
- const posToFixStep = Math.round(thumbPosition / stepSize) * stepSize;
689
- if (thumbPosition <= posToFixStep + (stepSize / 2)) {
690
- if (posToFixStep <= 1) {
691
- return posToFixStep;
827
+ getNearestPossibleValue(thumbPosition) {
828
+ const stepSize = this.calculatePosition(this.minNumber + this.stepRange, 1);
829
+ const nearestValue = Math.round(thumbPosition / stepSize) * stepSize;
830
+ if (thumbPosition <= nearestValue + (stepSize / 2)) {
831
+ if (nearestValue <= 1) {
832
+ return nearestValue;
692
833
  }
693
- else {
694
- return posToFixStep - stepSize;
695
- }
696
- }
697
- else {
698
- return posToFixStep + stepSize;
834
+ return nearestValue - stepSize;
699
835
  }
836
+ return nearestValue + stepSize;
700
837
  }
701
838
  /**
702
- * Calculate value by percentage
703
- * @param percentage percentage to be calculated
839
+ * Get slider value from thumb position
840
+ * @param position thumb position
704
841
  * @returns calculated value
705
842
  */
706
- calculateValue(percentage) {
707
- const value = this.minNumber + percentage * (this.maxNumber - this.minNumber);
843
+ getValueFromPosition(position) {
844
+ const value = this.minNumber + position * (this.maxNumber - this.minNumber);
708
845
  // if value is outside boundary, set to boundary
709
846
  if (value >= this.maxNumber) {
710
847
  return this.maxNumber;
@@ -721,20 +858,11 @@ let Slider = class Slider extends ControlElement {
721
858
  * @param value value before use display
722
859
  * @returns formatted value
723
860
  */
724
- displayValue(value) {
725
- if (isDecimalNumber(value)) {
726
- const valueDecimalCount = countDecimalPlace(value);
727
- // return value decimal by a boundary of max decimal
728
- if (valueDecimalCount > this.decimalPlace) {
729
- return value.toFixed(this.decimalPlace);
730
- }
731
- else {
732
- return value.toString();
733
- }
734
- }
735
- else {
736
- return value.toString();
861
+ format(value) {
862
+ if (isDecimalNumber(value) && countDecimalPlace(value) > this.decimalPlace) {
863
+ return value.toFixed(this.decimalPlace);
737
864
  }
865
+ return value.toString();
738
866
  }
739
867
  /**
740
868
  * End dragging event and remove dragging event
@@ -761,10 +889,6 @@ let Slider = class Slider extends ControlElement {
761
889
  event.preventDefault();
762
890
  }
763
891
  this.dispatchDataChangedEvent();
764
- // Reset tabIndex of input that's being drag.
765
- if (this.isShowInputField) {
766
- this.toggleFocusField(this.activeThumb.getAttribute('name') || '', false);
767
- }
768
892
  }
769
893
  }
770
894
  /**
@@ -773,26 +897,19 @@ let Slider = class Slider extends ControlElement {
773
897
  */
774
898
  onValueChange() {
775
899
  if (this.readonly) {
776
- const valuePercent = this.calculatePercentage(Number(this.value)) / 100;
777
- const closestStep = this.calculateStep(valuePercent);
778
- const thumbLeft = this.stepRange !== 0 ? closestStep : valuePercent;
779
- const calStepValue = this.calculateValue(thumbLeft);
780
- this.value = this.displayValue(calStepValue);
900
+ const thumbPosition = this.calculatePosition(this.valueNumber, 1);
901
+ const nearestPossibleValue = this.getNearestPossibleValue(thumbPosition);
902
+ const value = this.getValueFromPosition(this.stepRange === 0 ? thumbPosition : nearestPossibleValue);
903
+ this.value = this.format(value);
781
904
  }
782
- else {
783
- const valueSanitize = Number(validateNumber(Number(this.value), 0));
784
- if (this.isValueInBoundary(valueSanitize, '')) {
785
- this.value = this.displayValue(valueSanitize);
786
- }
787
- else {
788
- // if value is outside boundary, set to boundary
789
- if (valueSanitize < this.minNumber) {
790
- this.value = this.min;
791
- }
792
- if (valueSanitize > this.maxNumber) {
793
- this.value = this.max;
794
- }
795
- }
905
+ else if (this.isValueInBoundary(this.valueNumber, '')) {
906
+ this.value = this.format(this.valueNumber);
907
+ }
908
+ else if (this.valueNumber < this.minNumber) {
909
+ this.value = this.min;
910
+ }
911
+ else if (this.valueNumber > this.maxNumber) {
912
+ this.value = this.max;
796
913
  }
797
914
  if (!this.dragging) {
798
915
  // Update internal `valuePrevious` when `value` was programatically set by user.
@@ -804,16 +921,15 @@ let Slider = class Slider extends ControlElement {
804
921
  * @returns {void}
805
922
  */
806
923
  onFromValueChange() {
807
- const valueFrom = Number(validateNumber(this.fromNumber, 0));
808
- if (this.isValueInBoundary(valueFrom, 'from')) {
809
- this.from = this.displayValue(this.fromNumber);
924
+ if (this.isValueInBoundary(this.fromNumber, SliderDataName.from)) {
925
+ this.from = this.format(this.fromNumber);
810
926
  }
811
927
  else {
812
928
  // if value is outside boundary, set to boundary
813
- if (valueFrom < this.minNumber) {
929
+ if (this.fromNumber < this.minNumber) {
814
930
  this.from = this.min;
815
931
  }
816
- if (valueFrom > this.toNumber) {
932
+ else if (this.fromNumber > this.toNumber) {
817
933
  this.from = this.to;
818
934
  }
819
935
  if (this.minRangeNumber) {
@@ -838,7 +954,7 @@ let Slider = class Slider extends ControlElement {
838
954
  if (this.minNumber < this.maxNumber) {
839
955
  // Check if value is in range
840
956
  if (this.range) {
841
- if (valueFor === 'to') {
957
+ if (valueFor === SliderDataName.to) {
842
958
  if (value < (this.fromNumber + this.minRangeNumber) || value > this.maxNumber) {
843
959
  return false;
844
960
  }
@@ -862,16 +978,15 @@ let Slider = class Slider extends ControlElement {
862
978
  * @returns {void}
863
979
  */
864
980
  onToValueChange() {
865
- const valueTo = Number(validateNumber(this.toNumber, 0));
866
- if (this.isValueInBoundary(valueTo, 'to')) {
867
- this.to = this.displayValue(valueTo);
981
+ if (this.isValueInBoundary(this.toNumber, SliderDataName.to)) {
982
+ this.to = this.format(this.toNumber);
868
983
  }
869
984
  else {
870
985
  // if value is outside boundary, set to boundary
871
- if (valueTo < this.fromNumber) {
986
+ if (this.toNumber < this.fromNumber) {
872
987
  this.to = this.from;
873
988
  }
874
- if (valueTo > this.maxNumber) {
989
+ else if (this.toNumber > this.maxNumber) {
875
990
  this.to = this.max;
876
991
  }
877
992
  if (this.minRangeNumber) {
@@ -891,14 +1006,14 @@ let Slider = class Slider extends ControlElement {
891
1006
  * @returns {void}
892
1007
  */
893
1008
  onStepChange() {
894
- this.step = validateNumber(this.stepNumber, 1);
1009
+ this.step = this.stepNumber.toString();
895
1010
  }
896
1011
  /**
897
1012
  * Min range observer
898
1013
  * @returns {void}
899
1014
  */
900
1015
  onMinRangeChange() {
901
- const valueMinRange = Math.abs(Number(validateNumber(this.minRangeNumber, 0)));
1016
+ const valueMinRange = Math.abs(this.minRangeNumber);
902
1017
  const maximumRangeMinMax = Math.abs(this.maxNumber - this.minNumber);
903
1018
  const maximumRangeFromTo = Math.abs(this.toNumber - this.fromNumber);
904
1019
  if (valueMinRange && valueMinRange >= this.stepNumber) {
@@ -922,11 +1037,12 @@ let Slider = class Slider extends ControlElement {
922
1037
  * @returns {void}
923
1038
  */
924
1039
  onMinChange(oldValue) {
925
- this.min = validateNumber(this.minNumber, 0);
1040
+ this.min = this.minNumber.toString();
926
1041
  if (this.minNumber > this.maxNumber) {
927
1042
  this.min = this.max;
1043
+ return;
928
1044
  }
929
- else if (this.range) {
1045
+ if (this.range) {
930
1046
  if (this.minNumber <= this.toNumber - this.minRangeNumber) {
931
1047
  this.from = clamp(this.fromNumber, this.minNumber, this.toNumber);
932
1048
  }
@@ -944,11 +1060,12 @@ let Slider = class Slider extends ControlElement {
944
1060
  * @returns {void}
945
1061
  */
946
1062
  onMaxChange(oldValue) {
947
- this.max = validateNumber(this.maxNumber, 100);
1063
+ this.max = this.maxNumber.toString();
948
1064
  if (this.maxNumber < this.minNumber) {
949
1065
  this.max = this.min;
1066
+ return;
950
1067
  }
951
- else if (this.range) {
1068
+ if (this.range) {
952
1069
  if (this.maxNumber >= this.fromNumber + this.minRangeNumber) {
953
1070
  this.to = clamp(this.toNumber, this.fromNumber, this.maxNumber);
954
1071
  }
@@ -966,13 +1083,13 @@ let Slider = class Slider extends ControlElement {
966
1083
  * @returns Track template
967
1084
  */
968
1085
  renderTrack(range) {
969
- const stepContainerSize = this.calculatePercentage(this.minNumber + this.stepNumber);
1086
+ const stepContainerSize = this.calculatePosition(this.minNumber + this.stepNumber);
970
1087
  const translateX = (stepContainerSize / 2);
971
1088
  const stepsStyle = { transform: `translateX(${translateX}%)`, backgroundSize: `${stepContainerSize}% 100%` };
972
1089
  const stepContainerStyle = { transform: `translateX(-${translateX}%)` };
973
1090
  const trackFillStyle = range
974
- ? { width: `${this.calculatePercentage(this.toNumber) - this.calculatePercentage(this.fromNumber)}%`, left: `${this.calculatePercentage(this.fromNumber)}%` }
975
- : { width: `${this.calculatePercentage(Number(this.value))}%` };
1091
+ ? { width: `${this.calculatePosition(this.toNumber) - this.calculatePosition(this.fromNumber)}%`, left: `${this.calculatePosition(this.fromNumber)}%` }
1092
+ : { width: `${this.calculatePosition(Number(this.value))}%` };
976
1093
  return html `
977
1094
  <div part="track-wrapper" ${ref(this.trackRef)}>
978
1095
  <div part="track-fill" style=${styleMap(trackFillStyle)}></div>
@@ -985,14 +1102,33 @@ let Slider = class Slider extends ControlElement {
985
1102
  /**
986
1103
  * Implement `render` Thumb template.
987
1104
  * @param value thumb value in track
988
- * @param percentageValue thumb position in track
989
- * @param name name of active thumb
990
- * @returns Track template
1105
+ * @param thumbPosition thumb position in track
1106
+ * @param name name of thumb to render
1107
+ * @returns Thumb template
991
1108
  */
992
- renderThumb(value, percentageValue, name) {
993
- const thumbStyle = { left: `${percentageValue}%` };
1109
+ thumbTemplate(value, thumbPosition, name) {
1110
+ const isActive = this.activeThumb?.getAttribute('name') === name;
1111
+ const isChanged = this.changedThumb?.getAttribute('name') === name;
1112
+ const valueNow = this.range ? name === SliderDataName.from ? this.from : this.to : this.value;
1113
+ const valueMin = this.range ? name === SliderDataName.from ? this.min : this.fromNumber + this.minRangeNumber : this.min;
1114
+ const valueMax = this.range ? name === SliderDataName.from ? this.toNumber - this.minRangeNumber : this.max : this.max;
1115
+ const thumbStyle = { left: `${thumbPosition}%`, zIndex: this.range ? isChanged ? '4' : '3' : null };
994
1116
  return html `
995
- <div part="thumb-container" name=${name} style=${styleMap(thumbStyle)}>
1117
+ <div
1118
+ ${ref(this[`${name}ThumbRef`])}
1119
+ @focus=${this.onThumbFocus}
1120
+ @blur=${this.onThumbBlur}
1121
+ active=${ifDefined(isActive || undefined)}
1122
+ name="${name}"
1123
+ role="slider"
1124
+ aria-label="${this.t(name.toUpperCase())}"
1125
+ tabindex="1"
1126
+ aria-valuemin=${valueMin}
1127
+ aria-valuemax=${valueMax}
1128
+ aria-valuenow=${valueNow}
1129
+ part="thumb-container"
1130
+ style=${styleMap(thumbStyle)}
1131
+ >
996
1132
  <div part="pin">
997
1133
  <span part="pin-value-marker">${value}</span>
998
1134
  </div>
@@ -1001,30 +1137,34 @@ let Slider = class Slider extends ControlElement {
1001
1137
  `;
1002
1138
  }
1003
1139
  /**
1004
- * Implement `render` Thumb has range template.
1140
+ * Renders thumb template depending on parameter
1005
1141
  * @param from thumb value start in track
1006
- * @param to thumb value end in track
1007
- * @returns Thumb has range template
1142
+ * @param to thumb value end in track (optional)
1143
+ * @returns Thumbs template
1008
1144
  */
1009
- renderThumbRange(from, to) {
1010
- const fromPercentageValue = this.calculatePercentage(from);
1011
- const toPercentageValue = this.calculatePercentage(to);
1145
+ renderThumb(from, to) {
1012
1146
  return html `
1013
- ${this.renderThumb(from, fromPercentageValue, 'from')}
1014
- ${this.renderThumb(to, toPercentageValue, 'to')}
1147
+ ${this.thumbTemplate(from, this.calculatePosition(from), to ? SliderDataName.from : SliderDataName.value)}
1148
+ ${to && this.thumbTemplate(to, this.calculatePosition(to), SliderDataName.to)}
1015
1149
  `;
1016
1150
  }
1017
1151
  /**
1018
1152
  * Implement `render` number field has template.
1019
1153
  * @param value value in the slider for binding in the input value
1020
1154
  * @param name name input value
1021
- * @returns {TemplateResult} number field template
1155
+ * @returns {TemplateResult} number field template
1022
1156
  */
1023
1157
  renderNumberField(value, name) {
1158
+ /**
1159
+ * Hiding number-field from screen reader and tabbing sequence because it's redundant,
1160
+ * and complicate the accessibility implementation.
1161
+ */
1024
1162
  return html `
1025
1163
  <ef-number-field
1026
- @blur=${this.onBlur}
1027
- @keydown=${this.onKeydown}
1164
+ tabindex="-1"
1165
+ aria-hidden="true"
1166
+ @blur=${this.onNumberFieldBlur}
1167
+ @keydown=${this.onNumberFieldKeyDown}
1028
1168
  part="input"
1029
1169
  name="${name}"
1030
1170
  no-spinner
@@ -1043,15 +1183,15 @@ let Slider = class Slider extends ControlElement {
1043
1183
  */
1044
1184
  render() {
1045
1185
  return html `
1046
- ${this.range && this.isShowInputField ? this.renderNumberField(this.from, 'from') : null}
1186
+ ${this.range && this.isShowInputField ? this.renderNumberField(this.from, SliderDataName.from) : null}
1047
1187
  <div part="slider-wrapper">
1048
1188
  <div part="slider" ${ref(this.sliderRef)}>
1049
1189
  ${this.renderTrack(this.range)}
1050
- ${this.range ? this.renderThumbRange(this.fromNumber, this.toNumber) : this.renderThumb(this.valueNumber, this.calculatePercentage(Number(this.value)), 'value')}
1190
+ ${this.range ? this.renderThumb(this.fromNumber, this.toNumber) : this.renderThumb(this.valueNumber)}
1051
1191
  </div>
1052
1192
  </div>
1053
- ${this.range && this.isShowInputField ? this.renderNumberField(this.to, 'to') : null}
1054
- ${!this.range && this.isShowInputField ? this.renderNumberField(this.value, 'value') : null}
1193
+ ${this.range && this.isShowInputField ? this.renderNumberField(this.to, SliderDataName.to) : null}
1194
+ ${!this.range && this.isShowInputField ? this.renderNumberField(this.value, SliderDataName.value) : null}
1055
1195
  `;
1056
1196
  }
1057
1197
  };
@@ -1085,6 +1225,9 @@ __decorate([
1085
1225
  __decorate([
1086
1226
  property({ type: String, attribute: 'min-range' })
1087
1227
  ], Slider.prototype, "minRange", void 0);
1228
+ __decorate([
1229
+ translate({ scope: 'ef-slider' })
1230
+ ], Slider.prototype, "t", void 0);
1088
1231
  __decorate([
1089
1232
  query('ef-number-field[name=value]')
1090
1233
  ], Slider.prototype, "valueInput", void 0);
@@ -1094,6 +1237,12 @@ __decorate([
1094
1237
  __decorate([
1095
1238
  query('ef-number-field[name=to]')
1096
1239
  ], Slider.prototype, "toInput", void 0);
1240
+ __decorate([
1241
+ state()
1242
+ ], Slider.prototype, "activeThumb", void 0);
1243
+ __decorate([
1244
+ state()
1245
+ ], Slider.prototype, "changedThumb", void 0);
1097
1246
  Slider = __decorate([
1098
1247
  customElement('ef-slider', {
1099
1248
  alias: 'coral-slider'