@zywave/zui-slider 4.4.0-pre.3 → 4.4.0-pre.4

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/lab.html CHANGED
@@ -21,10 +21,6 @@
21
21
  margin-bottom: 20px;
22
22
  }
23
23
 
24
- zui-slider#decimals {
25
- --zui-slider-thumb-size: 3rem;
26
- }
27
-
28
24
  </style>
29
25
  </head>
30
26
 
@@ -43,17 +39,29 @@
43
39
  <h3>disabled zui slider</h3>
44
40
  <zui-slider id="disabled" disabled></zui-slider>
45
41
 
46
- <h3>zui slider negative values</h3>
42
+ <h3>bounded slider with negative values (min=-50, max=50)</h3>
47
43
  <zui-slider id="negative" value="0" min="-50" max="50"></zui-slider>
48
44
 
49
45
  <h3>zui slider no text</h3>
50
46
  <zui-slider id="text" no-text></zui-slider>
51
47
 
52
- <h3>zui slider show min/max</h3>
53
- <zui-slider id="show-min-max" show-min-max></zui-slider>
48
+ <h2>Decimal sliders</h2>
49
+
50
+ <h3>decimal — step=0.5</h3>
51
+ <p>Drag snaps to 0.5 increments. Typing <code>47.01</code> rounds to <code>47.0</code>; typing <code>47.9</code> rounds to <code>48.0</code>.</p>
52
+ <zui-slider id="decimals" step="0.5" value="50"></zui-slider>
53
+
54
+ <h3>decimal — step=0.1, min=0, max=10</h3>
55
+ <zui-slider id="decimals-tenth" step="0.1" value="5" min="0" max="10"></zui-slider>
56
+
57
+ <h3>decimal — step=0.25, min=0, max=5</h3>
58
+ <zui-slider id="decimals-quarter" step="0.25" value="2.5" min="0" max="5"></zui-slider>
59
+
60
+ <h3>decimal — range, step=0.5</h3>
61
+ <zui-slider id="decimals-range" range step="0.5" value-start="25" value-end="75"></zui-slider>
54
62
 
55
- <h3>zui slider stepped + show min/max</h3>
56
- <zui-slider id="stepped-min-max" step="10" show-min-max></zui-slider>
63
+ <h3>decimal range, step=0.1, min=0, max=10</h3>
64
+ <zui-slider id="decimals-range-tenth" range step="0.1" value-start="2" value-end="8" min="0" max="10"></zui-slider>
57
65
 
58
66
  <h3>set zui slider programmatically</h3>
59
67
  <button id="clickme">Click me</button>
@@ -77,12 +85,6 @@
77
85
  <h3>range slider disabled</h3>
78
86
  <zui-slider id="range-disabled" range disabled value-start="30" value-end="70"></zui-slider>
79
87
 
80
- <h3>range slider show min/max</h3>
81
- <zui-slider id="range-show-min-max" range show-min-max></zui-slider>
82
-
83
- <h3>range slider stepped + show min/max</h3>
84
- <zui-slider id="range-stepped-min-max" range step="10" value-start="20" value-end="60" show-min-max></zui-slider>
85
-
86
88
  <h3>set range slider programmatically</h3>
87
89
  <button id="clickme-range">Reset range</button>
88
90
  <zui-slider id="range-programmatic" range value-start="10" value-end="90"></zui-slider>
@@ -113,6 +115,404 @@
113
115
  });
114
116
 
115
117
  </script>
