@vaadin/slider 25.1.0-alpha2 → 25.1.0-alpha4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/slider",
3
- "version": "25.1.0-alpha2",
3
+ "version": "25.1.0-alpha4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -34,19 +34,21 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@open-wc/dedupe-mixin": "^1.3.0",
37
- "@vaadin/a11y-base": "25.1.0-alpha2",
38
- "@vaadin/component-base": "25.1.0-alpha2",
39
- "@vaadin/vaadin-themable-mixin": "25.1.0-alpha2",
37
+ "@vaadin/a11y-base": "25.1.0-alpha4",
38
+ "@vaadin/component-base": "25.1.0-alpha4",
39
+ "@vaadin/field-base": "25.1.0-alpha4",
40
+ "@vaadin/vaadin-themable-mixin": "25.1.0-alpha4",
40
41
  "lit": "^3.0.0"
41
42
  },
42
43
  "devDependencies": {
43
- "@vaadin/chai-plugins": "25.1.0-alpha2",
44
- "@vaadin/test-runner-commands": "25.1.0-alpha2",
45
- "@vaadin/testing-helpers": "^2.0.0"
44
+ "@vaadin/chai-plugins": "25.1.0-alpha4",
45
+ "@vaadin/test-runner-commands": "25.1.0-alpha4",
46
+ "@vaadin/testing-helpers": "^2.0.0",
47
+ "@vaadin/vaadin-lumo-styles": "25.1.0-alpha4"
46
48
  },
47
49
  "web-types": [
48
50
  "web-types.json",
49
51
  "web-types.lit.json"
50
52
  ],
51
- "gitHead": "dfeb6e14643ec923e5505ca645f7354c6dc170ec"
53
+ "gitHead": "4fb917150617231c1acf27faabf386560dcd3bc5"
52
54
  }
@@ -8,17 +8,11 @@ import { css } from 'lit';
8
8
 
9
9
  export const sliderStyles = css`
10
10
  :host {
11
- display: inline-flex;
12
- align-items: center;
13
11
  box-sizing: border-box;
14
- position: relative;
15
- width: 100%;
16
- height: var(--_thumb-size);
17
12
  user-select: none;
18
13
  -webkit-user-select: none;
19
- border-radius: var(--vaadin-slider-track-border-radius, var(--vaadin-radius-m));
20
- --_thumb-size: var(--vaadin-slider-thumb-size, 1lh);
21
- --_track-size: var(--vaadin-slider-track-size, 0.25lh);
14
+ --_thumb-width: var(--vaadin-slider-thumb-width, 1lh);
15
+ --_thumb-height: var(--vaadin-slider-thumb-height, 1lh);
22
16
  }
23
17
 
24
18
  :host([hidden]) {
@@ -36,32 +30,51 @@ export const sliderStyles = css`
36
30
 
37
31
  :host([readonly]) {
38
32
  --vaadin-slider-fill-background: var(--vaadin-background-color);
33
+ --_outline-style: dashed;
34
+ }
35
+
36
+ #controls {
37
+ grid-area: input;
38
+ display: inline-grid;
39
+ align-items: center;
40
+ width: var(--vaadin-field-default-width, 12em);
41
+ max-width: 100%;
42
+ min-width: 100%;
43
+ min-height: var(--_thumb-height);
44
+ --_track-width: calc(100% - var(--_thumb-width));
45
+ }
46
+
47
+ :host([has-label]) #controls {
48
+ border-block: var(--vaadin-input-field-border-width, 1px) solid transparent;
49
+ padding-block: var(--vaadin-padding-block-container);
39
50
  }
40
51
 
41
52
  [part='track'] {
42
53
  box-sizing: border-box;
43
- position: absolute;
44
- height: var(--_track-size);
54
+ grid-row: 1;
55
+ grid-column: track-start / track-end;
56
+ display: grid;
57
+ grid-template-columns: subgrid;
58
+ align-items: center;
45
59
  width: 100%;
60
+ height: var(--vaadin-slider-track-height, 0.25lh);
46
61
  background: var(--vaadin-slider-track-background, var(--vaadin-background-container));
47
- border-radius: inherit;
62
+ border-radius: var(--vaadin-slider-track-border-radius, var(--vaadin-radius-m));
48
63
  }
49
64
 
50
65
  [part='track-fill'] {
51
66
  box-sizing: border-box;
52
- position: absolute;
53
- height: var(--_track-size);
67
+ grid-column: fill-start / fill-end;
68
+ height: 100%;
54
69
  background: var(--vaadin-slider-fill-background, var(--vaadin-text-color));
55
- border-start-start-radius: inherit;
56
- border-end-start-radius: inherit;
57
70
  }
