@zywave/zui-slider 4.4.0-pre.5 → 4.4.0-pre.6
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 +21 -3
- package/dist/zui-slider.js +75 -37
- package/dist/zui-slider.js.map +1 -1
- package/package.json +2 -2
- package/src/zui-slider.ts +78 -37
- package/test/zui-slider.test.ts +239 -97
package/src/zui-slider.ts
CHANGED
|
@@ -69,15 +69,15 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
69
69
|
{
|
|
70
70
|
visible: boolean;
|
|
71
71
|
focused: boolean;
|
|
72
|
+
committed: boolean;
|
|
72
73
|
timer?: ReturnType<typeof setTimeout>;
|
|
73
74
|
}
|
|
74
75
|
>([
|
|
75
|
-
['thumb', { visible: false, focused: false }],
|
|
76
|
-
['startThumb', { visible: false, focused: false }],
|
|
77
|
-
['endThumb', { visible: false, focused: false }],
|
|
76
|
+
['thumb', { visible: false, focused: false, committed: false }],
|
|
77
|
+
['startThumb', { visible: false, focused: false, committed: false }],
|
|
78
|
+
['endThumb', { visible: false, focused: false, committed: false }],
|
|
78
79
|
]);
|
|
79
80
|
|
|
80
|
-
// Cached floating input change handlers; commit value on Enter or blur
|
|
81
81
|
#onThumbFloatingChange = this.#makeFloatingChange(
|
|
82
82
|
'thumb',
|
|
83
83
|
(v) => (this.value = v),
|
|
@@ -100,34 +100,21 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
100
100
|
e.preventDefault();
|
|
101
101
|
const input = e.target as HTMLInputElement;
|
|
102
102
|
input.dispatchEvent(new Event('change'));
|
|
103
|
-
input.blur();
|
|
104
103
|
}
|
|
105
104
|
};
|
|
106
105
|
|
|
107
|
-
// Cached range drag input handlers
|
|
108
106
|
#onRangeStartInput = this.#onRangeInput('start');
|
|
109
107
|
#onRangeEndInput = this.#onRangeInput('end');
|
|
108
|
+
#stopClickPropagation = (e: Event) => e.stopPropagation();
|
|
110
109
|
|
|
111
110
|
// Cached pointer/focus handlers per thumb; prevents new closures on every render
|
|
112
|
-
#h: Record<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
startThumb: {
|
|
120
|
-
show: () => this.#showThumbInput('startThumb'),
|
|
121
|
-
hide: () => this.#scheduleHideThumbInput('startThumb'),
|
|
122
|
-
focus: () => this.#focusFloatingInput('startThumb'),
|
|
123
|
-
blur: () => this.#blurFloatingInput('startThumb'),
|
|
124
|
-
},
|
|
125
|
-
endThumb: {
|
|
126
|
-
show: () => this.#showThumbInput('endThumb'),
|
|
127
|
-
hide: () => this.#scheduleHideThumbInput('endThumb'),
|
|
128
|
-
focus: () => this.#focusFloatingInput('endThumb'),
|
|
129
|
-
blur: () => this.#blurFloatingInput('endThumb'),
|
|
130
|
-
},
|
|
111
|
+
#h: Record<
|
|
112
|
+
ThumbFlag,
|
|
113
|
+
{ show: () => void; hide: () => void; focus: () => void; input: () => void; blurCommit: (e: FocusEvent) => void }
|
|
114
|
+
> = {
|
|
115
|
+
thumb: this.#makeThumbHandlers('thumb'),
|
|
116
|
+
startThumb: this.#makeThumbHandlers('startThumb'),
|
|
117
|
+
endThumb: this.#makeThumbHandlers('endThumb'),
|
|
131
118
|
};
|
|
132
119
|
|
|
133
120
|
static get styles() {
|
|
@@ -250,6 +237,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
250
237
|
entry.timer = undefined;
|
|
251
238
|
entry.visible = false;
|
|
252
239
|
entry.focused = false;
|
|
240
|
+
entry.committed = false;
|
|
253
241
|
}
|
|
254
242
|
}
|
|
255
243
|
|
|
@@ -603,6 +591,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
603
591
|
.step="${nativeStep}"
|
|
604
592
|
.value="${live(nativeValue)}"
|
|
605
593
|
?disabled="${this.disabled || this.readOnly}"
|
|
594
|
+
@click="${this.#stopClickPropagation}"
|
|
606
595
|
@input="${onInput}"
|
|
607
596
|
@change="${this.#onRangeChange}"
|
|
608
597
|
@pointerenter="${h.show}"
|
|
@@ -629,6 +618,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
629
618
|
<div
|
|
630
619
|
class=${classMap({ 'thumb-input': true, 'thumb-input--visible': visible })}
|
|
631
620
|
style=${styleMap({ left: ZuiSlider.#thumbPositionCSS(progress) })}
|
|
621
|
+
@click="${this.#stopClickPropagation}"
|
|
632
622
|
@pointerenter="${h.show}"
|
|
633
623
|
@pointerleave="${h.hide}"
|
|
634
624
|
>
|
|
@@ -642,9 +632,10 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
642
632
|
?disabled="${this.disabled}"
|
|
643
633
|
?readonly="${this.readOnly}"
|
|
644
634
|
@keydown="${this.#onFloatingInputKeydown}"
|
|
635
|
+
@input="${h.input}"
|
|
645
636
|
@change="${onFloatingChange}"
|
|
646
637
|
@focus="${h.focus}"
|
|
647
|
-
@blur="${h.
|
|
638
|
+
@blur="${h.blurCommit}"
|
|
648
639
|
/>
|
|
649
640
|
</div>
|
|
650
641
|
`;
|
|
@@ -701,7 +692,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
701
692
|
const normalized = this.#normalizedSteps;
|
|
702
693
|
const minLabel = this.#stepsMode ? normalized[0]?.label ?? '' : String(this.min);
|
|
703
694
|
const maxLabel = this.#stepsMode ? normalized[normalized.length - 1]?.label ?? '' : String(this.max);
|
|
704
|
-
const hidden = this.showStepLabels
|
|
695
|
+
const hidden = this.showStepLabels;
|
|
705
696
|
return html`
|
|
706
697
|
<div
|
|
707
698
|
class="min-max-labels"
|
|
@@ -749,6 +740,25 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
749
740
|
return this.#value;
|
|
750
741
|
}
|
|
751
742
|
|
|
743
|
+
#makeThumbHandlers(flag: ThumbFlag) {
|
|
744
|
+
return {
|
|
745
|
+
show: () => this.#showThumbInput(flag),
|
|
746
|
+
hide: () => this.#scheduleHideThumbInput(flag),
|
|
747
|
+
focus: () => this.#focusFloatingInput(flag),
|
|
748
|
+
input: () => {
|
|
749
|
+
this.#thumbInputState.get(flag)!.committed = false;
|
|
750
|
+
},
|
|
751
|
+
blurCommit: (e: FocusEvent) => {
|
|
752
|
+
const entry = this.#thumbInputState.get(flag)!;
|
|
753
|
+
if (!entry.committed) {
|
|
754
|
+
(e.target as HTMLInputElement).dispatchEvent(new Event('change'));
|
|
755
|
+
}
|
|
756
|
+
entry.committed = false;
|
|
757
|
+
this.#blurFloatingInput(flag);
|
|
758
|
+
},
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
752
762
|
#makeFloatingChange(flag: ThumbFlag, setter: (val: string) => void, dispatch: () => void) {
|
|
753
763
|
return (e: Event) => {
|
|
754
764
|
if (this.readOnly) {
|
|
@@ -760,23 +770,53 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
760
770
|
return;
|
|
761
771
|
}
|
|
762
772
|
const before = this.#currentValueForFlag(flag);
|
|
773
|
+
const commit = (value: string) => {
|
|
774
|
+
setter(value);
|
|
775
|
+
this.#thumbInputState.get(flag)!.committed = true;
|
|
776
|
+
if (this.#currentValueForFlag(flag) !== before) {
|
|
777
|
+
dispatch();
|
|
778
|
+
}
|
|
779
|
+
};
|
|
763
780
|
if (this.#stepsMode) {
|
|
764
781
|
const resolved = this.#resolveFloatingInput(input.value);
|
|
765
782
|
if (resolved !== null) {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
783
|
+
let finalResolved = resolved;
|
|
784
|
+
if (flag !== 'thumb') {
|
|
785
|
+
const resolvedIdx = this.#stepsIndexOf(resolved);
|
|
786
|
+
const endIdx = Math.max(0, this.#stepsIndexOf(this.#valueEnd));
|
|
787
|
+
const startIdx = Math.max(0, this.#stepsIndexOf(this.#valueStart));
|
|
788
|
+
if (flag === 'startThumb' && resolvedIdx >= endIdx) {
|
|
789
|
+
const nudgedIdx = endIdx - 1;
|
|
790
|
+
finalResolved = nudgedIdx >= 0 ? this.#stepAt(nudgedIdx) : this.#currentValueForFlag(flag);
|
|
791
|
+
} else if (flag === 'endThumb' && resolvedIdx <= startIdx) {
|
|
792
|
+
const nudgedIdx = startIdx + 1;
|
|
793
|
+
finalResolved =
|
|
794
|
+
nudgedIdx < this.#normalizedSteps.length ? this.#stepAt(nudgedIdx) : this.#currentValueForFlag(flag);
|
|
795
|
+
}
|
|
770
796
|
}
|
|
797
|
+
commit(finalResolved);
|
|
771
798
|
} else {
|
|
772
799
|
input.value = this.#currentValueForFlag(flag);
|
|
773
800
|
}
|
|
774
801
|
} else {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
802
|
+
let processed = this.#processFloatingValue(parseFloat(input.value));
|
|
803
|
+
if (flag !== 'thumb') {
|
|
804
|
+
const effectiveStep = this.step > 0 ? this.step : 1;
|
|
805
|
+
if (flag === 'startThumb') {
|
|
806
|
+
const endNum = parseFloat(this.#valueEnd);
|
|
807
|
+
if (parseFloat(processed) >= endNum) {
|
|
808
|
+
const nudged = this.#processFloatingValue(endNum - effectiveStep);
|
|
809
|
+
processed = parseFloat(nudged) < endNum ? nudged : this.#currentValueForFlag(flag);
|
|
810
|
+
}
|
|
811
|
+
} else {
|
|
812
|
+
const startNum = parseFloat(this.#valueStart);
|
|
813
|
+
if (parseFloat(processed) <= startNum) {
|
|
814
|
+
const nudged = this.#processFloatingValue(startNum + effectiveStep);
|
|
815
|
+
processed = parseFloat(nudged) > startNum ? nudged : this.#currentValueForFlag(flag);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
779
818
|
}
|
|
819
|
+
commit(processed);
|
|
780
820
|
}
|
|
781
821
|
};
|
|
782
822
|
}
|
|
@@ -835,7 +875,6 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
835
875
|
if (this.disabled || this.readOnly) {
|
|
836
876
|
return;
|
|
837
877
|
}
|
|
838
|
-
|
|
839
878
|
const wrapper = e.currentTarget as HTMLElement;
|
|
840
879
|
const rect = wrapper.getBoundingClientRect();
|
|
841
880
|
// Thumb diameter is 3× the custom property; radius is 1.5×. Track is inset by one radius each side.
|
|
@@ -930,7 +969,9 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
930
969
|
return;
|
|
931
970
|
}
|
|
932
971
|
this.#showThumbInput(flag);
|
|
933
|
-
this.#thumbInputState.get(flag)
|
|
972
|
+
const entry = this.#thumbInputState.get(flag)!;
|
|
973
|
+
entry.focused = true;
|
|
974
|
+
entry.committed = false;
|
|
934
975
|
}
|
|
935
976
|
|
|
936
977
|
#blurFloatingInput(flag: ThumbFlag) {
|