@vaadin/slider 25.1.0-alpha5 → 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 -8
- package/src/vaadin-range-slider.js +273 -13
- 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 +97 -5
- package/src/vaadin-slider.d.ts +43 -6
- package/src/vaadin-slider.js +185 -13
- 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,17 +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-
|
|
54
|
-
* `end-
|
|
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
|
|
55
63
|
*
|
|
56
64
|
* The following custom CSS properties are available for styling:
|
|
57
65
|
*
|
|
@@ -68,20 +76,52 @@ import { SliderMixin } from './vaadin-slider-mixin.js';
|
|
|
68
76
|
* `--vaadin-input-field-label-font-size` |
|
|
69
77
|
* `--vaadin-input-field-label-font-weight` |
|
|
70
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` |
|
|
71
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` |
|
|
72
102
|
* `--vaadin-slider-thumb-height` |
|
|
73
103
|
* `--vaadin-slider-thumb-width` |
|
|
74
104
|
* `--vaadin-slider-track-background` |
|
|
105
|
+
* `--vaadin-slider-track-border-color` |
|
|
75
106
|
* `--vaadin-slider-track-border-radius` |
|
|
107
|
+
* `--vaadin-slider-track-border-width` |
|
|
76
108
|
* `--vaadin-slider-track-height` |
|
|
77
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
|
+
*
|
|
78
118
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
79
119
|
*
|
|
80
120
|
* @fires {Event} change - Fired when the user commits a value change.
|
|
81
121
|
* @fires {Event} input - Fired when the slider value changes during user interaction.
|
|
82
122
|
* @fires {CustomEvent} value-changed - Fired when the `value` property changes.
|
|
83
123
|
*
|
|
84
|
-
* @customElement
|
|
124
|
+
* @customElement vaadin-range-slider
|
|
85
125
|
* @extends HTMLElement
|
|
86
126
|
* @mixes ElementMixin
|
|
87
127
|
* @mixes FieldMixin
|
|
@@ -181,6 +221,88 @@ class RangeSlider extends FieldMixin(
|
|
|
181
221
|
notify: true,
|
|
182
222
|
sync: true,
|
|
183
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
|
+
},
|
|
184
306
|
};
|
|
185
307
|
}
|
|
186
308
|
|
|
@@ -190,6 +312,7 @@ class RangeSlider extends FieldMixin(
|
|
|
190
312
|
|
|
191
313
|
const startPercent = this.__getPercentFromValue(startValue);
|
|
192
314
|
const endPercent = this.__getPercentFromValue(endValue);
|
|
315
|
+
const { min, max } = this.__getConstraints();
|
|
193
316
|
|
|
194
317
|
return html`
|
|
195
318
|
<div class="vaadin-slider-container">
|
|
@@ -205,6 +328,12 @@ class RangeSlider extends FieldMixin(
|
|
|
205
328
|
<div part="thumb thumb-start"></div>
|
|
206
329
|
<div part="thumb thumb-end"></div>
|
|
207
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>
|
|
208
337
|
</div>
|
|
209
338
|
|
|
210
339
|
<div part="helper-text">
|
|
@@ -224,6 +353,8 @@ class RangeSlider extends FieldMixin(
|
|
|
224
353
|
this.__value = [...this.value];
|
|
225
354
|
this.__inputId0 = `slider-${generateUniqueId()}`;
|
|
226
355
|
this.__inputId1 = `slider-${generateUniqueId()}`;
|
|
356
|
+
|
|
357
|
+
this.__onPointerUp = this.__onPointerUp.bind(this);
|
|
227
358
|
}
|
|
228
359
|
|
|
229
360
|
/** @protected */
|
|
@@ -233,6 +364,33 @@ class RangeSlider extends FieldMixin(
|
|
|
233
364
|
const inputs = this.querySelectorAll('[slot="input"]');
|
|
234
365
|
this._inputElements = [...inputs];
|
|
235
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')];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** @private */
|
|
375
|
+
__onPointerDown(event) {
|
|
376
|
+
super.__onPointerDown(event);
|
|
377
|
+
|
|
378
|
+
const index = this._inputElements.indexOf(event.composedPath()[0]);
|
|
379
|
+
|
|
380
|
+
if (!this.readonly && index !== -1) {
|
|
381
|
+
this.toggleAttribute('start-active', index === 0);
|
|
382
|
+
this.toggleAttribute('end-active', index === 1);
|
|
383
|
+
window.addEventListener('pointerup', this.__onPointerUp);
|
|
384
|
+
window.addEventListener('pointercancel', this.__onPointerUp);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** @private */
|
|
389
|
+
__onPointerUp() {
|
|
390
|
+
window.removeEventListener('pointerup', this.__onPointerUp);
|
|
391
|
+
window.removeEventListener('pointercancel', this.__onPointerUp);
|
|
392
|
+
this.removeAttribute('start-active');
|
|
393
|
+
this.removeAttribute('end-active');
|
|
236
394
|
}
|
|
237
395
|
|
|
238
396
|
/**
|
|
@@ -252,12 +410,16 @@ class RangeSlider extends FieldMixin(
|
|
|
252
410
|
type="range"
|
|
253
411
|
id="${this.__inputId0}"
|
|
254
412
|
slot="input"
|
|
413
|
+
aria-label="${this.accessibleNameStart || this.__getAriaLabel('min')}"
|
|
255
414
|
.min="${min}"
|
|
256
415
|
.max="${max}"
|
|
257
416
|
.step="${step}"
|
|
258
417
|
.value="${startValue}"
|
|
259
418
|
.disabled="${this.disabled}"
|
|
260
419
|
tabindex="${this.disabled ? -1 : 0}"
|
|
420
|
+
@pointerenter="${this.__onStartPointerEnter}"
|
|
421
|
+
@pointermove="${this.__onStartPointerMove}"
|
|
422
|
+
@pointerleave="${this.__onStartPointerLeave}"
|
|
261
423
|
@keydown="${this.__onKeyDown}"
|
|
262
424
|
@input="${this.__onStartInput}"
|
|
263
425
|
@change="${this.__onChange}"
|
|
@@ -266,22 +428,63 @@ class RangeSlider extends FieldMixin(
|
|
|
266
428
|
type="range"
|
|
267
429
|
id="${this.__inputId1}"
|
|
268
430
|
slot="input"
|
|
431
|
+
aria-label="${this.accessibleNameEnd || this.__getAriaLabel('max')}"
|
|
269
432
|
.min="${min}"
|
|
270
433
|
.max="${max}"
|
|
271
434
|
.step="${step}"
|
|
272
435
|
.value="${endValue}"
|
|
273
436
|
.disabled="${this.disabled}"
|
|
274
437
|
tabindex="${this.disabled ? -1 : 0}"
|
|
438
|
+
@pointerenter="${this.__onEndPointerEnter}"
|
|
439
|
+
@pointermove="${this.__onEndPointerMove}"
|
|
440
|
+
@pointerleave="${this.__onEndPointerLeave}"
|
|
275
441
|
@keydown="${this.__onKeyDown}"
|
|
276
442
|
@input="${this.__onEndInput}"
|
|
277
443
|
@change="${this.__onChange}"
|
|
278
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>
|
|
279
461
|
`,
|
|
280
462
|
this,
|
|
281
463
|
{ host: this },
|
|
282
464
|
);
|
|
283
465
|
}
|
|
284
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
|
+
|
|
285
488
|
/** @protected */
|
|
286
489
|
updated(props) {
|
|
287
490
|
super.updated(props);
|
|
@@ -335,8 +538,13 @@ class RangeSlider extends FieldMixin(
|
|
|
335
538
|
_setFocused(focused) {
|
|
336
539
|
super._setFocused(focused);
|
|
337
540
|
|
|
338
|
-
this.
|
|
339
|
-
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;
|
|
340
548
|
}
|
|
341
549
|
|
|
342
550
|
/** @private */
|
|
@@ -355,6 +563,7 @@ class RangeSlider extends FieldMixin(
|
|
|
355
563
|
|
|
356
564
|
const value = event.target.value;
|
|
357
565
|
this.__updateValue(value, 0);
|
|
566
|
+
this.__updateBubble(0);
|
|
358
567
|
this.__dispatchInputEvent();
|
|
359
568
|
this.__commitValue();
|
|
360
569
|
}
|
|
@@ -370,14 +579,60 @@ class RangeSlider extends FieldMixin(
|
|
|
370
579
|
|
|
371
580
|
const value = event.target.value;
|
|
372
581
|
this.__updateValue(value, 1);
|
|
582
|
+
this.__updateBubble(1);
|
|
373
583
|
this.__dispatchInputEvent();
|
|
374
584
|
this.__commitValue();
|
|
375
585
|
}
|
|
376
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
|
+
|
|
377
632
|
/** @private */
|
|
378
633
|
__onKeyDown(event) {
|
|
379
|
-
const prevKeys = ['ArrowLeft', 'ArrowDown'];
|
|
380
|
-
const nextKeys = ['ArrowRight', 'ArrowUp'];
|
|
634
|
+
const prevKeys = ['ArrowLeft', 'ArrowDown', 'PageDown', 'Home'];
|
|
635
|
+
const nextKeys = ['ArrowRight', 'ArrowUp', 'PageUp', 'End'];
|
|
381
636
|
|
|
382
637
|
const isNextKey = nextKeys.includes(event.key);
|
|
383
638
|
const isPrevKey = prevKeys.includes(event.key);
|
|
@@ -397,6 +652,11 @@ class RangeSlider extends FieldMixin(
|
|
|
397
652
|
event.preventDefault();
|
|
398
653
|
}
|
|
399
654
|
}
|
|
655
|
+
|
|
656
|
+
/** @private */
|
|
657
|
+
__updateBubble(idx) {
|
|
658
|
+
this.__bubbleElements[idx].$.overlay._updatePosition();
|
|
659
|
+
}
|
|
400
660
|
}
|
|
401
661
|
|
|
402
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
|
}
|