@vaadin/slider 25.1.0-alpha6 → 25.1.0-alpha7
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/custom-elements.json +1660 -0
- package/package.json +12 -9
- package/src/styles/vaadin-slider-base-styles.js +39 -11
- package/src/styles/vaadin-slider-bubble-overlay-base-styles.js +124 -0
- package/src/vaadin-range-slider.d.ts +60 -10
- package/src/vaadin-range-slider.js +258 -23
- package/src/vaadin-slider-bubble-overlay.js +97 -0
- package/src/vaadin-slider-bubble.d.ts +28 -0
- package/src/vaadin-slider-bubble.js +75 -0
- package/src/vaadin-slider-mixin.d.ts +13 -0
- package/src/vaadin-slider-mixin.js +95 -3
- package/src/vaadin-slider.d.ts +43 -7
- package/src/vaadin-slider.js +166 -17
- package/web-types.json +135 -3
- package/web-types.lit.json +45 -3
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Copyright (c) 2026 - 2026 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
+
import './vaadin-slider-bubble.js';
|
|
6
7
|
import { css, html, LitElement, render } from 'lit';
|
|
8
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
7
9
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
8
10
|
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
|
|
9
11
|
import { isElementFocused } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
@@ -41,19 +43,23 @@ import { SliderMixin } from './vaadin-slider-mixin.js';
|
|
|
41
43
|
* `thumb` | The slider thumb (applies to both thumbs)
|
|
42
44
|
* `thumb-start` | The start (lower value) thumb
|
|
43
45
|
* `thumb-end` | The end (upper value) thumb
|
|
46
|
+
* `marks` | Container for min/max labels
|
|
47
|
+
* `min` | Minimum value label
|
|
48
|
+
* `max` | Maximum value label
|
|
44
49
|
*
|
|
45
50
|
* The following state attributes are available for styling:
|
|
46
51
|
*
|
|
47
|
-
* Attribute
|
|
48
|
-
*
|
|
49
|
-
* `disabled`
|
|
50
|
-
* `readonly`
|
|
51
|
-
* `focused`
|
|
52
|
-
* `focus-ring`
|
|
53
|
-
* `start-active`
|
|
54
|
-
* `end-active`
|
|
55
|
-
* `start-focused`
|
|
56
|
-
* `end-focused`
|
|
52
|
+
* Attribute | Description
|
|
53
|
+
* -------------------|-------------
|
|
54
|
+
* `disabled` | Set when the slider is disabled
|
|
55
|
+
* `readonly` | Set when the slider is read-only
|
|
56
|
+
* `focused` | Set when the slider has focus
|
|
57
|
+
* `focus-ring` | Set when the slider is focused using the keyboard
|
|
58
|
+
* `start-active` | Set when the start thumb is activated with mouse or touch
|
|
59
|
+
* `end-active` | Set when the end thumb is activated with mouse or touch
|
|
60
|
+
* `start-focused` | Set when the start thumb has focus
|
|
61
|
+
* `end-focused` | Set when the end thumb has focus
|
|
62
|
+
* `min-max-visible` | Set when the min/max labels are displayed
|
|
57
63
|
*
|
|
58
64
|
* The following custom CSS properties are available for styling:
|
|
59
65
|
*
|
|
@@ -70,20 +76,52 @@ import { SliderMixin } from './vaadin-slider-mixin.js';
|
|
|
70
76
|
* `--vaadin-input-field-label-font-size` |
|
|
71
77
|
* `--vaadin-input-field-label-font-weight` |
|
|
72
78
|
* `--vaadin-input-field-required-indicator` |
|
|
79
|
+
* `--vaadin-slider-bubble-arrow-size` |
|
|
80
|
+
* `--vaadin-slider-bubble-background` |
|
|
81
|
+
* `--vaadin-slider-bubble-border-color` |
|
|
82
|
+
* `--vaadin-slider-bubble-border-radius` |
|
|
83
|
+
* `--vaadin-slider-bubble-border-width` |
|
|
84
|
+
* `--vaadin-slider-bubble-offset` |
|
|
85
|
+
* `--vaadin-slider-bubble-padding` |
|
|
86
|
+
* `--vaadin-slider-bubble-shadow` |
|
|
87
|
+
* `--vaadin-slider-bubble-text-color` |
|
|
88
|
+
* `--vaadin-slider-bubble-font-size` |
|
|
89
|
+
* `--vaadin-slider-bubble-font-weight` |
|
|
90
|
+
* `--vaadin-slider-bubble-line-height` |
|
|
73
91
|
* `--vaadin-slider-fill-background` |
|
|
92
|
+
* `--vaadin-slider-fill-border-color` |
|
|
93
|
+
* `--vaadin-slider-fill-border-width` |
|
|
94
|
+
* `--vaadin-slider-marks-color` |
|
|
95
|
+
* `--vaadin-slider-marks-font-size` |
|
|
96
|
+
* `--vaadin-slider-marks-font-weight` |
|
|
97
|
+
* `--vaadin-slider-thumb-border-color` |
|
|
98
|
+
* `--vaadin-slider-thumb-border-radius` |
|
|
99
|
+
* `--vaadin-slider-thumb-border-width` |
|
|
100
|
+
* `--vaadin-slider-thumb-cursor` |
|
|
101
|
+
* `--vaadin-slider-thumb-cursor-active` |
|
|
74
102
|
* `--vaadin-slider-thumb-height` |
|
|
75
103
|
* `--vaadin-slider-thumb-width` |
|
|
76
104
|
* `--vaadin-slider-track-background` |
|
|
105
|
+
* `--vaadin-slider-track-border-color` |
|
|
77
106
|
* `--vaadin-slider-track-border-radius` |
|
|
107
|
+
* `--vaadin-slider-track-border-width` |
|
|
78
108
|
* `--vaadin-slider-track-height` |
|
|
79
109
|
*
|
|
110
|
+
* In order to style the slider bubble, use `<vaadin-slider-bubble>` shadow DOM parts:
|
|
111
|
+
*
|
|
112
|
+
* Part name | Description
|
|
113
|
+
* -----------------|----------------------
|
|
114
|
+
* `overlay` | The overlay container
|
|
115
|
+
* `content` | The overlay content
|
|
116
|
+
* `arrow` | Arrow pointing to the thumb
|
|
117
|
+
*
|
|
80
118
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
81
119
|
*
|
|
82
120
|
* @fires {Event} change - Fired when the user commits a value change.
|
|
83
121
|
* @fires {Event} input - Fired when the slider value changes during user interaction.
|
|
84
122
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
85
123
|
*
|
|
86
|
-
* @customElement
|
|
124
|
+
* @customElement vaadin-range-slider
|
|
87
125
|
* @extends HTMLElement
|
|
88
126
|
* @mixes ElementMixin
|
|
89
127
|
* @mixes FieldMixin
|
|
@@ -183,6 +221,88 @@ class RangeSlider extends FieldMixin(
|
|
|
183
221
|
notify: true,
|
|
184
222
|
sync: true,
|
|
185
223
|
},
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Custom accessible name for the start (minimum) input.
|
|
227
|
+
* When not set, defaults to "${label} min" or "min" if no label.
|
|
228
|
+
* @attr {string} accessible-name-start
|
|
229
|
+
*/
|
|
230
|
+
accessibleNameStart: {
|
|
231
|
+
type: String,
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Custom accessible name for the end (maximum) input.
|
|
236
|
+
* When not set, defaults to "${label} max" or "max" if no label.
|
|
237
|
+
* @attr {string} accessible-name-end
|
|
238
|
+
*/
|
|
239
|
+
accessibleNameEnd: {
|
|
240
|
+
type: String,
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
/** @private */
|
|
244
|
+
__startActive: {
|
|
245
|
+
type: Boolean,
|
|
246
|
+
value: false,
|
|
247
|
+
reflectToAttribute: true,
|
|
248
|
+
attribute: 'start-active',
|
|
249
|
+
sync: true,
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
/** @private */
|
|
253
|
+
__endActive: {
|
|
254
|
+
type: Boolean,
|
|
255
|
+
value: false,
|
|
256
|
+
reflectToAttribute: true,
|
|
257
|
+
attribute: 'end-active',
|
|
258
|
+
sync: true,
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
/** @private */
|
|
262
|
+
__startFocused: {
|
|
263
|
+
type: Boolean,
|
|
264
|
+
value: false,
|
|
265
|
+
reflectToAttribute: true,
|
|
266
|
+
attribute: 'start-focused',
|
|
267
|
+
sync: true,
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
/** @private */
|
|
271
|
+
__endFocused: {
|
|
272
|
+
type: Boolean,
|
|
273
|
+
value: false,
|
|
274
|
+
reflectToAttribute: true,
|
|
275
|
+
attribute: 'end-focused',
|
|
276
|
+
sync: true,
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
/** @private */
|
|
280
|
+
__startHover: {
|
|
281
|
+
type: Boolean,
|
|
282
|
+
value: false,
|
|
283
|
+
sync: true,
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
/** @private */
|
|
287
|
+
__endHover: {
|
|
288
|
+
type: Boolean,
|
|
289
|
+
value: false,
|
|
290
|
+
sync: true,
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
/** @private */
|
|
294
|
+
__startBubbleOpened: {
|
|
295
|
+
type: Boolean,
|
|
296
|
+
value: false,
|
|
297
|
+
sync: true,
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
/** @private */
|
|
301
|
+
__endBubbleOpened: {
|
|
302
|
+
type: Boolean,
|
|
303
|
+
value: false,
|
|
304
|
+
sync: true,
|
|
305
|
+
},
|
|
186
306
|
};
|
|
187
307
|
}
|
|
188
308
|
|
|
@@ -192,6 +312,7 @@ class RangeSlider extends FieldMixin(
|
|
|
192
312
|
|
|
193
313
|
const startPercent = this.__getPercentFromValue(startValue);
|
|
194
314
|
const endPercent = this.__getPercentFromValue(endValue);
|
|
315
|
+
const { min, max } = this.__getConstraints();
|
|
195
316
|
|
|
196
317
|
return html`
|
|
197
318
|
<div class="vaadin-slider-container">
|
|
@@ -207,6 +328,12 @@ class RangeSlider extends FieldMixin(
|
|
|
207
328
|
<div part="thumb thumb-start"></div>
|
|
208
329
|
<div part="thumb thumb-end"></div>
|
|
209
330
|
<slot name="input"></slot>
|
|
331
|
+
<slot name="bubble"></slot>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div part="marks" aria-hidden="true">
|
|
335
|
+
<span part="min">${min}</span>
|
|
336
|
+
<span part="max">${max}</span>
|
|
210
337
|
</div>
|
|
211
338
|
|
|
212
339
|
<div part="helper-text">
|
|
@@ -227,8 +354,7 @@ class RangeSlider extends FieldMixin(
|
|
|
227
354
|
this.__inputId0 = `slider-${generateUniqueId()}`;
|
|
228
355
|
this.__inputId1 = `slider-${generateUniqueId()}`;
|
|
229
356
|
|
|
230
|
-
this.
|
|
231
|
-
this.addEventListener('pointercancel', (e) => this.__onPointerUp(e));
|
|
357
|
+
this.__onPointerUp = this.__onPointerUp.bind(this);
|
|
232
358
|
}
|
|
233
359
|
|
|
234
360
|
/** @protected */
|
|
@@ -238,6 +364,11 @@ class RangeSlider extends FieldMixin(
|
|
|
238
364
|
const inputs = this.querySelectorAll('[slot="input"]');
|
|
239
365
|
this._inputElements = [...inputs];
|
|
240
366
|
this.ariaTarget = this;
|
|
367
|
+
|
|
368
|
+
this.__thumbStartElement = this.shadowRoot.querySelector('[part~="thumb-start"]');
|
|
369
|
+
this.__thumbEndElement = this.shadowRoot.querySelector('[part~="thumb-end"]');
|
|
370
|
+
|
|
371
|
+
this.__bubbleElements = [...this.querySelectorAll('vaadin-slider-bubble')];
|
|
241
372
|
}
|
|
242
373
|
|
|
243
374
|
/** @private */
|
|
@@ -246,18 +377,20 @@ class RangeSlider extends FieldMixin(
|
|
|
246
377
|
|
|
247
378
|
const index = this._inputElements.indexOf(event.composedPath()[0]);
|
|
248
379
|
|
|
249
|
-
if (index !== -1) {
|
|
380
|
+
if (!this.readonly && index !== -1) {
|
|
250
381
|
this.toggleAttribute('start-active', index === 0);
|
|
251
382
|
this.toggleAttribute('end-active', index === 1);
|
|
383
|
+
window.addEventListener('pointerup', this.__onPointerUp);
|
|
384
|
+
window.addEventListener('pointercancel', this.__onPointerUp);
|
|
252
385
|
}
|
|
253
386
|
}
|
|
254
387
|
|
|
255
388
|
/** @private */
|
|
256
|
-
__onPointerUp(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
389
|
+
__onPointerUp() {
|
|
390
|
+
window.removeEventListener('pointerup', this.__onPointerUp);
|
|
391
|
+
window.removeEventListener('pointercancel', this.__onPointerUp);
|
|
392
|
+
this.removeAttribute('start-active');
|
|
393
|
+
this.removeAttribute('end-active');
|
|
261
394
|
}
|
|
262
395
|
|
|
263
396
|
/**
|
|
@@ -277,12 +410,16 @@ class RangeSlider extends FieldMixin(
|
|
|
277
410
|
type="range"
|
|
278
411
|
id="${this.__inputId0}"
|
|
279
412
|
slot="input"
|
|
413
|
+
aria-label="${this.accessibleNameStart || this.__getAriaLabel('min')}"
|
|
280
414
|
.min="${min}"
|
|
281
415
|
.max="${max}"
|
|
282
416
|
.step="${step}"
|
|
283
417
|
.value="${startValue}"
|
|
284
418
|
.disabled="${this.disabled}"
|
|
285
419
|
tabindex="${this.disabled ? -1 : 0}"
|
|
420
|
+
@pointerenter="${this.__onStartPointerEnter}"
|
|
421
|
+
@pointermove="${this.__onStartPointerMove}"
|
|
422
|
+
@pointerleave="${this.__onStartPointerLeave}"
|
|
286
423
|
@keydown="${this.__onKeyDown}"
|
|
287
424
|
@input="${this.__onStartInput}"
|
|
288
425
|
@change="${this.__onChange}"
|
|
@@ -291,22 +428,63 @@ class RangeSlider extends FieldMixin(
|
|
|
291
428
|
type="range"
|
|
292
429
|
id="${this.__inputId1}"
|
|
293
430
|
slot="input"
|
|
431
|
+
aria-label="${this.accessibleNameEnd || this.__getAriaLabel('max')}"
|
|
294
432
|
.min="${min}"
|
|
295
433
|
.max="${max}"
|
|
296
434
|
.step="${step}"
|
|
297
435
|
.value="${endValue}"
|
|
298
436
|
.disabled="${this.disabled}"
|
|
299
437
|
tabindex="${this.disabled ? -1 : 0}"
|
|
438
|
+
@pointerenter="${this.__onEndPointerEnter}"
|
|
439
|
+
@pointermove="${this.__onEndPointerMove}"
|
|
440
|
+
@pointerleave="${this.__onEndPointerLeave}"
|
|
300
441
|
@keydown="${this.__onKeyDown}"
|
|
301
442
|
@input="${this.__onEndInput}"
|
|
302
443
|
@change="${this.__onChange}"
|
|
303
444
|
/>
|
|
445
|
+
<vaadin-slider-bubble
|
|
446
|
+
slot="bubble"
|
|
447
|
+
.positionTarget="${this.__thumbStartElement}"
|
|
448
|
+
.opened="${this.valueAlwaysVisible || this.__startBubbleOpened}"
|
|
449
|
+
theme="${ifDefined(this._theme)}"
|
|
450
|
+
>
|
|
451
|
+
${startValue}
|
|
452
|
+
</vaadin-slider-bubble>
|
|
453
|
+
<vaadin-slider-bubble
|
|
454
|
+
slot="bubble"
|
|
455
|
+
.positionTarget="${this.__thumbEndElement}"
|
|
456
|
+
.opened="${this.valueAlwaysVisible || this.__endBubbleOpened}"
|
|
457
|
+
theme="${ifDefined(this._theme)}"
|
|
458
|
+
>
|
|
459
|
+
${endValue}
|
|
460
|
+
</vaadin-slider-bubble>
|
|
304
461
|
`,
|
|
305
462
|
this,
|
|
306
463
|
{ host: this },
|
|
307
464
|
);
|
|
308
465
|
}
|
|
309
466
|
|
|
467
|
+
/** @protected */
|
|
468
|
+
willUpdate(props) {
|
|
469
|
+
super.willUpdate(props);
|
|
470
|
+
|
|
471
|
+
this.__updateBubbleState(props, {
|
|
472
|
+
active: '__startActive',
|
|
473
|
+
focused: '__startFocused',
|
|
474
|
+
hover: '__startHover',
|
|
475
|
+
opened: '__startBubbleOpened',
|
|
476
|
+
otherOpened: '__endBubbleOpened',
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
this.__updateBubbleState(props, {
|
|
480
|
+
active: '__endActive',
|
|
481
|
+
focused: '__endFocused',
|
|
482
|
+
hover: '__endHover',
|
|
483
|
+
opened: '__endBubbleOpened',
|
|
484
|
+
otherOpened: '__startBubbleOpened',
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
310
488
|
/** @protected */
|
|
311
489
|
updated(props) {
|
|
312
490
|
super.updated(props);
|
|
@@ -360,8 +538,13 @@ class RangeSlider extends FieldMixin(
|
|
|
360
538
|
_setFocused(focused) {
|
|
361
539
|
super._setFocused(focused);
|
|
362
540
|
|
|
363
|
-
this.
|
|
364
|
-
this.
|
|
541
|
+
this.__startFocused = isElementFocused(this._inputElements[0]);
|
|
542
|
+
this.__endFocused = isElementFocused(this._inputElements[1]);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/** @private */
|
|
546
|
+
__getAriaLabel(suffix) {
|
|
547
|
+
return this.label ? `${this.label} ${suffix}` : suffix;
|
|
365
548
|
}
|
|
366
549
|
|
|
367
550
|
/** @private */
|
|
@@ -380,6 +563,7 @@ class RangeSlider extends FieldMixin(
|
|
|
380
563
|
|
|
381
564
|
const value = event.target.value;
|
|
382
565
|
this.__updateValue(value, 0);
|
|
566
|
+
this.__updateBubble(0);
|
|
383
567
|
this.__dispatchInputEvent();
|
|
384
568
|
this.__commitValue();
|
|
385
569
|
}
|
|
@@ -395,14 +579,60 @@ class RangeSlider extends FieldMixin(
|
|
|
395
579
|
|
|
396
580
|
const value = event.target.value;
|
|
397
581
|
this.__updateValue(value, 1);
|
|
582
|
+
this.__updateBubble(1);
|
|
398
583
|
this.__dispatchInputEvent();
|
|
399
584
|
this.__commitValue();
|
|
400
585
|
}
|
|
401
586
|
|
|
587
|
+
/** @private */
|
|
588
|
+
__isThumbEvent(event, thumb) {
|
|
589
|
+
const rect = thumb.getBoundingClientRect();
|
|
590
|
+
return (
|
|
591
|
+
event.clientX >= rect.left &&
|
|
592
|
+
event.clientX <= rect.right &&
|
|
593
|
+
event.clientY >= rect.top &&
|
|
594
|
+
event.clientY <= rect.bottom
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** @private */
|
|
599
|
+
__onStartPointerEnter(event) {
|
|
600
|
+
if (this.__isThumbEvent(event, this.__thumbStartElement)) {
|
|
601
|
+
this.__startHover = true;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** @private */
|
|
606
|
+
__onStartPointerMove(event) {
|
|
607
|
+
this.__startHover = this.__isThumbEvent(event, this.__thumbStartElement);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/** @private */
|
|
611
|
+
__onStartPointerLeave() {
|
|
612
|
+
this.__startHover = false;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/** @private */
|
|
616
|
+
__onEndPointerEnter(event) {
|
|
617
|
+
if (this.__isThumbEvent(event, this.__thumbEndElement)) {
|
|
618
|
+
this.__endHover = true;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/** @private */
|
|
623
|
+
__onEndPointerMove(event) {
|
|
624
|
+
this.__endHover = this.__isThumbEvent(event, this.__thumbEndElement);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** @private */
|
|
628
|
+
__onEndPointerLeave() {
|
|
629
|
+
this.__endHover = false;
|
|
630
|
+
}
|
|
631
|
+
|
|
402
632
|
/** @private */
|
|
403
633
|
__onKeyDown(event) {
|
|
404
|
-
const prevKeys = ['ArrowLeft', 'ArrowDown'];
|
|
405
|
-
const nextKeys = ['ArrowRight', 'ArrowUp'];
|
|
634
|
+
const prevKeys = ['ArrowLeft', 'ArrowDown', 'PageDown', 'Home'];
|
|
635
|
+
const nextKeys = ['ArrowRight', 'ArrowUp', 'PageUp', 'End'];
|
|
406
636
|
|
|
407
637
|
const isNextKey = nextKeys.includes(event.key);
|
|
408
638
|
const isPrevKey = prevKeys.includes(event.key);
|
|
@@ -422,6 +652,11 @@ class RangeSlider extends FieldMixin(
|
|
|
422
652
|
event.preventDefault();
|
|
423
653
|
}
|
|
424
654
|
}
|
|
655
|
+
|
|
656
|
+
/** @private */
|
|
657
|
+
__updateBubble(idx) {
|
|
658
|
+
this.__bubbleElements[idx].$.overlay._updatePosition();
|
|
659
|
+
}
|
|
425
660
|
}
|
|
426
661
|
|
|
427
662
|
defineCustomElement(RangeSlider);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { html, LitElement } from 'lit';
|
|
7
|
+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
8
|
+
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
|
|
9
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
10
|
+
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
|
|
11
|
+
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
|
|
12
|
+
import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
|
|
13
|
+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
14
|
+
import { sliderBubbleOverlayStyles } from './styles/vaadin-slider-bubble-overlay-base-styles.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* An element used internally by `<vaadin-slider>`. Not intended to be used separately.
|
|
18
|
+
*
|
|
19
|
+
* @customElement vaadin-slider-bubble-overlay
|
|
20
|
+
* @extends HTMLElement
|
|
21
|
+
* @mixes DirMixin
|
|
22
|
+
* @mixes OverlayMixin
|
|
23
|
+
* @mixes PositionMixin
|
|
24
|
+
* @mixes ThemableMixin
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
class SliderBubbleOverlay extends PositionMixin(
|
|
28
|
+
OverlayMixin(DirMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
|
|
29
|
+
) {
|
|
30
|
+
static get is() {
|
|
31
|
+
return 'vaadin-slider-bubble-overlay';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static get styles() {
|
|
35
|
+
return sliderBubbleOverlayStyles;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static get lumoInjector() {
|
|
39
|
+
return { ...super.lumoInjector, includeBaseStyles: true };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @protected */
|
|
43
|
+
render() {
|
|
44
|
+
return html`
|
|
45
|
+
<div part="overlay" id="overlay">
|
|
46
|
+
<div part="arrow"></div>
|
|
47
|
+
<div part="content" id="content"><slot></slot></div>
|
|
48
|
+
</div>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Override method from `OverlayMixin` to not close on outside click.
|
|
54
|
+
* The bubble overlay `opened` is fully controlled by the slider.
|
|
55
|
+
* @return {boolean}
|
|
56
|
+
* @protected
|
|
57
|
+
* @override
|
|
58
|
+
*/
|
|
59
|
+
_shouldCloseOnOutsideClick() {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @protected
|
|
65
|
+
* @override
|
|
66
|
+
*/
|
|
67
|
+
_updatePosition() {
|
|
68
|
+
super._updatePosition();
|
|
69
|
+
|
|
70
|
+
if (!this.positionTarget || !this.opened) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const targetRect = this.positionTarget.getBoundingClientRect();
|
|
75
|
+
const overlayRect = this.$.overlay.getBoundingClientRect();
|
|
76
|
+
|
|
77
|
+
const offset = targetRect.width / 2 - overlayRect.width / 2;
|
|
78
|
+
|
|
79
|
+
if (this.style.left) {
|
|
80
|
+
const left = overlayRect.left + offset;
|
|
81
|
+
if (left > 0) {
|
|
82
|
+
this.style.left = `${left}px`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (this.style.right) {
|
|
87
|
+
const right = parseFloat(this.style.right) + offset;
|
|
88
|
+
if (right > 0) {
|
|
89
|
+
this.style.right = `${right}px`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
defineCustomElement(SliderBubbleOverlay);
|
|
96
|
+
|
|
97
|
+
export { SliderBubbleOverlay };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* An element used internally by `<vaadin-slider>`. Not intended to be used separately.
|
|
9
|
+
*/
|
|
10
|
+
declare class SliderBubble extends HTMLElement {
|
|
11
|
+
/**
|
|
12
|
+
* The thumb element next to which the overlay should be aligned.
|
|
13
|
+
*/
|
|
14
|
+
positionTarget: HTMLElement;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Whether the overlay is opened.
|
|
18
|
+
*/
|
|
19
|
+
opened: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface HTMLElementTagNameMap {
|
|
24
|
+
'vaadin-slider-bubble': SliderBubble;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { SliderBubble };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2026 - 2026 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import './vaadin-slider-bubble-overlay.js';
|
|
7
|
+
import { css, html, LitElement } from 'lit';
|
|
8
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
9
|
+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
10
|
+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
11
|
+
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* An element used internally by `<vaadin-slider>`. Not intended to be used separately.
|
|
15
|
+
*
|
|
16
|
+
* @customElement vaadin-slider-bubble
|
|
17
|
+
* @extends HTMLElement
|
|
18
|
+
* @private
|
|
19
|
+
*/
|
|
20
|
+
class SliderBubble extends ThemePropertyMixin(PolylitMixin(LitElement)) {
|
|
21
|
+
static get is() {
|
|
22
|
+
return 'vaadin-slider-bubble';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static get styles() {
|
|
26
|
+
return css`
|
|
27
|
+
:host {
|
|
28
|
+
display: contents;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static get properties() {
|
|
34
|
+
return {
|
|
35
|
+
/**
|
|
36
|
+
* The thumb element next to which the overlay should be aligned.
|
|
37
|
+
*/
|
|
38
|
+
positionTarget: {
|
|
39
|
+
type: Object,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Whether the overlay is opened.
|
|
44
|
+
*/
|
|
45
|
+
opened: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
value: false,
|
|
48
|
+
reflectToAttribute: true,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** @protected */
|
|
54
|
+
render() {
|
|
55
|
+
return html`
|
|
56
|
+
<vaadin-slider-bubble-overlay
|
|
57
|
+
id="overlay"
|
|
58
|
+
.owner="${this}"
|
|
59
|
+
.opened="${this.opened}"
|
|
60
|
+
.positionTarget="${this.positionTarget}"
|
|
61
|
+
theme="${ifDefined(this._theme)}"
|
|
62
|
+
vertical-align="bottom"
|
|
63
|
+
no-vertical-overlap
|
|
64
|
+
modeless
|
|
65
|
+
exportparts="overlay, content, arrow"
|
|
66
|
+
>
|
|
67
|
+
<slot></slot>
|
|
68
|
+
</vaadin-slider-bubble-overlay>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
defineCustomElement(SliderBubble);
|
|
74
|
+
|
|
75
|
+
export { SliderBubble };
|
|
@@ -34,4 +34,17 @@ export declare class SliderMixinClass {
|
|
|
34
34
|
* readers.
|
|
35
35
|
*/
|
|
36
36
|
readonly: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* When true, the value bubble is always visible,
|
|
40
|
+
* regardless of focus or hover state.
|
|
41
|
+
* @attr {boolean} value-always-visible
|
|
42
|
+
*/
|
|
43
|
+
valueAlwaysVisible: boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* When true, displays the min and max values below the slider track.
|
|
47
|
+
* @attr {boolean} min-max-visible
|
|
48
|
+
*/
|
|
49
|
+
minMaxVisible: boolean;
|
|
37
50
|
}
|