118
+
119
+ <!-- ─── Custom steps — single mode ─────────────────────────────────── -->
120
+
121
+ <h2>Custom steps</h2>
122
+
123
+ <h3>steps — numeric steps (single)</h3>
124
+ <zui-slider id="steps-basic"></zui-slider>
125
+
126
+ <h3>steps — initial value</h3>
127
+ <zui-slider id="steps-initial"></zui-slider>
128
+
129
+ <h3>steps — string labels</h3>
130
+ <zui-slider id="steps-sizes"></zui-slider>
131
+
132
+ <h3>steps - overflow step</h3>
133
+ <zui-slider id="steps-overflow"></zui-slider>
134
+
135
+ <h3>steps — object form <code>{ value, label }</code></h3>
136
+ <zui-slider id="steps-obj-basic"></zui-slider>
137
+
138
+ <h3>steps — object form: financial labels without stepParser</h3>
139
+ <p>Explicit numeric <code>value</code> fields let the slider snap correctly without a <code>stepParser</code>.</p>
140
+ <zui-slider id="steps-obj-financial"></zui-slider>
141
+
142
+ <h3>steps — object form: label omitted (defaults to String(value))</h3>
143
+ <zui-slider id="steps-obj-no-label"></zui-slider>
144
+
145
+ <h3>steps — mixed shorthand and object form</h3>
146
+ <zui-slider id="steps-mixed"></zui-slider>
147
+
148
+ <h3>steps — show-step-labels (numeric steps)</h3>
149
+ <zui-slider steps="0,1,10,50,100,200,1000" show-step-labels></zui-slider>
150
+
151
+ <h3>steps — show-step-labels (object form with labels)</h3>
152
+ <zui-slider id="steps-show-labels-obj" show-step-labels></zui-slider>
153
+
154
+ <h3>steps — show-step-labels (range mode)</h3>
155
+ <zui-slider steps="0,1,10,50,100,200,1000" range show-step-labels></zui-slider>
156
+
157
+ <h3>steps — show-step-labels (JS setup)</h3>
158
+ <zui-slider id="steps-show-labels-js"></zui-slider>
159
+
160
+ <h3>steps — disabled</h3>
161
+ <zui-slider id="steps-disabled" disabled></zui-slider>
162
+
163
+ <h3>steps — read-only</h3>
164
+ <zui-slider id="steps-readonly" readonly></zui-slider>
165
+
166
+ <h3>steps — programmatic value update</h3>
167
+ <button id="steps-programmatic-btn">Pick random step</button>
168
+ <zui-slider id="steps-programmatic"></zui-slider>
169
+
170
+ <!-- ─── stepParser — single mode ──────────────────────────────────── -->
171
+
172
+ <h2>stepParser</h2>
173
+
174
+ <h3>stepParser — multiple aliases for one step</h3>
175
+ <p>Type <code>1k</code>, <code>1,000</code>, or <code>1000</code> — all resolve to the step labeled <code>1K</code>. The displayed value is always the canonical step label, not the typed alias.</p>
176
+ <zui-slider id="steps-parser-numeric"></zui-slider>
177
+
178
+ <h3>stepParser — financial labels ('1M', '1MM', '1,000,000')</h3>
179
+ <p>Type <code>1M</code>, <code>1MM</code>, <code>1,000,000</code>, or <code>1000000</code> in the floating input.</p>
180
+ <zui-slider id="steps-parser-financial"></zui-slider>
181
+
182
+ <h3>stepParser — exact string label match (case-insensitive)</h3>
183
+ <p>Type <code>low</code>, <code>Medium</code>, or <code>HIGH</code>. Unrecognised input is rejected.</p>
184
+ <zui-slider id="steps-parser-exact"></zui-slider>
185
+
186
+ <h3>stepParser — exact label match takes priority over stepParser</h3>
187
+ <p>
188
+ Steps have labels <code>0</code>, <code>1K</code>, <code>1M</code>, <code>1B</code>.
189
+ Typing an exact label (e.g. <code>1M</code>) is accepted directly — <code>stepParser</code>
190
+ is never called. Typing an alias like <code>1mm</code> or <code>500k</code> goes through
191
+ <code>stepParser</code>.
192
+ </p>
193
+ <zui-slider id="steps-parser-priority"></zui-slider>
194
+
195
+ <!-- ─── Custom steps — range mode ─────────────────────────────────── -->
196
+
197
+ <h2>Custom steps — range mode</h2>
198
+
199
+ <h3>steps — range slider</h3>
200
+ <zui-slider id="steps-range" range></zui-slider>
201
+
202
+ <h3>steps — range slider with initial values</h3>
203
+ <zui-slider id="steps-range-initial" range></zui-slider>
204
+
205
+ <h3>steps — range slider with overflow step</h3>
206
+ <zui-slider id="steps-range-overflow" range></zui-slider>
207
+
208
+ <h3>steps — range slider disabled</h3>
209
+ <zui-slider id="steps-range-disabled" range disabled></zui-slider>
210
+
211
+ <h3>steps — range slider read-only</h3>
212
+ <zui-slider id="steps-range-readonly" range readonly></zui-slider>
213
+
214
+ <h3>steps — range slider programmatic update</h3>
215
+ <button id="steps-range-programmatic-btn">Pick random range</button>
216
+ <zui-slider id="steps-range-programmatic" range></zui-slider>
217
+
218
+ <!-- ─── stepParser — range mode ───────────────────────────────────── -->
219
+
220
+ <h3>stepParser — financial range slider ('1M'–'1B+')</h3>
221
+ <p>Type <code>1M</code>, <code>1MM</code>, <code>1,000,000</code>, or <code>10000000000</code> in either floating input.</p>
222
+ <zui-slider id="steps-range-parser" range></zui-slider>
223
+ <p>Last change: <strong id="steps-range-parser-output">—</strong></p>
224
+
225
+ <!-- ─── Dynamic steps update ──────────────────────────────────────── -->
226
+
227
+ <h2>Dynamic steps</h2>
228
+
229
+ <h3>steps — dynamic update at runtime</h3>
230
+ <button id="steps-dynamic-few">3 steps</button>
231
+ <button id="steps-dynamic-many">8 steps</button>
232
+ <button id="steps-dynamic-clear">Clear (fallback to min/max)</button>
233
+ <zui-slider id="steps-dynamic"></zui-slider>
234
+
235
+ <!-- ─── Form integration ───────────────────────────────────────────── -->
236
+
237
+ <h2>Custom steps — form integration</h2>
238
+
239
+ <h3>steps — form integration (single mode)</h3>
240
+ <form id="steps-form">
241
+ <zui-slider id="steps-form-slider" name="tier"></zui-slider>
242
+ <br />
243
+ <button type="submit">Submit</button>
244
+ <button type="reset">Reset</button>
245
+ </form>
246
+ <p>Submitted value: <strong id="steps-form-output">—</strong></p>
247
+
248
+ <h3>steps — form integration (range mode)</h3>
249
+ <form id="steps-range-form">
250
+ <zui-slider id="steps-range-form-slider" range name="budget"></zui-slider>
251
+ <br />
252
+ <button type="submit">Submit</button>
253
+ <button type="reset">Reset</button>
254
+ </form>
255
+ <p>Submitted value: <strong id="steps-range-form-output">—</strong></p>
256
+
257
+ <script>
258
+ // ── Helpers ──────────────────────────────────────────────────────────
259
+ const numericSteps = [1, 10, 50, 100, 200, 1000];
260
+
261
+ function financialParser(input) {
262
+ const cleaned = input.replace(/,/g, "").trim().toUpperCase();
263
+ if (cleaned === "0") return 0;
264
+ const multipliers = { K: 1e3, MM: 1e6, M: 1e6, B: 1e9, T: 1e12 };
265
+ for (const [suffix, factor] of Object.entries(multipliers)) {
266
+ if (cleaned.endsWith(suffix)) {
267
+ const n = parseFloat(cleaned);
268
+ if (!isNaN(n)) return n * factor;
269
+ }
270
+ }
271
+ const n = parseFloat(cleaned);
272
+ return isNaN(n) ? null : n;
273
+ }
274
+
275
+ // ── Single — numeric steps ────────────────────────────────────────────
276
+ document.getElementById("steps-basic").steps = numericSteps;
277
+
278
+ const stepsInitial = document.getElementById("steps-initial");
279
+ stepsInitial.steps = numericSteps;
280
+ stepsInitial.value = "100";
281
+
282
+ document.getElementById("steps-sizes").steps = ["0", "1M", "10M", "50M", "100M", "200M", "1B", "1B+"];
283
+
284
+ document.getElementById("steps-overflow").steps = [0, 100, 250, 500, { value: Infinity, label: "1000+" }];
285
+
286
+ // Object form: { value, label } — label is displayed, value is used for numeric snapping
287
+ document.getElementById("steps-obj-basic").steps = [
288
+ { value: 0, label: "0" },
289
+ { value: 100, label: "100" },
290
+ { value: 250, label: "250" },
291
+ { value: 500, label: "500" },
292
+ { value: 1000, label: "1000+" },
293
+ ];
294
+
295
+ // Object form with explicit numeric values for financial labels — no stepParser required
296
+ document.getElementById("steps-obj-financial").steps = [
297
+ { value: 0, label: "0" },
298
+ { value: 1_000_000, label: "1M" },
299
+ { value: 10_000_000, label: "10M" },
300
+ { value: 50_000_000, label: "50M" },
301
+ { value: 100_000_000, label: "100M"},
302
+ { value: 200_000_000, label: "200M"},
303
+ { value: 1_000_000_000, label: "1B"},
304
+ { value: Infinity, label: "1B+" },
305
+ ];
306
+
307
+ // Object form with label omitted — label defaults to String(value)
308
+ document.getElementById("steps-obj-no-label").steps = [
309
+ { value: 0 },
310
+ { value: 25 },
311
+ { value: 50 },
312
+ { value: 75 },
313
+ { value: 100 },
314
+ ];
315
+
316
+ // Mix of shorthand numbers, shorthand strings, and object form
317
+ document.getElementById("steps-mixed").steps = [
318
+ 0,
319
+ { value: 500_000, label: "500K" },
320
+ { value: 1_000_000, label: "1M" },
321
+ { value: 5_000_000, label: "5M" },
322
+ "custom",
323
+ ];
324
+
325
+ const stepsShowLabelsJs = document.getElementById("steps-show-labels-js");
326
+ stepsShowLabelsJs.steps = [0, 1, 10, 50, 100, 200, 1000];
327
+ stepsShowLabelsJs.showStepLabels = true;
328
+
329
+ document.getElementById("steps-show-labels-obj").steps = [
330
+ { value: 0, label: "0" },
331
+ { value: 1, label: "1M" },
332
+ { value: 10, label: "10M" },
333
+ { value: 50, label: "50M" },
334
+ { value: 100, label: "100M" },
335
+ { value: 200, label: "200M" },
336
+ { value: 1000, label: "1B" },
337
+ { value: 1001, label: "1B+" },
338
+ ];
339
+
340
+ const stepsDisabled = document.getElementById("steps-disabled");
341
+ stepsDisabled.steps = numericSteps;
342
+ stepsDisabled.value = "50";
343
+
344
+ const stepsReadonly = document.getElementById("steps-readonly");
345
+ stepsReadonly.steps = numericSteps;
346
+ stepsReadonly.value = "50";
347
+
348
+ const stepsProgrammatic = document.getElementById("steps-programmatic");
349
+ stepsProgrammatic.steps = numericSteps;
350
+ document.getElementById("steps-programmatic-btn").addEventListener("click", () => {
351
+ stepsProgrammatic.value = String(numericSteps[Math.floor(Math.random() * numericSteps.length)]);
352
+ });
353
+
354
+ // ── Single — stepParser ───────────────────────────────────────────────
355
+ const stepsParserNumeric = document.getElementById("steps-parser-numeric");
356
+ stepsParserNumeric.steps = [
357
+ { value: 0, label: "0" },
358
+ { value: 1_000, label: "1K" },
359
+ { value: 10_000, label: "10K" },
360
+ { value: 100_000, label: "100K" },
361
+ { value: 1_000_000, label: "1M" },
362
+ ];
363
+ stepsParserNumeric.stepParser = (input) => {
364
+ const cleaned = input.replace(/,/g, "").trim().toLowerCase();
365
+ if (cleaned.endsWith("k")) {
366
+ const n = parseFloat(cleaned);
367
+ return isNaN(n) ? null : n * 1e3;
368
+ }
369
+ if (cleaned.endsWith("m")) {
370
+ const n = parseFloat(cleaned);
371
+ return isNaN(n) ? null : n * 1e6;
372
+ }
373
+ const n = parseFloat(cleaned);
374
+ return isNaN(n) ? null : n;
375
+ };
376
+
377
+ const stepsParserFinancial = document.getElementById("steps-parser-financial");
378
+ stepsParserFinancial.steps = [
379
+ { value: 0, label: "0" },
380
+ { value: 1_000_000, label: "1M" },
381
+ { value: 10_000_000, label: "10M" },
382
+ { value: 50_000_000, label: "50M" },
383
+ { value: 100_000_000, label: "100M" },
384
+ { value: 200_000_000, label: "200M" },
385
+ { value: 1_000_000_000, label: "1B" },
386
+ { value: Infinity, label: "1B+" },
387
+ ];
388
+ stepsParserFinancial.stepParser = financialParser;
389
+
390
+ const stepsParserExact = document.getElementById("steps-parser-exact");
391
+ stepsParserExact.steps = ["Low", "Medium", "High"];
392
+ stepsParserExact.stepParser = (input) => {
393
+ const map = { low: "Low", medium: "Medium", high: "High" };
394
+ return map[input.trim().toLowerCase()] ?? null;
395
+ };
396
+
397
+ const stepsParserPriority = document.getElementById("steps-parser-priority");
398
+ stepsParserPriority.steps = [
399
+ { value: 0, label: "0" },
400
+ { value: 1_000, label: "1K" },
401
+ { value: 1_000_000, label: "1M" },
402
+ { value: 1_000_000_000, label: "1B" },
403
+ ];
404
+ // Handles aliases like '1k', '1mm', '500k' but NOT '1K', '1M', '1B' themselves —
405
+ // those match step labels exactly and are accepted before stepParser is ever called.
406
+ stepsParserPriority.stepParser = (input) => {
407
+ const cleaned = input.replace(/,/g, "").trim().toLowerCase();
408
+ const suffixes = { k: 1e3, mm: 1e6, m: 1e6, b: 1e9 };
409
+ for (const [suffix, factor] of Object.entries(suffixes)) {
410
+ if (cleaned.endsWith(suffix)) {
411
+ const n = parseFloat(cleaned);
412
+ if (!isNaN(n)) { return n * factor; }
413
+ }
414
+ }
415
+ const n = parseFloat(cleaned);
416
+ return isNaN(n) ? null : n;
417
+ };
418
+
419
+ // ── Range — numeric steps ─────────────────────────────────────────────
420
+ document.getElementById("steps-range").steps = numericSteps;
421
+
422
+ const stepsRangeInitial = document.getElementById("steps-range-initial");
423
+ stepsRangeInitial.steps = numericSteps;
424
+ stepsRangeInitial.valueStart = "10";
425
+ stepsRangeInitial.valueEnd = "200";
426
+
427
+ document.getElementById("steps-range-overflow").steps = [
428
+ { value: 0, label: "0" },
429
+ { value: 1_000_000, label: "1M" },
430
+ { value: 10_000_000, label: "10M" },
431
+ { value: 50_000_000, label: "50M" },
432
+ { value: 100_000_000, label: "100M" },
433
+ { value: 200_000_000, label: "200M" },
434
+ { value: 1_000_000_000, label: "1B" },
435
+ { value: Infinity, label: "1B+" },
436
+ ];
437
+
438
+ const stepsRangeDisabled = document.getElementById("steps-range-disabled");
439
+ stepsRangeDisabled.steps = numericSteps;
440
+ stepsRangeDisabled.valueStart = "10";
441
+ stepsRangeDisabled.valueEnd = "200";
442
+
443
+ const stepsRangeReadonly = document.getElementById("steps-range-readonly");
444
+ stepsRangeReadonly.steps = numericSteps;
445
+ stepsRangeReadonly.valueStart = "10";
446
+ stepsRangeReadonly.valueEnd = "200";
447
+
448
+ const stepsRangeProgrammatic = document.getElementById("steps-range-programmatic");
449
+ stepsRangeProgrammatic.steps = numericSteps;
450
+ document.getElementById("steps-range-programmatic-btn").addEventListener("click", () => {
451
+ const a = Math.floor(Math.random() * (numericSteps.length - 1));
452
+ const b = Math.floor(Math.random() * (numericSteps.length - 1 - a)) + a + 1;
453
+ stepsRangeProgrammatic.valueStart = String(numericSteps[a]);
454
+ stepsRangeProgrammatic.valueEnd = String(numericSteps[b]);
455
+ });
456
+
457
+ // ── Range — stepParser ────────────────────────────────────────────────
458
+ const stepsRangeParser = document.getElementById("steps-range-parser");
459
+ stepsRangeParser.steps = [
460
+ { value: 0, label: "0" },
461
+ { value: 1_000_000, label: "1M" },
462
+ { value: 10_000_000, label: "10M" },
463
+ { value: 50_000_000, label: "50M" },
464
+ { value: 100_000_000, label: "100M" },
465
+ { value: 200_000_000, label: "200M" },
466
+ { value: 1_000_000_000, label: "1B" },
467
+ { value: Infinity, label: "1B+" },
468
+ ];
469
+ stepsRangeParser.valueStart = "1M";
470
+ stepsRangeParser.valueEnd = "100M";
471
+ stepsRangeParser.stepParser = financialParser;
472
+ stepsRangeParser.addEventListener("change", (e) => {
473
+ const { valueStart, valueEnd } = e.detail;
474
+ document.getElementById("steps-range-parser-output").textContent =
475
+ `valueStart=${valueStart}, valueEnd=${valueEnd}`;
476
+ });
477
+
478
+ // ── Dynamic steps ─────────────────────────────────────────────────────
479
+ const stepsDynamic = document.getElementById("steps-dynamic");
480
+ stepsDynamic.steps = [10, 50, 100];
481
+ document.getElementById("steps-dynamic-few").addEventListener("click", () => {
482
+ stepsDynamic.steps = [10, 50, 100];
483
+ });
484
+ document.getElementById("steps-dynamic-many").addEventListener("click", () => {
485
+ stepsDynamic.steps = [0, 10, 25, 50, 75, 100, 200, 500];
486
+ });
487
+ document.getElementById("steps-dynamic-clear").addEventListener("click", () => {
488
+ stepsDynamic.steps = [];
489
+ });
490
+
491
+ // ── Form integration ──────────────────────────────────────────────────
492
+ document.getElementById("steps-form-slider").steps = numericSteps;
493
+ document.getElementById("steps-form").addEventListener("submit", (e) => {
494
+ e.preventDefault();
495
+ document.getElementById("steps-form-output").textContent = new FormData(e.target).get("tier");
496
+ });
497
+
498
+ const stepsRangeFormSlider = document.getElementById("steps-range-form-slider");
499
+ stepsRangeFormSlider.steps = [
500
+ { value: 0, label: "0" },
501
+ { value: 1_000_000, label: "1M" },
502
+ { value: 10_000_000, label: "10M" },
503
+ { value: 50_000_000, label: "50M" },
504
+ { value: 100_000_000, label: "100M" },
505
+ { value: 200_000_000, label: "200M" },
506
+ { value: 1_000_000_000, label: "1B" },
507
+ { value: Infinity, label: "1B+" },
508
+ ];
509
+ stepsRangeFormSlider.valueStart = "1M";
510
+ stepsRangeFormSlider.valueEnd = "100M";
511
+ document.getElementById("steps-range-form").addEventListener("submit", (e) => {
512
+ e.preventDefault();
513
+ document.getElementById("steps-range-form-output").textContent = new FormData(e.target).get("budget");
514
+ });
515
+ </script>
116
516
  </body>
