@vuu-ui/vuu-ui-controls 4.0.0-alpha.9 → 4.0.0-beta.2

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.
Files changed (197) hide show
  1. package/package.json +16 -14
  2. package/src/context-panel-provider/ContextPanelProvider.js +40 -28
  3. package/src/drag-drop/Draggable.css.js +2 -2
  4. package/src/expando-input/ExpandoInput.css.js +1 -1
  5. package/src/icon-button/Icon.js +0 -1
  6. package/src/icon-button/IconButton.js +1 -1
  7. package/src/icon-button/ToggleIconButton.css.js +1 -1
  8. package/src/index.js +1 -1
  9. package/src/instrument-picker/useTablePicker.js +2 -3
  10. package/src/item-picker/ItemPicker.css.js +1 -1
  11. package/src/item-picker/ItemPicker.js +53 -79
  12. package/src/price-ticker/PriceTicker.css.js +4 -4
  13. package/src/split-pane/SplitPane.css.js +82 -0
  14. package/src/split-pane/SplitPane.js +60 -0
  15. package/src/split-pane/Splitter.js +82 -0
  16. package/src/split-pane/index.js +1 -0
  17. package/src/split-pane/useSplitPane.js +101 -0
  18. package/src/tabstrip/Tab.css.js +3 -3
  19. package/src/tabstrip/Tabstrip.css.js +2 -2
  20. package/src/tabstrip/Tabstrip.js +1 -1
  21. package/src/time-input/MaskedInput.js +176 -156
  22. package/src/time-input/TimeInput.css.js +41 -18
  23. package/src/time-input/TimeInput.js +25 -14
  24. package/src/time-input/useTimeInput.js +57 -47
  25. package/src/toolbar/Toolbar.css.js +1 -1
  26. package/src/vuu-date-picker/VuuDatePicker.js +46 -52
  27. package/src/vuu-time-picker/VuuTimePicker.js +6 -5
  28. package/types/context-panel-provider/ContextPanelProvider.d.ts +7 -4
  29. package/types/drag-drop/DragDropProvider.d.ts +1 -1
  30. package/types/hover-overlay/HoverOverlay.d.ts +1 -1
  31. package/types/icon-button/Icon.d.ts +2 -2
  32. package/types/icon-button/IconButton.d.ts +1 -1
  33. package/types/icon-button/ToggleIconButton.d.ts +1 -1
  34. package/types/index.d.ts +4 -3
  35. package/types/instrument-picker/SearchCell.d.ts +1 -1
  36. package/types/instrument-picker/TablePicker.d.ts +1 -1
  37. package/types/instrument-picker/useTablePicker.d.ts +1 -1
  38. package/types/item-picker/ItemPicker.d.ts +5 -26
  39. package/types/item-picker/useItemPicker.d.ts +3 -4
  40. package/types/modal-provider/ModalProvider.d.ts +1 -1
  41. package/types/price-ticker/PriceTicker.d.ts +1 -1
  42. package/types/prompt/Prompt.d.ts +1 -1
  43. package/types/sortable-list/SortableList.d.ts +2 -2
  44. package/types/split-button/useSplitButton.d.ts +12 -12
  45. package/types/split-pane/SplitPane.d.ts +44 -0
  46. package/types/split-pane/Splitter.d.ts +10 -0
  47. package/types/split-pane/index.d.ts +2 -0
  48. package/types/split-pane/useSplitPane.d.ts +18 -0
  49. package/types/table-search/SearchCell.d.ts +1 -1
  50. package/types/table-search/TableSearch.d.ts +1 -1
  51. package/types/tabstrip/TabMenu.d.ts +1 -1
  52. package/types/tabstrip/Tabstrip.d.ts +2 -3
  53. package/types/time-input/MaskedInput.d.ts +60 -10
  54. package/types/time-input/TimeInput.d.ts +4 -3
  55. package/types/time-input/useTimeInput.d.ts +24 -5
  56. package/types/toolbar/Toolbar.d.ts +1 -2
  57. package/types/vuu-date-picker/VuuDatePicker.d.ts +19 -3
  58. package/types/vuu-time-picker/VuuTimePicker.d.ts +8 -6
  59. package/types/vuu-typeahead-input/VuuTypeaheadInput.d.ts +1 -1
  60. package/types/src/calendar/Calendar.d.ts +0 -13
  61. package/types/src/calendar/index.d.ts +0 -4
  62. package/types/src/calendar/internal/CalendarCarousel.d.ts +0 -3
  63. package/types/src/calendar/internal/CalendarContext.d.ts +0 -8
  64. package/types/src/calendar/internal/CalendarDay.d.ts +0 -14
  65. package/types/src/calendar/internal/CalendarMonth.d.ts +0 -10
  66. package/types/src/calendar/internal/CalendarNavigation.d.ts +0 -16
  67. package/types/src/calendar/internal/CalendarWeekHeader.d.ts +0 -3
  68. package/types/src/calendar/internal/useFocusManagement.d.ts +0 -9
  69. package/types/src/calendar/internal/utils.d.ts +0 -15
  70. package/types/src/calendar/useCalendar.d.ts +0 -54
  71. package/types/src/calendar/useCalendarDay.d.ts +0 -22
  72. package/types/src/calendar/useSelection.d.ts +0 -78
  73. package/types/src/column-picker/ColumnPicker.d.ts +0 -7
  74. package/types/src/column-picker/ColumnSearch.d.ts +0 -12
  75. package/types/src/column-picker/index.d.ts +0 -2
  76. package/types/src/column-picker/moving-window.d.ts +0 -14
  77. package/types/src/common-hooks/index.d.ts +0 -3
  78. package/types/src/common-hooks/selectionTypes.d.ts +0 -70
  79. package/types/src/common-hooks/use-resize-observer.d.ts +0 -16
  80. package/types/src/common-hooks/useControlled.d.ts +0 -24
  81. package/types/src/context-panel-provider/ContextPanelProvider.d.ts +0 -17
  82. package/types/src/cycle-state-button/CycleStateButton.d.ts +0 -11
  83. package/types/src/cycle-state-button/index.d.ts +0 -1
  84. package/types/src/date-input/DateInput.d.ts +0 -60
  85. package/types/src/date-input/index.d.ts +0 -1
  86. package/types/src/date-picker/DatePicker.d.ts +0 -66
  87. package/types/src/date-picker/DatePickerContext.d.ts +0 -19
  88. package/types/src/date-picker/DatePickerPanel.d.ts +0 -10
  89. package/types/src/date-picker/index.d.ts +0 -1
  90. package/types/src/drag-drop/DragDropProvider.d.ts +0 -38
  91. package/types/src/drag-drop/DragDropState.d.ts +0 -15
  92. package/types/src/drag-drop/Draggable.d.ts +0 -13
  93. package/types/src/drag-drop/DropIndicator.d.ts +0 -5
  94. package/types/src/drag-drop/dragDropTypes.d.ts +0 -100
  95. package/types/src/drag-drop/drop-target-utils.d.ts +0 -82
  96. package/types/src/drag-drop/index.d.ts +0 -6
  97. package/types/src/drag-drop/useAutoScroll.d.ts +0 -11
  98. package/types/src/drag-drop/useDragDisplacers.d.ts +0 -17
  99. package/types/src/drag-drop/useDragDrop.d.ts +0 -2
  100. package/types/src/drag-drop/useDragDropCopy.d.ts +0 -6
  101. package/types/src/drag-drop/useDragDropIndicator.d.ts +0 -2
  102. package/types/src/drag-drop/useDragDropNaturalMovement.d.ts +0 -2
  103. package/types/src/drag-drop/useDropIndicator.d.ts +0 -8
  104. package/types/src/drag-drop/useGlobalDragDrop.d.ts +0 -11
  105. package/types/src/drag-drop/useTransition.d.ts +0 -3
  106. package/types/src/editable/index.d.ts +0 -1
  107. package/types/src/editable/useEditableText.d.ts +0 -21
  108. package/types/src/editable-label/EditableLabel.d.ts +0 -19
  109. package/types/src/editable-label/index.d.ts +0 -1
  110. package/types/src/expando-input/ExpandoInput.d.ts +0 -6
  111. package/types/src/expando-input/index.d.ts +0 -1
  112. package/types/src/highlighter/Highlighter.d.ts +0 -6
  113. package/types/src/highlighter/index.d.ts +0 -1
  114. package/types/src/hover-overlay/HoverOverlay.d.ts +0 -8
  115. package/types/src/icon-button/Icon.d.ts +0 -6
  116. package/types/src/icon-button/IconButton.d.ts +0 -6
  117. package/types/src/icon-button/ToggleIconButton.d.ts +0 -5
  118. package/types/src/icon-button/index.d.ts +0 -3
  119. package/types/src/index.d.ts +0 -33
  120. package/types/src/instrument-picker/SearchCell.d.ts +0 -2
  121. package/types/src/instrument-picker/TablePicker.d.ts +0 -11
  122. package/types/src/instrument-picker/index.d.ts +0 -1
  123. package/types/src/instrument-picker/moving-window.d.ts +0 -14
  124. package/types/src/instrument-picker/useDataSource.d.ts +0 -6
  125. package/types/src/instrument-picker/useTablePicker.d.ts +0 -47
  126. package/types/src/item-picker/ItemPicker.d.ts +0 -14
  127. package/types/src/item-picker/useItemPicker.d.ts +0 -35
  128. package/types/src/measured-container/MeasuredContainer.d.ts +0 -16
  129. package/types/src/measured-container/index.d.ts +0 -2
  130. package/types/src/measured-container/useMeasuredContainer.d.ts +0 -23
  131. package/types/src/modal-provider/ModalProvider.d.ts +0 -13
  132. package/types/src/overflow-container/OverflowContainer.d.ts +0 -15
  133. package/types/src/overflow-container/index.d.ts +0 -2
  134. package/types/src/overflow-container/overflow-utils.d.ts +0 -48
  135. package/types/src/overflow-container/useOverflowContainer.d.ts +0 -19
  136. package/types/src/price-ticker/PriceTicker.d.ts +0 -7
  137. package/types/src/price-ticker/index.d.ts +0 -1
  138. package/types/src/prompt/Prompt.d.ts +0 -53
  139. package/types/src/sortable-list/SortableList.d.ts +0 -8
  140. package/types/src/split-button/SplitButton.d.ts +0 -11
  141. package/types/src/split-button/SplitStateButton.d.ts +0 -5
  142. package/types/src/split-button/index.d.ts +0 -2
  143. package/types/src/split-button/useSplitButton.d.ts +0 -311
  144. package/types/src/table-search/SearchCell.d.ts +0 -2
  145. package/types/src/table-search/TableSearch.d.ts +0 -9
  146. package/types/src/table-search/index.d.ts +0 -1
  147. package/types/src/table-search/moving-window.d.ts +0 -14
  148. package/types/src/table-search/useTableSearch.d.ts +0 -15
  149. package/types/src/tabs-next/TabBar.d.ts +0 -12
  150. package/types/src/tabs-next/TabListNext.d.ts +0 -12
  151. package/types/src/tabs-next/TabNext.d.ts +0 -12
  152. package/types/src/tabs-next/TabNextAction.d.ts +0 -5
  153. package/types/src/tabs-next/TabNextContext.d.ts +0 -12
  154. package/types/src/tabs-next/TabNextPanel.d.ts +0 -8
  155. package/types/src/tabs-next/TabNextTrigger.d.ts +0 -5
  156. package/types/src/tabs-next/TabOverflowList.d.ts +0 -11
  157. package/types/src/tabs-next/TabsNext.d.ts +0 -17
  158. package/types/src/tabs-next/TabsNextContext.d.ts +0 -21
  159. package/types/src/tabs-next/hooks/useCollection.d.ts +0 -18
  160. package/types/src/tabs-next/hooks/useFocusOutside.d.ts +0 -2
  161. package/types/src/tabs-next/hooks/useOverflow.d.ts +0 -11
  162. package/types/src/tabs-next/index.d.ts +0 -7
  163. package/types/src/tabstrip/Tab.d.ts +0 -27
  164. package/types/src/tabstrip/TabMenu.d.ts +0 -23
  165. package/types/src/tabstrip/TabMenuOptions.d.ts +0 -10
  166. package/types/src/tabstrip/TabsTypes.d.ts +0 -127
  167. package/types/src/tabstrip/Tabstrip.d.ts +0 -3
  168. package/types/src/tabstrip/index.d.ts +0 -5
  169. package/types/src/tabstrip/tabstrip-dom-utils.d.ts +0 -2
  170. package/types/src/tabstrip/useAnimatedSelectionThumb.d.ts +0 -7
  171. package/types/src/tabstrip/useKeyboardNavigation.d.ts +0 -29
  172. package/types/src/tabstrip/useSelection.d.ts +0 -13
  173. package/types/src/tabstrip/useTabstrip.d.ts +0 -55
  174. package/types/src/time-input/MaskedInput.d.ts +0 -49
  175. package/types/src/time-input/TimeInput.d.ts +0 -5
  176. package/types/src/time-input/useTimeInput.d.ts +0 -21
  177. package/types/src/toolbar/Toolbar.d.ts +0 -20
  178. package/types/src/toolbar/index.d.ts +0 -1
  179. package/types/src/toolbar/toolbar-dom-utils.d.ts +0 -3
  180. package/types/src/toolbar/useKeyboardNavigation.d.ts +0 -32
  181. package/types/src/toolbar/useSelection.d.ts +0 -22
  182. package/types/src/toolbar/useToolbar.d.ts +0 -28
  183. package/types/src/utils/deprecated-types.d.ts +0 -2
  184. package/types/src/utils/escapeRegExp.d.ts +0 -1
  185. package/types/src/utils/forwardCallbackProps.d.ts +0 -3
  186. package/types/src/utils/index.d.ts +0 -5
  187. package/types/src/utils/isOverflowElement.d.ts +0 -1
  188. package/types/src/utils/isPlainObject.d.ts +0 -1
  189. package/types/src/vuu-date-picker/VuuDatePicker.d.ts +0 -7
  190. package/types/src/vuu-date-picker/index.d.ts +0 -1
  191. package/types/src/vuu-input/VuuInput.d.ts +0 -18
  192. package/types/src/vuu-input/index.d.ts +0 -1
  193. package/types/src/vuu-time-picker/TimeRangePicker.d.ts +0 -0
  194. package/types/src/vuu-time-picker/VuuTimePicker.d.ts +0 -7
  195. package/types/src/vuu-typeahead-input/VuuTypeaheadInput.d.ts +0 -6
  196. package/types/src/vuu-typeahead-input/index.d.ts +0 -1
  197. package/types/src/vuu-typeahead-input/useVuuTypeaheadInput.d.ts +0 -49
