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