117
517
 
118
518
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zywave/zui-slider",
3
- "version": "4.4.0-pre.3",
3
+ "version": "4.4.0-pre.4",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "license": "UNLICENSED",
@@ -27,5 +27,5 @@
27
27
  "devDependencies": {
28
28
  "@zywave/zui-input": "^4.2.3"
29
29
  },
30
- "gitHead": "a50a61d9903db3e0a032d894708c2f3a9c5ffb39"
30
+ "gitHead": "951dae4a386aa23d0148f7fefc096a3f97a697b0"
31
31
  }
@@ -1,3 +1,3 @@
1
1
  import { css } from 'lit';
2
2
 
3
- export const style = css`:host .thumb-input input[type=number]{display:inline-block;width:100%;min-height:2.625rem;vertical-align:middle;padding:0 .625rem;background-color:#fff;border:.0625rem solid var(--zui-gray-200);border-radius:.25rem;font:inherit;color:inherit;transition:border 100ms ease-in-out,box-shadow 100ms ease-in-out;box-sizing:border-box;appearance:textfield}:host .thumb-input input[type=number]::-webkit-input-placeholder{color:var(--zui-gray-200)}:host .thumb-input input[type=number]::-moz-placeholder{opacity:1;color:var(--zui-gray-200)}:host .thumb-input input[type=number]:-moz-placeholder{opacity:1;color:var(--zui-gray-200)}:host .thumb-input input[type=number]:-ms-input-placeholder{color:var(--zui-gray-200)}:host .thumb-input input[type=number]:not(output):-moz-ui-invalid{box-shadow:none}:host .thumb-input input[type=number]:hover{border-color:var(--zui-gray-400)}:host .thumb-input input[type=number]:focus{border-color:var(--zui-blue-400);box-shadow:0 0 0 .0625rem var(--zui-blue-400);outline:none}:host .thumb-input input[disabled][type=number]{background-color:var(--zui-gray-100);cursor:not-allowed;color:var(--zui-gray-300)}:host .thumb-input input[disabled][type=number]:hover{border:.0625rem solid var(--zui-gray-200)}:host .thumb-input input[readonly][type=number]{outline:none}:host{--zui-slider-thumb-size: 0.875rem;--zui-slider-input-width: 8ch;position:relative;display:block}:host .single-wrapper,:host .range-wrapper{position:relative;display:flex;height:var(--zui-slider-thumb-size);align-items:center;margin:0 calc(var(--zui-slider-thumb-size)*-1)}:host input[type=range]{width:100%;height:var(--zui-slider-thumb-size);margin:0;background:rgba(0,0,0,0);outline:none;cursor:grab;-webkit-appearance:none;-moz-appearance:none;appearance:none}:host input[type=range]::-webkit-slider-runnable-track{height:.25rem;background:var(--zui-slider-track-bg)}:host input[type=range]::-moz-range-track{height:.25rem;background:var(--zui-slider-track-bg)}:host input[type=range]::-webkit-slider-thumb{width:calc(var(--zui-slider-thumb-size)*3);height:calc(var(--zui-slider-thumb-size)*3);background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));border-radius:50%;box-shadow:none;cursor:grab;transition:background 100ms ease-out,box-shadow 100ms ease-out}:host input[type=range]::-webkit-slider-thumb:hover{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]::-moz-range-thumb{width:calc(var(--zui-slider-thumb-size)*3);height:calc(var(--zui-slider-thumb-size)*3);background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));border-radius:50%;box-shadow:none;cursor:grab;transition:background 100ms ease-out,box-shadow 100ms ease-out}:host input[type=range]::-moz-range-thumb:hover{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]::-webkit-slider-thumb{transform:translateY(calc(-50% + 0.125rem));-webkit-appearance:none;appearance:none}:host input[type=range]::-moz-range-thumb{border:0}:host input[type=range]:hover::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue-400) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2))}:host input[type=range]:hover::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue-400) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2))}:host input[type=range]:focus::-webkit-slider-thumb{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]:focus::-moz-range-thumb{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]:active::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue-600) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16);cursor:grabbing}:host input[type=range]:active::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue-600) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16);cursor:grabbing}:host .thumb-input{position:absolute;bottom:calc(var(--zui-slider-thumb-size)*2 + 0.25rem);opacity:0;z-index:10;transform:translateX(-50%);transition:opacity 100ms ease-out;pointer-events:none}:host .thumb-input--visible,:host .thumb-input:focus-within{opacity:1;pointer-events:all}:host .thumb-input input[type=number]{width:var(--zui-slider-input-width);text-align:center}:host .thumb-input input[type=number]::-webkit-inner-spin-button,:host .thumb-input input[type=number]::-webkit-outer-spin-button{appearance:none}:host .step-dots{position:absolute;top:50%;left:0;width:100%;height:0;pointer-events:none}:host .step-dots .step-dot{position:absolute;width:.375rem;height:.375rem;background-color:var(--zui-blue);border-radius:50%;transform:translate(-50%, -50%)}:host .step-dots .step-dot:first-child,:host .step-dots .step-dot:last-child{width:.125rem;height:1rem;border-radius:.125rem}:host .range-wrapper{cursor:grab}:host .range-wrapper:active{cursor:grabbing}:host .range-wrapper input[type=range]{position:absolute;pointer-events:none}:host .range-wrapper input[type=range]::-webkit-slider-thumb{pointer-events:all}:host .range-wrapper input[type=range]::-moz-range-thumb{pointer-events:all}:host .range-wrapper input[type=range]::-moz-range-progress{background:rgba(0,0,0,0)}:host .min-max-labels{display:flex;justify-content:space-between}:host .min-max-labels .min-max-label{font-weight:600;color:var(--zui-gray-600)}:host([range]) .min-max-labels{margin-top:.3125rem}:host([readonly]) input[type=range]:disabled{cursor:default}:host([readonly]) input[type=range]:disabled::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:default}:host([readonly]) input[type=range]:disabled::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:default}:host([disabled]){cursor:not-allowed}:host([disabled]) input[type=range]:disabled{cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-gray) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-webkit-slider-thumb:hover{box-shadow:none}:host([disabled]) input[type=range]:disabled::-moz-range-thumb{background:radial-gradient(circle, var(--zui-gray) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-moz-range-thumb:hover{box-shadow:none}:host([disabled]) .step-dot{background-color:var(--zui-gray)}:host([disabled]) .range-wrapper{cursor:not-allowed}`;
3
+ export const style = css`:host .thumb-input input{display:inline-block;width:100%;min-height:2.625rem;vertical-align:middle;padding:0 .625rem;background-color:#fff;border:.0625rem solid var(--zui-gray-200);border-radius:.25rem;font:inherit;color:inherit;transition:border 100ms ease-in-out,box-shadow 100ms ease-in-out;box-sizing:border-box;appearance:textfield}:host .thumb-input input::-webkit-input-placeholder{color:var(--zui-gray-200)}:host .thumb-input input::-moz-placeholder{opacity:1;color:var(--zui-gray-200)}:host .thumb-input input:-moz-placeholder{opacity:1;color:var(--zui-gray-200)}:host .thumb-input input:-ms-input-placeholder{color:var(--zui-gray-200)}:host .thumb-input input:not(output):-moz-ui-invalid{box-shadow:none}:host .thumb-input input:hover{border-color:var(--zui-gray-400)}:host .thumb-input input:focus{border-color:var(--zui-blue-400);box-shadow:0 0 0 .0625rem var(--zui-blue-400);outline:none}:host .thumb-input input[disabled]{background-color:var(--zui-gray-100);cursor:not-allowed;color:var(--zui-gray-300)}:host .thumb-input input[disabled]:hover{border:.0625rem solid var(--zui-gray-200)}:host .thumb-input input[readonly]{outline:none}:host{--zui-slider-thumb-size: 0.875rem;--zui-slider-input-width: 8ch;position:relative;display:block}:host .single-wrapper,:host .range-wrapper{position:relative;display:flex;height:var(--zui-slider-thumb-size);align-items:center;margin:0 calc(var(--zui-slider-thumb-size)*-1)}:host input[type=range]{width:100%;height:var(--zui-slider-thumb-size);margin:0;background:rgba(0,0,0,0);outline:none;cursor:grab;-webkit-appearance:none;appearance:none}:host input[type=range]::-webkit-slider-runnable-track{height:.25rem;background:var(--zui-slider-track-bg)}:host input[type=range]::-moz-range-track{height:.25rem;background:var(--zui-slider-track-bg)}:host input[type=range]::-webkit-slider-thumb{width:calc(var(--zui-slider-thumb-size)*3);height:calc(var(--zui-slider-thumb-size)*3);background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));border-radius:50%;box-shadow:none;cursor:grab;transition:background 100ms ease-out,box-shadow 100ms ease-out}:host input[type=range]::-webkit-slider-thumb:hover{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]::-moz-range-thumb{width:calc(var(--zui-slider-thumb-size)*3);height:calc(var(--zui-slider-thumb-size)*3);background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));border-radius:50%;box-shadow:none;cursor:grab;transition:background 100ms ease-out,box-shadow 100ms ease-out}:host input[type=range]::-moz-range-thumb:hover{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]::-webkit-slider-thumb{transform:translateY(calc(-50% + 0.125rem));-webkit-appearance:none;appearance:none}:host input[type=range]::-moz-range-thumb{border:0}:host input[type=range]:hover::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue-400) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2))}:host input[type=range]:hover::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue-400) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2))}:host input[type=range]:focus::-webkit-slider-thumb{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]:focus::-moz-range-thumb{box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16)}:host input[type=range]:active::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue-600) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16);cursor:grabbing}:host input[type=range]:active::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue-600) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));box-shadow:inset 0 0 0 var(--zui-slider-thumb-size) rgba(120,120,140,.16);cursor:grabbing}:host .thumb-input{position:absolute;bottom:calc(var(--zui-slider-thumb-size)*2 + 0.25rem);opacity:0;z-index:10;transform:translateX(-50%);transition:opacity 100ms ease-out;pointer-events:none}:host .thumb-input--visible,:host .thumb-input:focus-within{opacity:1;pointer-events:auto}:host .thumb-input input{width:var(--zui-slider-input-width);text-align:center}:host .thumb-input input::-webkit-inner-spin-button,:host .thumb-input input::-webkit-outer-spin-button{appearance:none}:host .step-dots{position:absolute;top:50%;left:0;width:100%;height:0;pointer-events:none}:host .step-dots .step-dot{position:absolute;width:.375rem;height:.375rem;background-color:var(--zui-blue);border-radius:50%;transform:translate(-50%, -50%)}:host .step-dots .step-dot:first-child,:host .step-dots .step-dot:last-child,:host .step-dots .step-dot--last{width:.125rem;height:1rem;border-radius:.125rem}:host .step-dots .step-dot-label{position:absolute;top:.375rem;font-weight:600;white-space:nowrap;color:var(--zui-gray-600);transform:translateX(-50%);user-select:none}:host .step-dots .step-dot-label:nth-child(2){transform:none}:host .step-dots .step-dot-label:last-child{transform:translateX(-100%)}:host .range-wrapper{cursor:grab}:host .range-wrapper:active{cursor:grabbing}:host .range-wrapper input[type=range]{position:absolute;pointer-events:none}:host .range-wrapper input[type=range]::-webkit-slider-thumb{pointer-events:all}:host .range-wrapper input[type=range]::-moz-range-thumb{pointer-events:all}:host .range-wrapper input[type=range]::-moz-range-progress{background:rgba(0,0,0,0)}:host .min-max-labels{display:flex;justify-content:space-between;user-select:none}:host .min-max-labels .min-max-label{font-weight:600;color:var(--zui-gray-600)}:host([range]) .min-max-labels{margin-top:.3125rem}:host([readonly]) input[type=range]:disabled{cursor:default}:host([readonly]) input[type=range]:disabled::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:default}:host([readonly]) input[type=range]:disabled::-moz-range-thumb{background:radial-gradient(circle, var(--zui-blue) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:default}:host([disabled]){cursor:not-allowed}:host([disabled]) input[type=range]:disabled{cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-webkit-slider-thumb{background:radial-gradient(circle, var(--zui-gray) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-webkit-slider-thumb:hover{box-shadow:none}:host([disabled]) input[type=range]:disabled::-moz-range-thumb{background:radial-gradient(circle, var(--zui-gray) calc(var(--zui-slider-thumb-size) / 2 - 0.0625rem), transparent calc(var(--zui-slider-thumb-size) / 2));cursor:not-allowed}:host([disabled]) input[type=range]:disabled::-moz-range-thumb:hover{box-shadow:none}:host([disabled]) .step-dot{background-color:var(--zui-gray)}:host([disabled]) .range-wrapper{cursor:not-allowed}`;
@@ -36,7 +36,7 @@
36
36
  position: relative;