@@ -1,35 +1,78 @@
1
- import { EventEmitter, createSyntheticEvent, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, updateTimeString, zeroTime, zeroTimeUnit } from "@vuu-ui/vuu-utils";
2
- const NullSelection = {
3
- end: null,
4
- start: null
5
- };
6
- const FullSelection = {
7
- end: 0,
8
- start: 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 CursorAtEnd = {
11
- end: 8,
12
- start: 8
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
- #isIncrementalChange = false;
42
+ #milliseconds;
19
43
  #selectionStart = -1;
20
44
  #selectionEnd = -1;
45
+ #units;
21
46
  #value;
22
- #unitSelected;
23
- #halfUnitSelected;
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
- this.#value = defaultValue ?? zeroTime;
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) throw Error("MaskedInput cannot be reused, create a new instance for a new input");
69
+ if (el === this.#input) return;
70
+ this.#input?.removeEventListener("change", this.emitSyntheticChangeEvent);
31
71
  this.#input = el;
32
- el.addEventListener("change", this.emitSyntheticChangeEvent);
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 newTimeString = updateTimeString(this.#value, unit, value);
76
- this.setValue(newTimeString);
134
+ const newValue = updateTimeString(this.#value, unit, value);
135
+ this.setValue(newValue);
77
136
  }
78
- getUnitValue(unit) {
79
- switch(unit){
80
- case "hours":
81
- return this.#value.slice(0, 2);
82
- case "minutes":
83
- return this.#value.slice(3, 5);
84
- case "seconds":
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
- this.#value = value;
96
- if (this.isFocused) {
97
- const isIncremental = this.#isIncrementalChange;
98
- this.#isIncrementalChange = false;
99
- requestAnimationFrame(()=>{
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, halfUnit = false) {
115
- if (this.#input) {
116
- const offset = halfUnit ? 1 : 0;
117
- if ("hours" === unit) {
118
- this.selectionStart = this.#input.selectionStart = 0 + offset;
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
- if (halfUnit) {
129
- this.#halfUnitSelected = unit;
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.#selectionStart = this.selectionEnd = 8;
139
- this.#unitSelected = void 0;
140
- this.#halfUnitSelected = void 0;
173
+ this.selectionStart = this.selectionEnd = this.length;
174
+ this.#selectedUnit = void 0;
175
+ this.#digitIndex = 0;
141
176
  }
142
177
  restoreSelection() {
143
- if (this.#unitSelected) this.select(this.#unitSelected);
178
+ if (this.#selectedUnit) this.select(this.#selectedUnit, this.#digitIndex);
144
179
  }
145
- advanceSelection() {
146
- if (this.#unitSelected) this.select(this.#unitSelected, true);
147
- else if ("hours" === this.#halfUnitSelected) this.select("minutes");
148
- else if ("minutes" === this.#halfUnitSelected) this.select("seconds");
149
- else if ("seconds" === this.#halfUnitSelected) this.select("seconds");
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
- if ("right" === direction) {
154
- if ("hours" === this.#unitSelected || "hours" === this.#halfUnitSelected) this.select("minutes");
155
- else if ("minutes" === this.#unitSelected || "minutes" === this.#halfUnitSelected) this.select("seconds");
156
- else if ("seconds" === this.#unitSelected || "seconds" === this.#halfUnitSelected) this.select("seconds");
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
- this.#halfUnitSelected = void 0;
167
- this.#unitSelected = void 0;
168
- this.#value = value;
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
- if (cursorPos >= 0 && cursorPos < 3) return "hours";
172
- if (cursorPos < 6) return "minutes";
173
- if (cursorPos <= 8) return "seconds";
174
- throw Error(`[MaskedInput] getUnitAtCursorPos invalid cursor position ${cursorPos}`);
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
- this.#isIncrementalChange = true;
179
- const unit = this.getUnitAtCursorPos();
180
- const unitValue = this.getUnitValue(unit);
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
- this.#isIncrementalChange = true;
189
- const unit = this.getUnitAtCursorPos();
190
- const unitValue = this.getUnitValue(unit);
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 { cursorPos } = this;
199
- if ("seconds" === this.#unitSelected) {
200
- this.clear("seconds");
201
- this.select("minutes");
202
- } else if ("minutes" === this.#unitSelected) {
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 { cursorPos } = this;
223
- if (cursorPos < 8) {
224
- const originalValue = this.#value;
225
- const newValue = this.#value.split("").toSpliced(cursorPos, 1, key).join("");
226
- this.setValue(newValue);
227
- if (isValidTimeString(newValue)) {
228
- this.#input.classList.remove("invalid");
229
- if (this.#controlled && newValue === originalValue) this.advanceSelection();
230
- else if (!this.#controlled) this.advanceSelection();
231
- } else if (this.#unitSelected) {
232
- this.select(this.#unitSelected);
233
- this.#input.classList.add("invalid");
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
- if (null === selectionEnd || null === selectionStart) return NullSelection;
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("[MasketInput] selection referenced, but no input");
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 selection = this.getSelection();
255
- if (selection === NullSelection) this.select("hours");
256
- else if (selection === FullSelection) ;
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, selectionEnd } = this.#input;
266
- if (null === selectionStart || null === selectionEnd) ;
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.select("hours");
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
- .salt-density-high .vuuTimeInput {
3
- --time-input-width: 60px;
4
- }
5
-
6
- .salt-density-medium .vuuTimeInput {
7
- --time-input-width: 70px;
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
- .salt-density-low .vuuTimeInput {
11
- --time-input-width: 80px;
12
- }
24
+ &:before {
25
+ content: attr(data-placeholder);
26
+ }
13
27
 
14
- .salt-density-touch .vuuTimeInput {
15
- --time-input-width: 90px;
28
+ &:after {
29
+ content: attr(data-sample-value);
30
+ }
16
31
  }
17
32
 
18
33
  .vuuTimeInput {
19
34
  background: var(--vuuTimeInput-background, inherit);
20
- font-family: var(--salt-typography-fontFamily);
21
- width: var(--vuuTimeInput-width, var(--time-input-width));
22
- border-width: 1px;
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
- .invalid {
34
- border-color: red;
55
+ &.vuuTimeInput-invalid {
56
+ border-color: var(--vuuTimeInput-invalid-borderColor, var(--salt-status-error-borderColor, red));
57
+ }
35
58
  }
36
59
 
37
60
 
@@ -1,12 +1,14 @@
1
- import { createElement, forwardRef } from "react";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import { useForkRef } from "@salt-ds/core";
3
3
  import { useComponentCssInjection } from "@salt-ds/styles";
4
4
  import { useWindow } from "@salt-ds/window";
5
+ import { normaliseTimeString } from "@vuu-ui/vuu-utils";
5
6
  import clsx from "clsx";
7
+ import { forwardRef } from "react";
6
8
  import { useTimeInput } from "./useTimeInput.js";
7
9
  import TimeInput_0 from "./TimeInput.css";
8
10
  const classBase = "vuuTimeInput";
9
- const TimeInput_TimeInput = /*#__PURE__*/ forwardRef(function({ className, date, defaultValue, onChange, onCommit, placeholder = "hh:mm:ss", value, ...htmlAttributes }, ref) {
11
+ const TimeInput_TimeInput = /*#__PURE__*/ forwardRef(function({ className, date, defaultValue, milliseconds = false, onChange, onCommit, placeholder = milliseconds ? "hh:mm:ss.sss" : "hh:mm:ss", value, ...htmlAttributes }, ref) {
10
12
  const targetWindow = useWindow();
11
13
  useComponentCssInjection({
12
14
  testId: "vuu-time-input",
@@ -16,22 +18,31 @@ const TimeInput_TimeInput = /*#__PURE__*/ forwardRef(function({ className, date,
16
18
  const { inputRef, eventHandlers } = useTimeInput({
17
19
  date,
18
20
  defaultValue,
21
+ milliseconds,
19
22
  onChange,
20
23
  onCommit,
21
24
  value
22
25
  });
23
- return /*#__PURE__*/ createElement("input", {
24
- ...htmlAttributes,
25
- ...eventHandlers,
26
- "aria-placeholder": placeholder,
27
- className: clsx(classBase, className),
28
- defaultValue: defaultValue,
29
- key: defaultValue,
30
- placeholder: placeholder,
31
- readOnly: true,
32
- ref: useForkRef(ref, inputRef),
33
- spellCheck: "false",
34
- value: value
26
+ const displayValue = void 0 === value ? void 0 : normaliseTimeString(value, milliseconds) ?? value;
27
+ const displayDefaultValue = void 0 === value && void 0 !== defaultValue ? normaliseTimeString(defaultValue, milliseconds) ?? defaultValue : void 0;
28
+ return /*#__PURE__*/ jsx("span", {
29
+ className: `${classBase}-container`,
30
+ "data-placeholder": placeholder,
31
+ "data-sample-value": milliseconds ? "00:00:00.000" : "00:00:00",
32
+ children: /*#__PURE__*/ jsx("input", {
33
+ ...htmlAttributes,
34
+ ...eventHandlers,
35
+ "aria-placeholder": placeholder,
36
+ className: clsx(classBase, className, {
37
+ [`${classBase}-milliseconds`]: milliseconds
38
+ }),
39
+ defaultValue: displayDefaultValue,
40
+ placeholder: placeholder,
41
+ readOnly: true,
42
+ ref: useForkRef(ref, inputRef),
43
+ spellCheck: "false",
44
+ value: displayValue
45
+ })
35
46
  });
36
47
  });
37
48
  export { TimeInput_TimeInput as TimeInput };