@zywave/zui-slider 4.4.0-pre.4 → 4.4.0-pre.5
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/dist/custom-elements.json +5 -40
- package/dist/zui-slider-css.js +1 -1
- package/dist/zui-slider-css.js.map +1 -1
- package/dist/zui-slider.js +28 -56
- package/dist/zui-slider.js.map +1 -1
- package/docs/demo.html +1 -1
- package/lab.html +5 -5
- package/package.json +2 -2
- package/src/zui-slider-css.js +1 -1
- package/src/zui-slider.scss +2 -0
- package/src/zui-slider.ts +22 -53
- package/test/zui-slider.test.ts +175 -237
package/src/zui-slider.ts
CHANGED
|
@@ -70,7 +70,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
70
70
|
visible: boolean;
|
|
71
71
|
focused: boolean;
|
|
72
72
|
timer?: ReturnType<typeof setTimeout>;
|
|
73
|
-
debounceTimer?: ReturnType<typeof setTimeout>;
|
|
74
73
|
}
|
|
75
74
|
>([
|
|
76
75
|
['thumb', { visible: false, focused: false }],
|
|
@@ -78,12 +77,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
78
77
|
['endThumb', { visible: false, focused: false }],
|
|
79
78
|
]);
|
|
80
79
|
|
|
81
|
-
//
|
|
82
|
-
#onThumbFloatingInput = this.#onFloatingInput('thumb', (v) => (this.value = v));
|
|
83
|
-
#onStartThumbFloatingInput = this.#onFloatingInput('startThumb', (v) => (this.valueStart = v));
|
|
84
|
-
#onEndThumbFloatingInput = this.#onFloatingInput('endThumb', (v) => (this.valueEnd = v));
|
|
85
|
-
|
|
86
|
-
// Cached floating input change handlers; flush debounce and dispatch immediately on commit (Enter/blur)
|
|
80
|
+
// Cached floating input change handlers; commit value on Enter or blur
|
|
87
81
|
#onThumbFloatingChange = this.#makeFloatingChange(
|
|
88
82
|
'thumb',
|
|
89
83
|
(v) => (this.value = v),
|
|
@@ -100,6 +94,16 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
100
94
|
() => this.#onRangeChange()
|
|
101
95
|
);
|
|
102
96
|
|
|
97
|
+
// Cached keydown handler: Enter commits the floating input value in both number and text modes.
|
|
98
|
+
#onFloatingInputKeydown = (e: KeyboardEvent) => {
|
|
99
|
+
if (e.key === 'Enter') {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
const input = e.target as HTMLInputElement;
|
|
102
|
+
input.dispatchEvent(new Event('change'));
|
|
103
|
+
input.blur();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
103
107
|
// Cached range drag input handlers
|
|
104
108
|
#onRangeStartInput = this.#onRangeInput('start');
|
|
105
109
|
#onRangeEndInput = this.#onRangeInput('end');
|
|
@@ -243,9 +247,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
243
247
|
#clearAllThumbInputState() {
|
|
244
248
|
for (const entry of this.#thumbInputState.values()) {
|
|
245
249
|
clearTimeout(entry.timer);
|
|
246
|
-
clearTimeout(entry.debounceTimer);
|
|
247
250
|
entry.timer = undefined;
|
|
248
|
-
entry.debounceTimer = undefined;
|
|
249
251
|
entry.visible = false;
|
|
250
252
|
entry.focused = false;
|
|
251
253
|
}
|
|
@@ -524,6 +526,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
524
526
|
#renderSingle() {
|
|
525
527
|
const progress = this.progress;
|
|
526
528
|
const { nativeMin, nativeMax, nativeStep } = this.#nativeRangeAttrs;
|
|
529
|
+
// live() required: direct DOM writes during drag don't trigger a state change, so Lit won't re-sync without it.
|
|
527
530
|
const nativeValue = this.#stepsMode ? String(Math.max(0, this.#stepsIndexOf(this.#value))) : this.#value;
|
|
528
531
|
return html`
|
|
529
532
|
<div class="single-wrapper">
|
|
@@ -534,7 +537,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
534
537
|
.min="${nativeMin}"
|
|
535
538
|
.max="${nativeMax}"
|
|
536
539
|
.step="${nativeStep}"
|
|
537
|
-
.value="${nativeValue}"
|
|
540
|
+
.value="${live(nativeValue)}"
|
|
538
541
|
?disabled="${this.disabled || this.readOnly}"
|
|
539
542
|
@input="${this.#onInput}"
|
|
540
543
|
@change="${this.#onChange}"
|
|
@@ -545,7 +548,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
545
548
|
/>
|
|
546
549
|
${this.#renderFloatingInput(
|
|
547
550
|
this.#value,
|
|
548
|
-
this.#onThumbFloatingInput,
|
|
549
551
|
this.#onThumbFloatingChange,
|
|
550
552
|
'thumb',
|
|
551
553
|
this.#isVisible('thumb'),
|
|
@@ -564,7 +566,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
564
566
|
${this.#renderRangeInput('start', this.#rangeTrackBackground(progressStart, progressEnd))}
|
|
565
567
|
${this.#renderFloatingInput(
|
|
566
568
|
this.#valueStart,
|
|
567
|
-
this.#onStartThumbFloatingInput,
|
|
568
569
|
this.#onStartThumbFloatingChange,
|
|
569
570
|
'startThumb',
|
|
570
571
|
this.#isVisible('startThumb'),
|
|
@@ -573,7 +574,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
573
574
|
${this.#renderRangeInput('end')}
|
|
574
575
|
${this.#renderFloatingInput(
|
|
575
576
|
this.#valueEnd,
|
|
576
|
-
this.#onEndThumbFloatingInput,
|
|
577
577
|
this.#onEndThumbFloatingChange,
|
|
578
578
|
'endThumb',
|
|
579
579
|
this.#isVisible('endThumb'),
|
|
@@ -615,7 +615,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
615
615
|
|
|
616
616
|
#renderFloatingInput(
|
|
617
617
|
val: string,
|
|
618
|
-
onInput: (e: Event) => void,
|
|
619
618
|
onFloatingChange: (e: Event) => void,
|
|
620
619
|
flag: ThumbFlag,
|
|
621
620
|
visible: boolean,
|
|
@@ -623,7 +622,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
623
622
|
) {
|
|
624
623
|
const h = this.#h[flag];
|
|
625
624
|
// type="text" in steps mode to allow label and stepParser input.
|
|
626
|
-
// live() required:
|
|
625
|
+
// live() required: commits that snap/clamp to the current value skip reactive updates, so Lit won't re-sync without it.
|
|
627
626
|
const ariaLabel =
|
|
628
627
|
flag === 'startThumb' ? 'Range start value' : flag === 'endThumb' ? 'Range end value' : 'Slider value';
|
|
629
628
|
return html`
|
|
@@ -642,7 +641,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
642
641
|
.step="${this.#stepsMode ? '' : this.step > 0 ? String(this.step) : '1'}"
|
|
643
642
|
?disabled="${this.disabled}"
|
|
644
643
|
?readonly="${this.readOnly}"
|
|
645
|
-
@
|
|
644
|
+
@keydown="${this.#onFloatingInputKeydown}"
|
|
646
645
|
@change="${onFloatingChange}"
|
|
647
646
|
@focus="${h.focus}"
|
|
648
647
|
@blur="${h.blur}"
|
|
@@ -756,58 +755,28 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
756
755
|
return;
|
|
757
756
|
}
|
|
758
757
|
const input = e.target as HTMLInputElement;
|
|
759
|
-
const entry = this.#thumbInputState.get(flag)!;
|
|
760
|
-
clearTimeout(entry.debounceTimer);
|
|
761
|
-
entry.debounceTimer = undefined;
|
|
762
758
|
if (input.value === '') {
|
|
763
759
|
input.value = this.#currentValueForFlag(flag);
|
|
764
760
|
return;
|
|
765
761
|
}
|
|
762
|
+
const before = this.#currentValueForFlag(flag);
|
|
766
763
|
if (this.#stepsMode) {
|
|
767
764
|
const resolved = this.#resolveFloatingInput(input.value);
|
|
768
765
|
if (resolved !== null) {
|
|
769
766
|
setter(resolved);
|
|
770
767
|
this.requestUpdate();
|
|
771
|
-
|
|
768
|
+
if (this.#currentValueForFlag(flag) !== before) {
|
|
769
|
+
dispatch();
|
|
770
|
+
}
|
|
772
771
|
} else {
|
|
773
772
|
input.value = this.#currentValueForFlag(flag);
|
|
774
773
|
}
|
|
775
774
|
} else {
|
|
776
775
|
setter(this.#processFloatingValue(parseFloat(input.value)));
|
|
777
776
|
this.requestUpdate();
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
#onFloatingInput(flag: ThumbFlag, setter: (val: string) => void) {
|
|
784
|
-
return (e: Event) => {
|
|
785
|
-
if (this.readOnly) {
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
const input = e.target as HTMLInputElement;
|
|
789
|
-
const entry = this.#thumbInputState.get(flag)!;
|
|
790
|
-
clearTimeout(entry.debounceTimer);
|
|
791
|
-
entry.debounceTimer = undefined;
|
|
792
|
-
if (input.value === '') {
|
|
793
|
-
return;
|
|
794
|
-
}
|
|
795
|
-
if (this.#stepsMode) {
|
|
796
|
-
// Capture raw string at event time so the debounce closure reads the right value
|
|
797
|
-
const raw = input.value;
|
|
798
|
-
entry.debounceTimer = setTimeout(() => {
|
|
799
|
-
const resolved = this.#resolveFloatingInput(raw);
|
|
800
|
-
if (resolved !== null) {
|
|
801
|
-
setter(resolved);
|
|
802
|
-
this.requestUpdate();
|
|
803
|
-
}
|
|
804
|
-
}, 500);
|
|
805
|
-
} else {
|
|
806
|
-
const raw = parseFloat(input.value);
|
|
807
|
-
entry.debounceTimer = setTimeout(() => {
|
|
808
|
-
setter(this.#processFloatingValue(raw));
|
|
809
|
-
this.requestUpdate();
|
|
810
|
-
}, 500);
|
|
777
|
+
if (this.#currentValueForFlag(flag) !== before) {
|
|
778
|
+
dispatch();
|
|
779
|
+
}
|
|
811
780
|
}
|
|
812
781
|
};
|
|
813
782
|
}
|