@zywave/zui-slider 4.4.0-pre.5 → 4.4.0-pre.7
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 +69 -3
- package/dist/zui-slider.js +86 -42
- package/dist/zui-slider.js.map +1 -1
- package/package.json +2 -2
- package/src/zui-slider.ts +92 -41
- package/test/zui-slider.test.ts +265 -97
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"kind": "field",
|
|
95
95
|
"name": "#thumbInputState",
|
|
96
96
|
"privacy": "private",
|
|
97
|
-
"default": "new Map< ThumbFlag, { visible: boolean; focused: boolean; timer?: ReturnType<typeof setTimeout>; } >([ ['thumb', { visible: false, focused: false }], ['startThumb', { visible: false, focused: false }], ['endThumb', { visible: false, focused: false }], ])"
|
|
97
|
+
"default": "new Map< ThumbFlag, { visible: boolean; focused: boolean; committed: boolean; timer?: ReturnType<typeof setTimeout>; } >([ ['thumb', { visible: false, focused: false, committed: false }], ['startThumb', { visible: false, focused: false, committed: false }], ['endThumb', { visible: false, focused: false, committed: false }], ])"
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"kind": "field",
|
|
@@ -126,14 +126,19 @@
|
|
|
126
126
|
"name": "#onRangeEndInput",
|
|
127
127
|
"privacy": "private"
|
|
128
128
|
},
|
|
129
|
+
{
|
|
130
|
+
"kind": "field",
|
|
131
|
+
"name": "#stopClickPropagation",
|
|
132
|
+
"privacy": "private"
|
|
133
|
+
},
|
|
129
134
|
{
|
|
130
135
|
"kind": "field",
|
|
131
136
|
"name": "#h",
|
|
132
137
|
"privacy": "private",
|
|
133
138
|
"type": {
|
|
134
|
-
"text": "Record
|
|
139
|
+
"text": "Record<\n ThumbFlag,\n { show: () => void; hide: () => void; focus: () => void; input: () => void; blurCommit: (e: FocusEvent) => void }\n >"
|
|
135
140
|
},
|
|
136
|
-
"default": "{ thumb:
|
|
141
|
+
"default": "{ thumb: this.#makeThumbHandlers('thumb'), startThumb: this.#makeThumbHandlers('startThumb'), endThumb: this.#makeThumbHandlers('endThumb'), }"
|
|
137
142
|
},
|
|
138
143
|
{
|
|
139
144
|
"kind": "field",
|
|
@@ -673,6 +678,67 @@
|
|
|
673
678
|
}
|
|
674
679
|
]
|
|
675
680
|
},
|
|
681
|
+
{
|
|
682
|
+
"kind": "method",
|
|
683
|
+
"name": "#makeThumbHandlers",
|
|
684
|
+
"privacy": "private",
|
|
685
|
+
"parameters": [
|
|
686
|
+
{
|
|
687
|
+
"name": "flag",
|
|
688
|
+
"type": {
|
|
689
|
+
"text": "ThumbFlag"
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
]
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
"kind": "method",
|
|
696
|
+
"name": "#nudgeStepsValue",
|
|
697
|
+
"privacy": "private",
|
|
698
|
+
"return": {
|
|
699
|
+
"type": {
|
|
700
|
+
"text": "string"
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
"parameters": [
|
|
704
|
+
{
|
|
705
|
+
"name": "flag",
|
|
706
|
+
"type": {
|
|
707
|
+
"text": "ThumbFlag"
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
"name": "resolved",
|
|
712
|
+
"type": {
|
|
713
|
+
"text": "string"
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
]
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
"kind": "method",
|
|
720
|
+
"name": "#nudgeNumericValue",
|
|
721
|
+
"privacy": "private",
|
|
722
|
+
"return": {
|
|
723
|
+
"type": {
|
|
724
|
+
"text": "string"
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
"parameters": [
|
|
728
|
+
{
|
|
729
|
+
"name": "flag",
|
|
730
|
+
"type": {
|
|
731
|
+
"text": "ThumbFlag"
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
"name": "processed",
|
|
736
|
+
"type": {
|
|
737
|
+
"text": "string"
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
]
|
|
741
|
+
},
|
|
676
742
|
{
|
|
677
743
|
"kind": "method",
|
|
678
744
|
"name": "#makeFloatingChange",
|
package/dist/zui-slider.js
CHANGED
|
@@ -65,11 +65,10 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
65
65
|
this.#cachedNormalizedSteps = null;
|
|
66
66
|
this.#cachedNumericValues = null;
|
|
67
67
|
this.#thumbInputState = new Map([
|
|
68
|
-
['thumb', { visible: false, focused: false }],
|
|
69
|
-
['startThumb', { visible: false, focused: false }],
|
|
70
|
-
['endThumb', { visible: false, focused: false }],
|
|
68
|
+
['thumb', { visible: false, focused: false, committed: false }],
|
|
69
|
+
['startThumb', { visible: false, focused: false, committed: false }],
|
|
70
|
+
['endThumb', { visible: false, focused: false, committed: false }],
|
|
71
71
|
]);
|
|
72
|
-
// Cached floating input change handlers; commit value on Enter or blur
|
|
73
72
|
this.#onThumbFloatingChange = this.#makeFloatingChange('thumb', (v) => (this.value = v), () => this.#onChange());
|
|
74
73
|
this.#onStartThumbFloatingChange = this.#makeFloatingChange('startThumb', (v) => (this.valueStart = v), () => this.#onRangeChange());
|
|
75
74
|
this.#onEndThumbFloatingChange = this.#makeFloatingChange('endThumb', (v) => (this.valueEnd = v), () => this.#onRangeChange());
|
|
@@ -79,32 +78,16 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
79
78
|
e.preventDefault();
|
|
80
79
|
const input = e.target;
|
|
81
80
|
input.dispatchEvent(new Event('change'));
|
|
82
|
-
input.blur();
|
|
83
81
|
}
|
|
84
82
|
};
|
|
85
|
-
// Cached range drag input handlers
|
|
86
83
|
this.#onRangeStartInput = this.#onRangeInput('start');
|
|
87
84
|
this.#onRangeEndInput = this.#onRangeInput('end');
|
|
85
|
+
this.#stopClickPropagation = (e) => e.stopPropagation();
|
|
88
86
|
// Cached pointer/focus handlers per thumb; prevents new closures on every render
|
|
89
87
|
this.#h = {
|
|
90
|
-
thumb:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
focus: () => this.#focusFloatingInput('thumb'),
|
|
94
|
-
blur: () => this.#blurFloatingInput('thumb'),
|
|
95
|
-
},
|
|
96
|
-
startThumb: {
|
|
97
|
-
show: () => this.#showThumbInput('startThumb'),
|
|
98
|
-
hide: () => this.#scheduleHideThumbInput('startThumb'),
|
|
99
|
-
focus: () => this.#focusFloatingInput('startThumb'),
|
|
100
|
-
blur: () => this.#blurFloatingInput('startThumb'),
|
|
101
|
-
},
|
|
102
|
-
endThumb: {
|
|
103
|
-
show: () => this.#showThumbInput('endThumb'),
|
|
104
|
-
hide: () => this.#scheduleHideThumbInput('endThumb'),
|
|
105
|
-
focus: () => this.#focusFloatingInput('endThumb'),
|
|
106
|
-
blur: () => this.#blurFloatingInput('endThumb'),
|
|
107
|
-
},
|
|
88
|
+
thumb: this.#makeThumbHandlers('thumb'),
|
|
89
|
+
startThumb: this.#makeThumbHandlers('startThumb'),
|
|
90
|
+
endThumb: this.#makeThumbHandlers('endThumb'),
|
|
108
91
|
};
|
|
109
92
|
this.steps = [];
|
|
110
93
|
this.stepParser = null;
|
|
@@ -124,15 +107,14 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
124
107
|
#cachedNormalizedSteps;
|
|
125
108
|
#cachedNumericValues;
|
|
126
109
|
#thumbInputState;
|
|
127
|
-
// Cached floating input change handlers; commit value on Enter or blur
|
|
128
110
|
#onThumbFloatingChange;
|
|
129
111
|
#onStartThumbFloatingChange;
|
|
130
112
|
#onEndThumbFloatingChange;
|
|
131
113
|
// Cached keydown handler: Enter commits the floating input value in both number and text modes.
|
|
132
114
|
#onFloatingInputKeydown;
|
|
133
|
-
// Cached range drag input handlers
|
|
134
115
|
#onRangeStartInput;
|
|
135
116
|
#onRangeEndInput;
|
|
117
|
+
#stopClickPropagation;
|
|
136
118
|
// Cached pointer/focus handlers per thumb; prevents new closures on every render
|
|
137
119
|
#h;
|
|
138
120
|
static get styles() {
|
|
@@ -239,6 +221,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
239
221
|
entry.timer = undefined;
|
|
240
222
|
entry.visible = false;
|
|
241
223
|
entry.focused = false;
|
|
224
|
+
entry.committed = false;
|
|
242
225
|
}
|
|
243
226
|
}
|
|
244
227
|
#syncValuesToSteps() {
|
|
@@ -529,6 +512,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
529
512
|
.step="${nativeStep}"
|
|
530
513
|
.value="${live(nativeValue)}"
|
|
531
514
|
?disabled="${this.disabled || this.readOnly}"
|
|
515
|
+
@click="${this.#stopClickPropagation}"
|
|
532
516
|
@input="${onInput}"
|
|
533
517
|
@change="${this.#onRangeChange}"
|
|
534
518
|
@pointerenter="${h.show}"
|
|
@@ -547,6 +531,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
547
531
|
<div
|
|
548
532
|
class=${classMap({ 'thumb-input': true, 'thumb-input--visible': visible })}
|
|
549
533
|
style=${styleMap({ left: _a.#thumbPositionCSS(progress) })}
|
|
534
|
+
@click="${this.#stopClickPropagation}"
|
|
550
535
|
@pointerenter="${h.show}"
|
|
551
536
|
@pointerleave="${h.hide}"
|
|
552
537
|
>
|
|
@@ -560,9 +545,10 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
560
545
|
?disabled="${this.disabled}"
|
|
561
546
|
?readonly="${this.readOnly}"
|
|
562
547
|
@keydown="${this.#onFloatingInputKeydown}"
|
|
548
|
+
@input="${h.input}"
|
|
563
549
|
@change="${onFloatingChange}"
|
|
564
550
|
@focus="${h.focus}"
|
|
565
|
-
@blur="${h.
|
|
551
|
+
@blur="${h.blurCommit}"
|
|
566
552
|
/>
|
|
567
553
|
</div>
|
|
568
554
|
`;
|
|
@@ -618,7 +604,7 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
618
604
|
const normalized = this.#normalizedSteps;
|
|
619
605
|
const minLabel = this.#stepsMode ? normalized[0]?.label ?? '' : String(this.min);
|
|
620
606
|
const maxLabel = this.#stepsMode ? normalized[normalized.length - 1]?.label ?? '' : String(this.max);
|
|
621
|
-
const hidden = this.showStepLabels
|
|
607
|
+
const hidden = this.showStepLabels;
|
|
622
608
|
return html `
|
|
623
609
|
<div
|
|
624
610
|
class="min-max-labels"
|
|
@@ -661,6 +647,62 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
661
647
|
}
|
|
662
648
|
return this.#value;
|
|
663
649
|
}
|
|
650
|
+
#makeThumbHandlers(flag) {
|
|
651
|
+
return {
|
|
652
|
+
show: () => this.#showThumbInput(flag),
|
|
653
|
+
hide: () => this.#scheduleHideThumbInput(flag),
|
|
654
|
+
focus: () => this.#focusFloatingInput(flag),
|
|
655
|
+
input: () => {
|
|
656
|
+
this.#thumbInputState.get(flag).committed = false;
|
|
657
|
+
},
|
|
658
|
+
blurCommit: (e) => {
|
|
659
|
+
const entry = this.#thumbInputState.get(flag);
|
|
660
|
+
if (!entry.committed) {
|
|
661
|
+
e.target.dispatchEvent(new Event('change'));
|
|
662
|
+
}
|
|
663
|
+
entry.committed = false;
|
|
664
|
+
this.#blurFloatingInput(flag);
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
#nudgeStepsValue(flag, resolved) {
|
|
669
|
+
if (flag === 'thumb') {
|
|
670
|
+
return resolved;
|
|
671
|
+
}
|
|
672
|
+
const resolvedIdx = this.#stepsIndexOf(resolved);
|
|
673
|
+
const endIdx = Math.max(0, this.#stepsIndexOf(this.#valueEnd));
|
|
674
|
+
const startIdx = Math.max(0, this.#stepsIndexOf(this.#valueStart));
|
|
675
|
+
if (flag === 'startThumb' && resolvedIdx >= endIdx) {
|
|
676
|
+
const nudgedIdx = endIdx - 1;
|
|
677
|
+
return nudgedIdx >= 0 ? this.#stepAt(nudgedIdx) : this.#currentValueForFlag(flag);
|
|
678
|
+
}
|
|
679
|
+
if (flag === 'endThumb' && resolvedIdx <= startIdx) {
|
|
680
|
+
const nudgedIdx = startIdx + 1;
|
|
681
|
+
return nudgedIdx < this.#normalizedSteps.length ? this.#stepAt(nudgedIdx) : this.#currentValueForFlag(flag);
|
|
682
|
+
}
|
|
683
|
+
return resolved;
|
|
684
|
+
}
|
|
685
|
+
#nudgeNumericValue(flag, processed) {
|
|
686
|
+
if (flag === 'thumb') {
|
|
687
|
+
return processed;
|
|
688
|
+
}
|
|
689
|
+
const effectiveStep = this.step > 0 ? this.step : 1;
|
|
690
|
+
if (flag === 'startThumb') {
|
|
691
|
+
const endNum = parseFloat(this.#valueEnd);
|
|
692
|
+
if (parseFloat(processed) >= endNum) {
|
|
693
|
+
const nudged = this.#processFloatingValue(endNum - effectiveStep);
|
|
694
|
+
return parseFloat(nudged) < endNum ? nudged : this.#currentValueForFlag(flag);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
const startNum = parseFloat(this.#valueStart);
|
|
699
|
+
if (parseFloat(processed) <= startNum) {
|
|
700
|
+
const nudged = this.#processFloatingValue(startNum + effectiveStep);
|
|
701
|
+
return parseFloat(nudged) > startNum ? nudged : this.#currentValueForFlag(flag);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return processed;
|
|
705
|
+
}
|
|
664
706
|
#makeFloatingChange(flag, setter, dispatch) {
|
|
665
707
|
return (e) => {
|
|
666
708
|
if (this.readOnly) {
|
|
@@ -672,25 +714,25 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
672
714
|
return;
|
|
673
715
|
}
|
|
674
716
|
const before = this.#currentValueForFlag(flag);
|
|
717
|
+
const commit = (value) => {
|
|
718
|
+
setter(value);
|
|
719
|
+
this.#thumbInputState.get(flag).committed = true;
|
|
720
|
+
if (this.#currentValueForFlag(flag) !== before) {
|
|
721
|
+
dispatch();
|
|
722
|
+
}
|
|
723
|
+
// Force re-render so live() corrects the DOM when the nudged/clamped value equals the stored value and the setter no-ops.
|
|
724
|
+
this.requestUpdate();
|
|
725
|
+
};
|
|
675
726
|
if (this.#stepsMode) {
|
|
676
727
|
const resolved = this.#resolveFloatingInput(input.value);
|
|
677
|
-
if (resolved
|
|
678
|
-
setter(resolved);
|
|
679
|
-
this.requestUpdate();
|
|
680
|
-
if (this.#currentValueForFlag(flag) !== before) {
|
|
681
|
-
dispatch();
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
else {
|
|
728
|
+
if (resolved === null) {
|
|
685
729
|
input.value = this.#currentValueForFlag(flag);
|
|
730
|
+
return;
|
|
686
731
|
}
|
|
732
|
+
commit(this.#nudgeStepsValue(flag, resolved));
|
|
687
733
|
}
|
|
688
734
|
else {
|
|
689
|
-
|
|
690
|
-
this.requestUpdate();
|
|
691
|
-
if (this.#currentValueForFlag(flag) !== before) {
|
|
692
|
-
dispatch();
|
|
693
|
-
}
|
|
735
|
+
commit(this.#nudgeNumericValue(flag, this.#processFloatingValue(parseFloat(input.value))));
|
|
694
736
|
}
|
|
695
737
|
};
|
|
696
738
|
}
|
|
@@ -833,7 +875,9 @@ export class ZuiSlider extends ZuiFormAssociatedElement {
|
|
|
833
875
|
return;
|
|
834
876
|
}
|
|
835
877
|
this.#showThumbInput(flag);
|
|
836
|
-
this.#thumbInputState.get(flag)
|
|
878
|
+
const entry = this.#thumbInputState.get(flag);
|
|
879
|
+
entry.focused = true;
|
|
880
|
+
entry.committed = false;
|
|
837
881
|
}
|
|
838
882
|
#blurFloatingInput(flag) {
|
|
839
883
|
const entry = this.#thumbInputState.get(flag);
|