37
37
  display: block;
38
38
 
39
- // Single mode
39
+ // Shared: wrapper layout
40
40
 
41
41
  .single-wrapper,
42
42
  .range-wrapper {
@@ -57,7 +57,6 @@
57
57
  outline: none;
58
58
  cursor: grab;
59
59
  -webkit-appearance: none;
60
- -moz-appearance: none;
61
60
  appearance: none;
62
61
 
63
62
  @include on-track {
@@ -65,9 +64,11 @@
65
64
  background: var(--zui-slider-track-bg);
66
65
  }
67
66
 
67
+ $thumb-hit-size: calc(var(--zui-slider-thumb-size) * 3);
68
+
68
69
  @include on-thumb {
69
- width: calc(var(--zui-slider-thumb-size) * 3);
70
- height: calc(var(--zui-slider-thumb-size) * 3);
70
+ width: $thumb-hit-size;
71
+ height: $thumb-hit-size;
71
72
  @include thumb-bg(var(--zui-blue));
72
73
  border-radius: 50%;
73
74
  box-shadow: none;
@@ -124,10 +125,10 @@
124
125
  &--visible,
125
126
  &:focus-within {
126
127
  opacity: 1;
127
- pointer-events: all;
128
+ pointer-events: auto;
128
129
  }
129
130
 
130
- input[type='number'] {
131
+ input {
131
132
  @extend %input-base;
132
133
  width: var(--zui-slider-input-width);
133
134
  text-align: center;
@@ -157,13 +158,36 @@
157
158
  border-radius: 50%;
158
159
  transform: translate(-50%, -50%);
159
160
 
161
+ // &--last covers the case where showStepLabels is true: a label becomes the last
162
+ // child, so :last-child alone no longer targets the final dot.
160
163
  &:first-child,
161
- &:last-child {
164
+ &:last-child,
165
+ &--last {
162
166
  width: rem(2);
163
167
  height: rem(16);
164
168
  border-radius: rem(2);
165
169
  }
166
170
  }
171
+
172
+ .step-dot-label {
173
+ position: absolute;
174
+ top: rem(6);
175
+ font-weight: 600;
176
+ white-space: nowrap;
177
+ color: var(--zui-gray-600);
178
+ transform: translateX(-50%);
179
+ user-select: none;
180
+
181
+ // When labels are rendered, the first label is the 2nd child (after the first dot)
182
+ // and the last label is always the last child — align them to the track edges.
183
+ &:nth-child(2) {
184
+ transform: none;
185
+ }
186
+
187
+ &:last-child {
188
+ transform: translateX(-100%);
189
+ }
190
+ }
167
191
  }
168
192
 
169
193
  // Range mode
@@ -194,6 +218,7 @@
194
218
  .min-max-labels {
195
219
  display: flex;
196
220
  justify-content: space-between;
221
+ user-select: none;
197
222
 
198
223
  .min-max-label {
199
224
  font-weight: 600;