58
71
 
59
72
  [part~='thumb'] {
60
- position: absolute;
61
73
  box-sizing: border-box;
62
- width: var(--_thumb-size);
63
- height: var(--_thumb-size);
64
- transform: translateX(-50%);
74
+ grid-row: 1;
75
+ grid-column: thumb1;
76
+ width: var(--_thumb-width);
77
+ height: var(--_thumb-height);
65
78
  background: var(--vaadin-slider-fill-background, var(--vaadin-text-color));
66
79
  border-radius: 50%;
67
80
  touch-action: none;
@@ -72,13 +85,20 @@ export const sliderStyles = css`
72
85
  border: dashed 1px var(--vaadin-border-color);
73
86
  }
74
87
 
75
- /* visually hidden */
88
+ :host([readonly]) [part='track-fill'] {
89
+ border-inline-end: none;
90
+ }
91
+
76
92
  ::slotted(input) {
77
- flex: 1;
93
+ grid-row: 1;
94
+ grid-column: track-start / track-end;
95
+ appearance: none;
96
+ width: 100%;
97
+ height: 100%;
78
98
  font: inherit;
79
- height: var(--_thumb-size);
80
- opacity: 0 !important;
81
- margin: 0 !important;
82
- pointer-events: none;
99
+ margin: 0;
100
+ background: transparent;
101
+ outline: 0;
102
+ -webkit-tap-highlight-color: transparent;
83
103
  }
84
104
  `;
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
7
7
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
8
+ import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
8
9
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
9
10
  import { SliderMixin } from './vaadin-slider-mixin.js';
10
11
 
@@ -39,7 +40,7 @@ export interface RangeSliderEventMap extends HTMLElementEventMap, RangeSliderCus
39
40
  * @fires {Event} change - Fired when the user commits a value change.
40
41
  * @fires {CustomEvent} value-changed - Fired when the `value` property changes.
41
42
  */
42
- declare class RangeSlider extends SliderMixin(FocusMixin(ThemableMixin(ElementMixin(HTMLElement)))) {
43
+ declare class RangeSlider extends FieldMixin(SliderMixin(FocusMixin(ThemableMixin(ElementMixin(HTMLElement))))) {
43
44
  /**
44
45
  * The value of the slider.
45
46
  */
@@ -11,6 +11,8 @@ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
11
11
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
12
12
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
13
13
  import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
14
+ import { FieldMixin } from '@vaadin/field-base/src/field-mixin.js';
15
+ import { field } from '@vaadin/field-base/src/styles/field-base-styles.js';
14
16
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
15
17
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
16
18
  import { sliderStyles } from './styles/vaadin-slider-base-styles.js';
@@ -30,12 +32,13 @@ import { SliderMixin } from './vaadin-slider-mixin.js';
30
32
  * @customElement
31
33
  * @extends HTMLElement
32
34
  * @mixes ElementMixin
35
+ * @mixes FieldMixin
33
36
  * @mixes FocusMixin
34
37
  * @mixes SliderMixin
35
38
  * @mixes ThemableMixin
36
39
  */
37
- class RangeSlider extends SliderMixin(
38
- FocusMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))))),
40
+ class RangeSlider extends FieldMixin(
41
+ SliderMixin(FocusMixin(ElementMixin(ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement)))))),
39
42
  ) {
40
43
  static get is() {
41
44
  return 'vaadin-range-slider';
@@ -43,6 +46,7 @@ class RangeSlider extends SliderMixin(
43
46
 
44
47
  static get styles() {
45
48
  return [
49
+ field,
46
50
  sliderStyles,
47
51
  css`
48
52
  :host([focus-ring][start-focused]) [part~='thumb-start'],
@@ -51,8 +55,56 @@ class RangeSlider extends SliderMixin(
51
55
  outline-offset: 1px;
52
56
  }
53
57
 
54
- :host([readonly]) {
55
- --_outline-style: dashed;
58
+ #controls {
59
+ grid-template-columns:
60
+ [track-start]
61
+ calc(var(--start-value) * var(--_track-width))
62
+ [thumb1]
63
+ 0
64
+ [fill-start]
65
+ calc((var(--end-value) - var(--start-value)) * var(--_track-width))
66
+ [fill-end thumb2]
67
+ 0
68
+ calc((1 - var(--end-value)) * var(--_track-width))
69
+ var(--_thumb-width)
70
+ [track-end];
71
+ }
72
+
73
+ [part='track-fill'] {
74
+ margin-inline-start: var(--_thumb-width);
75
+ }
76
+
77
+ [part~='thumb-end'] {
78
+ grid-column: thumb2;
79
+ }
80
+
81
+ :host([readonly]) [part='track-fill'] {
82
+ border-inline-start: none;
83
+ }
84
+
85
+ ::slotted(input:last-of-type) {
86
+ clip-path: inset(
87
+ 0 0 0
88
+ clamp(
89
+ 0%,
90
+ var(--_thumb-width) / 2 + var(--start-value) * var(--_track-width) +
91
+ (var(--end-value) - var(--start-value)) * var(--_track-width) / 2,
92
+ 100%
93
+ )
94
+ );
95
+ }
96
+
97
+ :host([dir='rtl']) ::slotted(input:last-of-type) {
98
+ clip-path: inset(
99
+ 0
100
+ clamp(
101
+ 0%,
102
+ var(--_thumb-width) / 2 + var(--start-value) * var(--_track-width) +
103
+ (var(--end-value) - var(--start-value)) * var(--_track-width) / 2,
104
+ 100%
105
+ )
106
+ 0 0
107
+ );
56
108
  }
57
109
  `,
58
110
  ];
@@ -62,6 +114,10 @@ class RangeSlider extends SliderMixin(
62
114
  return 'sliderComponent';
63
115
  }
64
116
 
117
+ static get lumoInjector() {
118
+ return { ...super.lumoInjector, includeBaseStyles: true };
119
+ }
120
+
65
121
  static get properties() {
66
122
  return {
67
123
  /**
@@ -84,18 +140,29 @@ class RangeSlider extends SliderMixin(
84
140
  const endPercent = this.__getPercentFromValue(endValue);
85
141
 
86
142
  return html`
87
- <div part="track">
88
- <div
89
- part="track-fill"
90
- style="${styleMap({
91
- insetInlineStart: `${startPercent}%`,
92
- insetInlineEnd: `${100 - endPercent}%`,
93
- })}"
94
- ></div>
143
+ <div class="vaadin-slider-container">
144
+ <div part="label" @click="${this.focus}">
145
+ <slot name="label"></slot>
146
+ <span part="required-indicator" aria-hidden="true"></span>
147
+ </div>
148
+
149
+ <div id="controls" style="${styleMap({ '--start-value': startPercent, '--end-value': endPercent })}">
150
+ <div part="track">
151
+ <div part="track-fill"></div>
152
+ </div>
153
+ <div part="thumb thumb-start"></div>
154
+ <div part="thumb thumb-end"></div>
155
+ <slot name="input"></slot>
156
+ </div>
157
+
158
+ <div part="helper-text">
159
+ <slot name="helper"></slot>
160
+ </div>
161
+
162
+ <div part="error-message">
163
+ <slot name="error-message"></slot>
164
+ </div>
95
165
  </div>
96
- <div part="thumb thumb-start" style="${styleMap({ insetInlineStart: `${startPercent}%` })}"></div>
97
- <div part="thumb thumb-end" style="${styleMap({ insetInlineStart: `${endPercent}%` })}"></div>
98
- <slot name="input"></slot>
99
166
  `;
100
167
  }
101
168
 
@@ -113,6 +180,7 @@ class RangeSlider extends SliderMixin(
113
180
 
114
181
  const inputs = this.querySelectorAll('[slot="input"]');
115
182
  this._inputElements = [...inputs];
183
+ this.ariaTarget = this;
116
184
  }
117
185
 
118
186
  /**
@@ -139,7 +207,7 @@ class RangeSlider extends SliderMixin(
139
207
  .disabled="${this.disabled}"
140
208
  tabindex="${this.disabled ? -1 : 0}"
141
209
  @keydown="${this.__onKeyDown}"
142
- @input="${this.__onInput}"
210
+ @input="${this.__onStartInput}"
143
211
  @change="${this.__onChange}"
144
212
  />
145
213
  <input
@@ -153,7 +221,7 @@ class RangeSlider extends SliderMixin(
153
221
  .disabled="${this.disabled}"
154
222
  tabindex="${this.disabled ? -1 : 0}"
155
223
  @keydown="${this.__onKeyDown}"
156
- @input="${this.__onInput}"
224
+ @input="${this.__onEndInput}"
157
225
  @change="${this.__onChange}"
158
226
  />
159
227
  `,
@@ -166,7 +234,7 @@ class RangeSlider extends SliderMixin(
166
234
  updated(props) {
167
235
  super.updated(props);
168
236
 
169
- if (props.has('value') || props.has('min') || props.has('max')) {
237
+ if (props.has('value') || props.has('min') || props.has('max') || props.has('step')) {
170
238
  const value = [...this.value];
171
239
  value.forEach((v, idx) => {
172
240
  this.__updateValue(v, idx, value);
@@ -191,6 +259,19 @@ class RangeSlider extends SliderMixin(
191
259
  super.focus(options);
192
260
  }
193
261
 
262
+ /**
263
+ * @protected
264
+ * @override
265
+ */
266
+ blur() {
267
+ if (this._inputElements) {
268
+ const focusedInput = this._inputElements.find((input) => isElementFocused(input));
269
+ if (focusedInput) {
270
+ focusedInput.blur();
271
+ }
272
+ }
273
+ }
274
+
194
275
  /**
195
276
  * Override method inherited from `FocusMixin` to set
196
277
  * state attributes indicating which thumb has focus.
@@ -206,78 +287,33 @@ class RangeSlider extends SliderMixin(
206
287
  this.toggleAttribute('end-focused', isElementFocused(this._inputElements[1]));
207
288
  }
208
289
 
209
- /**
210
- * @param {PointerEvent} event
211
- * @private
212
- */
213
- __focusInput(event) {
214
- const index = this.__getThumbIndex(event);
215
- this._inputElements[index].focus();
216
- }
217
-
218
290
  /** @private */
219
291
  __commitValue() {
220
292
  this.value = [...this.__value];
221
293
  }
222
294
 
223
- /**
224
- * @param {Event} event
225
- * @return {number}
226
- */
227
- __getThumbIndex(event) {
228
- if (event.type === 'input') {
229
- return this._inputElements.indexOf(event.target);
295
+ /** @private */
296
+ __onStartInput(event) {
297
+ // Use second input value as first input max limit
298
+ if (parseFloat(event.target.value) > this.__value[1]) {
299
+ event.target.value = this.__value[1];
230
300
  }
231
301
 
232
- return this.__getClosestThumb(event);
302
+ const value = event.target.value;
303
+ this.__updateValue(value, 0);
304
+ this.__commitValue();
233
305
  }
234
306
 
235
- /**
236
- * @param {PointerEvent} event
237
- * @return {number}
238
- * @private
239
- */
240
- __getClosestThumb(event) {
241
- let closestThumb;
242
-
243
- // If both thumbs are at the start, use the second thumb,
244
- // and if both are at tne end, use the first one instead.
245
- if (this.__value[0] === this.__value[1]) {
246
- const { min, max } = this.__getConstraints();
247
- if (this.__value[0] === min) {
248
- return 1;
249
- }
250
-
251
- if (this.__value[0] === max) {
252
- return 0;
253
- }
254
- }
255
-
256
- const percent = this.__getEventPercent(event);
257
- const value = this.__getValueFromPercent(percent);
258
-
259
- // First thumb position from the "end"
260
- const index = this.__value.findIndex((v) => value - v < 0);
261
-
262
- // Pick the first one
263
- if (index === 0) {
264
- closestThumb = index;
265
- } else if (index === -1) {
266
- // Pick the last one (position is past all the thumbs)
267
- closestThumb = this.__value.length - 1;
268
- } else {
269
- const lastStart = this.__value[index - 1];
270
- const firstEnd = this.__value[index];
271
- // Pick the first one from the "start" unless thumbs are stacked on top of each other
272
- if (Math.abs(lastStart - value) < Math.abs(firstEnd - value)) {
273
- closestThumb = index - 1;
274
- } else {
275
- // Pick the last one from the "end"
276
- closestThumb = index;
277
- }
307
+ /** @private */
308
+ __onEndInput(event) {
309
+ // Use first input value as second input min limit
310
+ if (parseFloat(event.target.value) < this.__value[0]) {
311
+ event.target.value = this.__value[0];
278
312
  }
279
313
 
280
- return closestThumb;
314
+ const value = event.target.value;
315
+ this.__updateValue(value, 1);
316
+ this.__commitValue();
281
317
  }
282
318
 
283
319
  /** @private */
@@ -5,10 +5,11 @@
5
5
  */
6
6
  import type { Constructor } from '@open-wc/dedupe-mixin';
7
7
  import type { DisabledMixinClass } from '@vaadin/a11y-base/src/disabled-mixin.js';
8
+ import type { SlotStylesMixinClass } from '@vaadin/component-base/src/slot-styles-mixin.js';
8
9
 
9
10
  export declare function SliderMixin<T extends Constructor<HTMLElement>>(
10
11
  base: T,
11
- ): Constructor<DisabledMixinClass> & Constructor<SliderMixinClass> & T;
12
+ ): Constructor<DisabledMixinClass> & Constructor<SliderMixinClass> & Constructor<SlotStylesMixinClass> & T;
12
13
 
13
14
  export declare class SliderMixinClass {
14
15
  /**