@vuu-ui/vuu-ui-controls 4.0.0-alpha.2 → 4.0.0-alpha.21
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 +15 -14
- package/src/drag-drop/Draggable.css.js +2 -2
- package/src/expando-input/ExpandoInput.css.js +1 -1
- package/src/icon-button/ToggleIconButton.css.js +1 -1
- package/src/index.js +1 -1
- package/src/item-picker/ItemPicker.css.js +1 -5
- package/src/item-picker/ItemPicker.js +69 -60
- package/src/item-picker/useItemPicker.js +2 -5
- package/src/price-ticker/PriceTicker.css.js +4 -4
- package/src/split-pane/SplitPane.css.js +82 -0
- package/src/split-pane/SplitPane.js +60 -0
- package/src/split-pane/Splitter.js +82 -0
- package/src/split-pane/index.js +1 -0
- package/src/split-pane/useSplitPane.js +101 -0
- package/src/tabstrip/Tab.css.js +3 -3
- package/src/tabstrip/Tabstrip.css.js +2 -2
- package/src/time-input/MaskedInput.js +176 -156
- package/src/time-input/TimeInput.css.js +41 -18
- package/src/time-input/TimeInput.js +25 -14
- package/src/time-input/useTimeInput.js +57 -47
- package/src/toolbar/Toolbar.css.js +1 -1
- package/src/vuu-date-picker/VuuDatePicker.js +46 -52
- package/src/vuu-time-picker/VuuTimePicker.js +6 -5
- package/types/context-panel-provider/ContextPanelProvider.d.ts +1 -1
- package/types/drag-drop/DragDropProvider.d.ts +1 -1
- package/types/hover-overlay/HoverOverlay.d.ts +1 -1
- package/types/icon-button/Icon.d.ts +1 -1
- package/types/icon-button/ToggleIconButton.d.ts +1 -1
- package/types/index.d.ts +4 -4
- package/types/instrument-picker/SearchCell.d.ts +1 -1
- package/types/instrument-picker/TablePicker.d.ts +1 -1
- package/types/item-picker/ItemPicker.d.ts +25 -7
- package/types/item-picker/useItemPicker.d.ts +3 -6
- package/types/modal-provider/ModalProvider.d.ts +1 -1
- package/types/price-ticker/PriceTicker.d.ts +1 -1
- package/types/prompt/Prompt.d.ts +1 -1
- package/types/sortable-list/SortableList.d.ts +2 -2
- package/types/split-button/useSplitButton.d.ts +12 -12
- package/types/split-pane/SplitPane.d.ts +44 -0
- package/types/split-pane/Splitter.d.ts +10 -0
- package/types/split-pane/index.d.ts +2 -0
- package/types/split-pane/useSplitPane.d.ts +18 -0
- package/types/table-search/SearchCell.d.ts +1 -1
- package/types/table-search/TableSearch.d.ts +1 -1
- package/types/tabstrip/TabMenu.d.ts +1 -1
- package/types/tabstrip/Tabstrip.d.ts +1 -2
- package/types/time-input/MaskedInput.d.ts +60 -10
- package/types/time-input/TimeInput.d.ts +4 -3
- package/types/time-input/useTimeInput.d.ts +24 -5
- package/types/toolbar/Toolbar.d.ts +1 -2
- package/types/vuu-date-picker/VuuDatePicker.d.ts +19 -3
- package/types/vuu-time-picker/VuuTimePicker.d.ts +8 -6
- package/types/vuu-typeahead-input/VuuTypeaheadInput.d.ts +1 -1
- package/src/modal-provider/ModalProvider.js +0 -147
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
const parsePx = (value, defaultValue)=>value.endsWith("px") ? parseFloat(value) : defaultValue;
|
|
3
|
+
const measurePane = (el, dimension)=>{
|
|
4
|
+
const { [dimension]: size } = el.getBoundingClientRect();
|
|
5
|
+
const style = getComputedStyle(el);
|
|
6
|
+
return {
|
|
7
|
+
size,
|
|
8
|
+
minSize: parsePx(style.getPropertyValue(`min-${dimension}`), 0),
|
|
9
|
+
maxSize: parsePx(style.getPropertyValue(`max-${dimension}`), 1 / 0)
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const getPanes = (root)=>{
|
|
13
|
+
const pane1 = root?.children[0];
|
|
14
|
+
const pane2 = root?.children[2];
|
|
15
|
+
if (pane1 && pane2) return [
|
|
16
|
+
pane1,
|
|
17
|
+
pane2
|
|
18
|
+
];
|
|
19
|
+
};
|
|
20
|
+
const toPercentage = ([size1, size2])=>{
|
|
21
|
+
const total = size1 + size2;
|
|
22
|
+
return 0 === total ? 0 : Math.round(size1 / total * 100);
|
|
23
|
+
};
|
|
24
|
+
const useSplitPane = ({ direction, onSplitterMoved, paneCount })=>{
|
|
25
|
+
const rootRef = useRef(null);
|
|
26
|
+
const resizeStateRef = useRef(void 0);
|
|
27
|
+
const [splitterPosition, setSplitterPosition] = useState(void 0);
|
|
28
|
+
const dimension = "column" === direction ? "height" : "width";
|
|
29
|
+
const hasSplitter = 2 === paneCount;
|
|
30
|
+
useLayoutEffect(()=>{
|
|
31
|
+
const panes = hasSplitter ? getPanes(rootRef.current) : void 0;
|
|
32
|
+
if (panes) {
|
|
33
|
+
const [pane1, pane2] = panes;
|
|
34
|
+
setSplitterPosition(toPercentage([
|
|
35
|
+
pane1.getBoundingClientRect()[dimension],
|
|
36
|
+
pane2.getBoundingClientRect()[dimension]
|
|
37
|
+
]));
|
|
38
|
+
}
|
|
39
|
+
}, [
|
|
40
|
+
dimension,
|
|
41
|
+
hasSplitter
|
|
42
|
+
]);
|
|
43
|
+
const beginResize = useCallback(()=>{
|
|
44
|
+
const panes = getPanes(rootRef.current);
|
|
45
|
+
if (panes) {
|
|
46
|
+
const [pane1, pane2] = panes;
|
|
47
|
+
const measurements = [
|
|
48
|
+
measurePane(pane1, dimension),
|
|
49
|
+
measurePane(pane2, dimension)
|
|
50
|
+
];
|
|
51
|
+
resizeStateRef.current = {
|
|
52
|
+
panes,
|
|
53
|
+
measurements,
|
|
54
|
+
sizes: [
|
|
55
|
+
measurements[0].size,
|
|
56
|
+
measurements[1].size
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}, [
|
|
63
|
+
dimension
|
|
64
|
+
]);
|
|
65
|
+
const resize = useCallback((distance)=>{
|
|
66
|
+
const resizeState = resizeStateRef.current;
|
|
67
|
+
if (resizeState) {
|
|
68
|
+
const { panes: [pane1, pane2], measurements: [m1, m2] } = resizeState;
|
|
69
|
+
const total = m1.size + m2.size;
|
|
70
|
+
const lowerBound = Math.max(m1.minSize, total - m2.maxSize);
|
|
71
|
+
const upperBound = Math.min(m1.maxSize, total - m2.minSize);
|
|
72
|
+
const size1 = Math.max(lowerBound, Math.min(upperBound, m1.size + distance));
|
|
73
|
+
const size2 = total - size1;
|
|
74
|
+
pane1.style.flexBasis = `${size1}px`;
|
|
75
|
+
pane2.style.flexBasis = `${size2}px`;
|
|
76
|
+
resizeState.sizes = [
|
|
77
|
+
size1,
|
|
78
|
+
size2
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
}, []);
|
|
82
|
+
const endResize = useCallback(()=>{
|
|
83
|
+
const resizeState = resizeStateRef.current;
|
|
84
|
+
if (resizeState) {
|
|
85
|
+
resizeStateRef.current = void 0;
|
|
86
|
+
const { measurements, sizes } = resizeState;
|
|
87
|
+
setSplitterPosition(toPercentage(sizes));
|
|
88
|
+
if (sizes[0] !== measurements[0].size || sizes[1] !== measurements[1].size) onSplitterMoved?.(sizes);
|
|
89
|
+
}
|
|
90
|
+
}, [
|
|
91
|
+
onSplitterMoved
|
|
92
|
+
]);
|
|
93
|
+
return {
|
|
94
|
+
beginResize,
|
|
95
|
+
endResize,
|
|
96
|
+
resize,
|
|
97
|
+
rootRef,
|
|
98
|
+
splitterPosition
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export { useSplitPane };
|
package/src/tabstrip/Tab.css.js
CHANGED
|
@@ -101,7 +101,7 @@ const css = `
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
.vuuTab.vuuFocusVisible {
|
|
104
|
-
background: var(--vuuTab-hover-background, var(--salt-navigable-
|
|
104
|
+
background: var(--vuuTab-hover-background, var(--salt-navigable-background-hover));
|
|
105
105
|
outline-color: var(--vuuTab-focusVisible-color, var(--salt-focused-outlineColor));
|
|
106
106
|
outline-offset: -1px;
|
|
107
107
|
outline-width: 1px;
|
|
@@ -121,7 +121,7 @@ const css = `
|
|
|
121
121
|
|
|
122
122
|
.vuuTabstrip-draggingTab .vuuTab-selected:before {
|
|
123
123
|
--tab-before-content: "";
|
|
124
|
-
--tab-before-background: var(--salt-navigable-indicator-
|
|
124
|
+
--tab-before-background: var(--salt-navigable-accent-indicator-active);
|
|
125
125
|
--tab-before-height: var(--tab-thumb-height);
|
|
126
126
|
--tab-before-inset: var(--tab-activationIndicator-inset);
|
|
127
127
|
--tab-before-width: var(--tab-activationIndicator-thumb-width);
|
|
@@ -140,7 +140,7 @@ const css = `
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
.vuuTab:hover:not(.vuuTab-closeHover, .vuuTab-selected) {
|
|
143
|
-
background: var(--vuuTab-hover-background, var(--salt-navigable-
|
|
143
|
+
background: var(--vuuTab-hover-background, var(--salt-navigable-background-hover));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
|
|
@@ -52,7 +52,7 @@ const css = `
|
|
|
52
52
|
|
|
53
53
|
.vuuTabstrip-overflowMenu-open {
|
|
54
54
|
--saltButton-background: var(--salt-actionable-subtle-background-active);
|
|
55
|
-
--saltButton-text-color: var(--salt-actionable-subtle-
|
|
55
|
+
--saltButton-text-color: var(--salt-actionable-subtle-foreground-active);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
.vuuTabstrip-overflowMenu-open .saltButton {
|
|
@@ -82,7 +82,7 @@ const css = `
|
|
|
82
82
|
--tabstrip-display: flex;
|
|
83
83
|
--tabstrip-height: 100%;
|
|
84
84
|
--tabstrip-dragging-display: block;
|
|
85
|
-
--tabs-tab-background: var(--salt-navigable-
|
|
85
|
+
--tabs-tab-background: var(--salt-navigable-background-hover);
|
|
86
86
|
--tabs-tab-before-content: "";
|
|
87
87
|
--tabs-tab-before-background: var(--salt-navigable-indicator-hover);
|
|
88
88
|
--tabs-tab-before-height: var(--tab-activationIndicator-thumb-height);
|
|
@@ -1,35 +1,78 @@
|
|
|
1
|
-
import { EventEmitter, createSyntheticEvent, decrementTimeUnitValue, incrementTimeUnitValue,
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { EventEmitter, createSyntheticEvent, decrementTimeUnitValue, incrementTimeUnitValue, normaliseTimeString, updateTimeString, zeroTime, zeroTimeMillis } from "@vuu-ui/vuu-utils";
|
|
2
|
+
const unitSpec = {
|
|
3
|
+
hours: {
|
|
4
|
+
start: 0,
|
|
5
|
+
length: 2,
|
|
6
|
+
max: 23
|
|
7
|
+
},
|
|
8
|
+
minutes: {
|
|
9
|
+
start: 3,
|
|
10
|
+
length: 2,
|
|
11
|
+
max: 59
|
|
12
|
+
},
|
|
13
|
+
seconds: {
|
|
14
|
+
start: 6,
|
|
15
|
+
length: 2,
|
|
16
|
+
max: 59
|
|
17
|
+
},
|
|
18
|
+
milliseconds: {
|
|
19
|
+
start: 9,
|
|
20
|
+
length: 3,
|
|
21
|
+
max: 999
|
|
22
|
+
}
|
|
9
23
|
};
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
const secondsUnits = [
|
|
25
|
+
"hours",
|
|
26
|
+
"minutes",
|
|
27
|
+
"seconds"
|
|
28
|
+
];
|
|
29
|
+
const millisecondsUnits = [
|
|
30
|
+
...secondsUnits,
|
|
31
|
+
"milliseconds"
|
|
32
|
+
];
|
|
33
|
+
const invalidClassName = "vuuTimeInput-invalid";
|
|
34
|
+
const isValidUnitValue = (unit, value)=>{
|
|
35
|
+
const { length, max } = unitSpec[unit];
|
|
36
|
+
return value.length === length && /^[0-9]+$/.test(value) && parseInt(value) <= max;
|
|
13
37
|
};
|
|
14
38
|
class MaskedInput extends EventEmitter {
|
|
15
39
|
#controlled = false;
|
|
16
40
|
#input = null;
|
|
17
41
|
#isFocused = false;
|
|
18
|
-
#
|
|
42
|
+
#milliseconds;
|
|
19
43
|
#selectionStart = -1;
|
|
20
44
|
#selectionEnd = -1;
|
|
45
|
+
#units;
|
|
21
46
|
#value;
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
constructor(defaultValue, inputEl = null){
|
|
47
|
+
#selectedUnit;
|
|
48
|
+
#digitIndex = 0;
|
|
49
|
+
constructor(defaultValue, inputEl = null, { milliseconds = false } = {}){
|
|
25
50
|
super();
|
|
51
|
+
this.#milliseconds = milliseconds;
|
|
52
|
+
this.#units = milliseconds ? millisecondsUnits : secondsUnits;
|
|
53
|
+
this.#value = this.normalise(defaultValue) ?? this.zeroValue;
|
|
26
54
|
if (inputEl) this.input = inputEl;
|
|
27
|
-
|
|
55
|
+
}
|
|
56
|
+
get milliseconds() {
|
|
57
|
+
return this.#milliseconds;
|
|
58
|
+
}
|
|
59
|
+
get zeroValue() {
|
|
60
|
+
return this.#milliseconds ? zeroTimeMillis : zeroTime;
|
|
61
|
+
}
|
|
62
|
+
get length() {
|
|
63
|
+
return this.#milliseconds ? 12 : 8;
|
|
64
|
+
}
|
|
65
|
+
normalise(value) {
|
|
66
|
+
return normaliseTimeString(value, this.#milliseconds);
|
|
28
67
|
}
|
|
29
68
|
set input(el) {
|
|
30
|
-
if (this.#input)
|
|
69
|
+
if (el === this.#input) return;
|
|
70
|
+
this.#input?.removeEventListener("change", this.emitSyntheticChangeEvent);
|
|
31
71
|
this.#input = el;
|
|
32
|
-
el
|
|
72
|
+
if (el) {
|
|
73
|
+
el.addEventListener("change", this.emitSyntheticChangeEvent);
|
|
74
|
+
if ("" !== el.value && el.value !== this.#value) el.value = this.#value;
|
|
75
|
+
}
|
|
33
76
|
}
|
|
34
77
|
emitSyntheticChangeEvent = (e)=>{
|
|
35
78
|
const syntheticEvent = createSyntheticEvent(e);
|
|
@@ -58,33 +101,46 @@ class MaskedInput extends EventEmitter {
|
|
|
58
101
|
set selectionEnd(value) {
|
|
59
102
|
this.#selectionEnd = value;
|
|
60
103
|
}
|
|
104
|
+
nextUnit(unit) {
|
|
105
|
+
const index = this.#units.indexOf(unit);
|
|
106
|
+
return this.#units[Math.min(index + 1, this.#units.length - 1)];
|
|
107
|
+
}
|
|
108
|
+
previousUnit(unit) {
|
|
109
|
+
const index = this.#units.indexOf(unit);
|
|
110
|
+
return this.#units[Math.max(index - 1, 0)];
|
|
111
|
+
}
|
|
112
|
+
get firstUnit() {
|
|
113
|
+
return this.#units[0];
|
|
114
|
+
}
|
|
115
|
+
get lastUnit() {
|
|
116
|
+
return this.#units[this.#units.length - 1];
|
|
117
|
+
}
|
|
61
118
|
setValue(value) {
|
|
62
|
-
if (!this.#controlled)
|
|
63
|
-
this.#isIncrementalChange = false;
|
|
64
|
-
this.#value = value;
|
|
65
|
-
}
|
|
119
|
+
if (!this.#controlled) this.#value = value;
|
|
66
120
|
if (this.#input) {
|
|
67
121
|
this.#input.value = value;
|
|
68
122
|
this.#input.dispatchEvent(new Event("change", {
|
|
69
123
|
bubbles: true,
|
|
70
124
|
composed: true
|
|
71
125
|
}));
|
|
126
|
+
if (this.#controlled) this.#input.value = this.#value;
|
|
72
127
|
}
|
|
73
128
|
}
|
|
129
|
+
getUnitValue(unit) {
|
|
130
|
+
const { start, length } = unitSpec[unit];
|
|
131
|
+
return this.#value.slice(start, start + length);
|
|
132
|
+
}
|
|
74
133
|
setUnitValue(unit, value) {
|
|
75
|
-
const
|
|
76
|
-
this.setValue(
|
|
134
|
+
const newValue = updateTimeString(this.#value, unit, value);
|
|
135
|
+
this.setValue(newValue);
|
|
77
136
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return this.#value.slice(6, 8);
|
|
86
|
-
default:
|
|
87
|
-
throw Error(`[MaskedInout] invalue unit ${unit}`);
|
|
137
|
+
setInvalid(invalid) {
|
|
138
|
+
if (this.#input) if (invalid) {
|
|
139
|
+
this.#input.classList.add(invalidClassName);
|
|
140
|
+
this.#input.setAttribute("aria-invalid", "true");
|
|
141
|
+
} else {
|
|
142
|
+
this.#input.classList.remove(invalidClassName);
|
|
143
|
+
this.#input.removeAttribute("aria-invalid");
|
|
88
144
|
}
|
|
89
145
|
}
|
|
90
146
|
get value() {
|
|
@@ -92,188 +148,152 @@ class MaskedInput extends EventEmitter {
|
|
|
92
148
|
}
|
|
93
149
|
set value(value) {
|
|
94
150
|
this.#controlled = true;
|
|
95
|
-
|
|
96
|
-
if (this
|
|
97
|
-
|
|
98
|
-
this
|
|
99
|
-
|
|
100
|
-
if (isIncremental) this.restoreSelection();
|
|
101
|
-
else this.advanceSelection();
|
|
151
|
+
const normalisedValue = this.normalise(value);
|
|
152
|
+
if (void 0 !== normalisedValue && normalisedValue !== this.#value) {
|
|
153
|
+
this.#value = normalisedValue;
|
|
154
|
+
if (this.isFocused) requestAnimationFrame(()=>{
|
|
155
|
+
this.restoreSelection();
|
|
102
156
|
});
|
|
103
157
|
}
|
|
104
158
|
}
|
|
105
159
|
clear(unit) {
|
|
106
|
-
if (this.#input)
|
|
107
|
-
let newValue = this.#value;
|
|
108
|
-
if ("hours" === unit) newValue = zeroTimeUnit.concat(this.#value.slice(2));
|
|
109
|
-
else if ("minutes" === unit) newValue = this.#value.slice(0, 3).concat(zeroTimeUnit).concat(this.#value.slice(5));
|
|
110
|
-
else if ("seconds" === unit) newValue = this.#value.slice(0, 6).concat(zeroTimeUnit);
|
|
111
|
-
if (newValue !== this.#value) this.setValue(newValue);
|
|
112
|
-
}
|
|
160
|
+
if (this.#input) this.setUnitValue(unit, "0".repeat(unitSpec[unit].length));
|
|
113
161
|
}
|
|
114
|
-
select(unit,
|
|
115
|
-
if (this.#input) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.selectionEnd = this.#input.selectionEnd = 2;
|
|
120
|
-
} else if ("minutes" === unit) {
|
|
121
|
-
this.selectionStart = this.#input.selectionStart = 3 + offset;
|
|
122
|
-
this.selectionEnd = this.#input.selectionEnd = 5;
|
|
123
|
-
} else if ("seconds" === unit) {
|
|
124
|
-
this.selectionStart = this.#input.selectionStart = 6 + offset;
|
|
125
|
-
this.selectionEnd = this.#input.selectionEnd = 8;
|
|
126
|
-
}
|
|
162
|
+
select(unit, digitIndex = 0) {
|
|
163
|
+
if (this.#input && this.#units.includes(unit)) {
|
|
164
|
+
const { start, length } = unitSpec[unit];
|
|
165
|
+
this.selectionStart = start + digitIndex;
|
|
166
|
+
this.selectionEnd = start + length;
|
|
127
167
|
this.#input.setSelectionRange(this.selectionStart, this.selectionEnd);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.#unitSelected = void 0;
|
|
131
|
-
} else {
|
|
132
|
-
this.#halfUnitSelected = void 0;
|
|
133
|
-
this.#unitSelected = unit;
|
|
134
|
-
}
|
|
168
|
+
this.#selectedUnit = unit;
|
|
169
|
+
this.#digitIndex = digitIndex;
|
|
135
170
|
}
|
|
136
171
|
}
|
|
137
172
|
removeSelection() {
|
|
138
|
-
this
|
|
139
|
-
this.#
|
|
140
|
-
this.#
|
|
173
|
+
this.selectionStart = this.selectionEnd = this.length;
|
|
174
|
+
this.#selectedUnit = void 0;
|
|
175
|
+
this.#digitIndex = 0;
|
|
141
176
|
}
|
|
142
177
|
restoreSelection() {
|
|
143
|
-
if (this.#
|
|
178
|
+
if (this.#selectedUnit) this.select(this.#selectedUnit, this.#digitIndex);
|
|
144
179
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
else throw Error("unreachable code, in theory");
|
|
180
|
+
selectFirst() {
|
|
181
|
+
this.select(this.firstUnit);
|
|
182
|
+
}
|
|
183
|
+
selectLast() {
|
|
184
|
+
this.select(this.lastUnit);
|
|
151
185
|
}
|
|
152
186
|
moveFocus(direction) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
} else if ("seconds" === this.#unitSelected || "seconds" === this.#halfUnitSelected) this.select("minutes");
|
|
158
|
-
else if ("minutes" === this.#unitSelected || "minutes" === this.#halfUnitSelected) this.select("hours");
|
|
159
|
-
else if ("hours" === this.#unitSelected || "hours" === this.#halfUnitSelected) this.select("hours");
|
|
160
|
-
else {
|
|
161
|
-
const selection = this.getSelection();
|
|
162
|
-
if (selection === CursorAtEnd) this.select("seconds");
|
|
163
|
-
}
|
|
187
|
+
const unit = this.#selectedUnit;
|
|
188
|
+
if (unit) this.select("right" === direction ? this.nextUnit(unit) : this.previousUnit(unit));
|
|
189
|
+
else if ("right" === direction) this.selectFirst();
|
|
190
|
+
else this.selectLast();
|
|
164
191
|
}
|
|
165
192
|
pasteValue(value) {
|
|
166
|
-
|
|
167
|
-
this.#
|
|
168
|
-
|
|
193
|
+
const normalisedValue = this.normalise(value.trim());
|
|
194
|
+
if (this.#input && void 0 !== normalisedValue) {
|
|
195
|
+
this.setInvalid(false);
|
|
196
|
+
if (normalisedValue !== this.#value) this.setValue(normalisedValue);
|
|
197
|
+
this.selectFirst();
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
169
201
|
}
|
|
170
202
|
getUnitAtCursorPos(cursorPos = this.cursorPos) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
for(let i = this.#units.length - 1; i > 0; i--){
|
|
204
|
+
const unit = this.#units[i];
|
|
205
|
+
if (cursorPos >= unitSpec[unit].start) return unit;
|
|
206
|
+
}
|
|
207
|
+
return this.firstUnit;
|
|
208
|
+
}
|
|
209
|
+
get activeUnit() {
|
|
210
|
+
return this.#selectedUnit ?? this.getUnitAtCursorPos();
|
|
175
211
|
}
|
|
176
212
|
incrementValue() {
|
|
177
213
|
if (this.#input) {
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
const newUnitValue = incrementTimeUnitValue(unit, unitValue);
|
|
214
|
+
const unit = this.activeUnit;
|
|
215
|
+
const newUnitValue = incrementTimeUnitValue(unit, this.getUnitValue(unit));
|
|
216
|
+
this.setInvalid(false);
|
|
182
217
|
this.setUnitValue(unit, newUnitValue);
|
|
183
218
|
this.select(unit);
|
|
184
219
|
}
|
|
185
220
|
}
|
|
186
221
|
decrementValue() {
|
|
187
222
|
if (this.#input) {
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
const newUnitValue = decrementTimeUnitValue(unit, unitValue);
|
|
223
|
+
const unit = this.activeUnit;
|
|
224
|
+
const newUnitValue = decrementTimeUnitValue(unit, this.getUnitValue(unit));
|
|
225
|
+
this.setInvalid(false);
|
|
192
226
|
this.setUnitValue(unit, newUnitValue);
|
|
193
227
|
this.select(unit);
|
|
194
228
|
}
|
|
195
229
|
}
|
|
196
230
|
backspace() {
|
|
197
231
|
if (this.#input) {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.clear("minutes");
|
|
204
|
-
this.select("hours");
|
|
205
|
-
} else if ("hours" === this.#unitSelected) {
|
|
206
|
-
this.clear("hours");
|
|
207
|
-
this.select("hours");
|
|
208
|
-
} else if (cursorPos > 0) {
|
|
209
|
-
const offset = 6 === this.selectionStart || 3 === this.selectionStart ? 2 : 1;
|
|
210
|
-
const newValue = this.#value.slice(0, cursorPos - offset).concat(zeroTime.slice(cursorPos - offset, cursorPos)).concat(this.#value.slice(cursorPos));
|
|
211
|
-
this.selectionStart -= offset;
|
|
212
|
-
this.selectionEnd -= offset;
|
|
213
|
-
this.setValue(newValue);
|
|
214
|
-
requestAnimationFrame(()=>{
|
|
215
|
-
this.#input?.setSelectionRange(this.#selectionStart, this.selectionEnd);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
232
|
+
const unit = this.activeUnit;
|
|
233
|
+
const isPartiallyEntered = this.#selectedUnit === unit && this.#digitIndex > 0;
|
|
234
|
+
this.setInvalid(false);
|
|
235
|
+
this.clear(unit);
|
|
236
|
+
this.select(isPartiallyEntered ? unit : this.previousUnit(unit));
|
|
218
237
|
}
|
|
219
238
|
}
|
|
220
239
|
update(key) {
|
|
221
240
|
if (this.#input) {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
241
|
+
const unit = this.activeUnit;
|
|
242
|
+
const { length, max } = unitSpec[unit];
|
|
243
|
+
const digitIndex = this.#selectedUnit === unit && this.#digitIndex < length ? this.#digitIndex : 0;
|
|
244
|
+
const maxFirstDigit = Math.floor(max / 10 ** (length - 1));
|
|
245
|
+
let newUnitValue;
|
|
246
|
+
let nextDigitIndex;
|
|
247
|
+
if (0 === digitIndex && parseInt(key) > maxFirstDigit) {
|
|
248
|
+
newUnitValue = key.padStart(length, "0");
|
|
249
|
+
nextDigitIndex = length;
|
|
250
|
+
} else {
|
|
251
|
+
const unitValue = this.getUnitValue(unit);
|
|
252
|
+
const head = unitValue.slice(0, digitIndex).concat(key);
|
|
253
|
+
const overwritten = head.concat(unitValue.slice(digitIndex + 1));
|
|
254
|
+
newUnitValue = isValidUnitValue(unit, overwritten) ? overwritten : head.padEnd(length, "0");
|
|
255
|
+
nextDigitIndex = digitIndex + 1;
|
|
256
|
+
}
|
|
257
|
+
if (!isValidUnitValue(unit, newUnitValue)) {
|
|
258
|
+
this.setInvalid(true);
|
|
259
|
+
this.select(unit, digitIndex);
|
|
260
|
+
return;
|
|
235
261
|
}
|
|
262
|
+
this.setInvalid(false);
|
|
263
|
+
this.setUnitValue(unit, newUnitValue);
|
|
264
|
+
if (nextDigitIndex === length) this.select(this.nextUnit(unit));
|
|
265
|
+
else this.select(unit, nextDigitIndex);
|
|
236
266
|
}
|
|
237
267
|
}
|
|
238
268
|
getSelection() {
|
|
239
269
|
if (this.#input) {
|
|
240
270
|
const { selectionEnd, selectionStart } = this.#input;
|
|
241
|
-
|
|
242
|
-
if (0 === selectionStart && 8 === selectionEnd) return FullSelection;
|
|
243
|
-
if (8 === selectionStart && 8 === selectionEnd) return CursorAtEnd;
|
|
244
|
-
else return {
|
|
271
|
+
return {
|
|
245
272
|
end: selectionEnd,
|
|
246
273
|
start: selectionStart
|
|
247
274
|
};
|
|
248
275
|
}
|
|
249
|
-
throw Error("[
|
|
276
|
+
throw Error("[MaskedInput] selection referenced, but no input");
|
|
250
277
|
}
|
|
251
278
|
click() {
|
|
252
279
|
if (this.#input) {
|
|
253
280
|
this.#isFocused = true;
|
|
254
|
-
const
|
|
255
|
-
if (
|
|
256
|
-
else
|
|
257
|
-
else {
|
|
258
|
-
const cursorPos = this.#input.selectionStart ?? 0;
|
|
259
|
-
this.select(this.getUnitAtCursorPos(cursorPos));
|
|
260
|
-
}
|
|
281
|
+
const { start, end } = this.getSelection();
|
|
282
|
+
if (null === start) this.selectFirst();
|
|
283
|
+
else 0 === start && end === this.length || this.select(this.getUnitAtCursorPos(start));
|
|
261
284
|
}
|
|
262
285
|
}
|
|
263
286
|
doubleClick() {
|
|
264
287
|
if (this.#input) {
|
|
265
|
-
const { selectionStart
|
|
266
|
-
if (null
|
|
267
|
-
else if (selectionStart < 3) this.select("hours");
|
|
268
|
-
else if (selectionStart < 6) this.select("minutes");
|
|
269
|
-
else if (selectionStart <= 8) this.select("seconds");
|
|
288
|
+
const { selectionStart } = this.#input;
|
|
289
|
+
if (null !== selectionStart) this.select(this.getUnitAtCursorPos(selectionStart));
|
|
270
290
|
}
|
|
271
291
|
}
|
|
272
292
|
focus = ()=>{
|
|
273
293
|
if (this.#input) {
|
|
274
294
|
this.#isFocused = true;
|
|
275
295
|
requestAnimationFrame(()=>{
|
|
276
|
-
this.
|
|
296
|
+
this.selectFirst();
|
|
277
297
|
});
|
|
278
298
|
}
|
|
279
299
|
};
|
|
@@ -282,4 +302,4 @@ class MaskedInput extends EventEmitter {
|
|
|
282
302
|
this.#isFocused = false;
|
|
283
303
|
};
|
|
284
304
|
}
|
|
285
|
-
export { MaskedInput };
|
|
305
|
+
export { MaskedInput, invalidClassName };
|
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
const css = `
|
|
2
|
-
.
|
|
3
|
-
--time-input-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
.vuuTimeInput-container {
|
|
3
|
+
--time-input-border: var(--vuuTimeInput-borderWidth, 1px);
|
|
4
|
+
--time-input-padding: var(--vuuTimeInput-paddingInline, var(--salt-spacing-50));
|
|
5
|
+
font-family: var(--salt-typography-fontFamily);
|
|
6
|
+
font-size: var(--vuuTimeInput-fontSize, var(--salt-text-fontSize));
|
|
7
|
+
font-variant-numeric: tabular-nums;
|
|
8
|
+
width: fit-content;
|
|
9
|
+
display: inline-grid;
|
|
10
|
+
|
|
11
|
+
&:before, &:after {
|
|
12
|
+
border: 0 solid #0000;
|
|
13
|
+
border-inline-width: var(--time-input-border);
|
|
14
|
+
box-sizing: content-box;
|
|
15
|
+
height: 0;
|
|
16
|
+
max-width: calc(var(--vuuTimeInput-width, 10000px) - 1px);
|
|
17
|
+
padding-inline: var(--time-input-padding);
|
|
18
|
+
visibility: hidden;
|
|
19
|
+
white-space: pre;
|
|
20
|
+
grid-area: 1 / 1;
|
|
21
|
+
padding-inline-end: calc(var(--time-input-padding) + 1px);
|
|
22
|
+
}
|
|
9
23
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
24
|
+
&:before {
|
|
25
|
+
content: attr(data-placeholder);
|
|
26
|
+
}
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
&:after {
|
|
29
|
+
content: attr(data-sample-value);
|
|
30
|
+
}
|
|
16
31
|
}
|
|
17
32
|
|
|
18
33
|
.vuuTimeInput {
|
|
19
34
|
background: var(--vuuTimeInput-background, inherit);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
border-width: var(--time-input-border);
|
|
36
|
+
box-sizing: content-box;
|
|
37
|
+
font-family: inherit;
|
|
38
|
+
font-size: inherit;
|
|
39
|
+
font-variant-numeric: inherit;
|
|
40
|
+
height: calc(var(--vuuTimeInput-height, var(--salt-size-base)) - 2 *
|
|
41
|
+
var(--time-input-border));
|
|
42
|
+
min-width: calc(100% - 2 * var(--time-input-padding) - 2 * var(--time-input-border));
|
|
43
|
+
padding: 0 var(--time-input-padding);
|
|
44
|
+
width: var(--vuuTimeInput-width, 0px);
|
|
45
|
+
grid-area: 1 / 1;
|
|
23
46
|
|
|
24
47
|
&:focus::-webkit-input-placeholder {
|
|
25
48
|
color: #0000;
|
|
@@ -28,10 +51,10 @@ const css = `
|
|
|
28
51
|
&:focus {
|
|
29
52
|
outline: var(--vuuTimeInput-outline, var(--salt-focused-outline));
|
|
30
53
|
}
|
|
31
|
-
}
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
&.vuuTimeInput-invalid {
|
|
56
|
+
border-color: var(--vuuTimeInput-invalid-borderColor, var(--salt-status-error-borderColor, red));
|
|
57
|
+
}
|
|
35
58
|
}
|
|
36
59
|
|
|
37
60
|
|