@astryxdesign/core 0.7.8-canary.ffd29c5 → 0.7.8

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 (189) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/dist/BottomSheet/BottomSheetPanel.d.ts +1 -1
  3. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  4. package/dist/BottomSheet/BottomSheetPanel.js +30 -14
  5. package/dist/BottomSheet/bottomSheetStack.js +1 -1
  6. package/dist/BottomSheet/useSheetGestures.d.ts +3 -1
  7. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  8. package/dist/BottomSheet/useSheetGestures.js +4 -0
  9. package/dist/CheckboxList/CheckboxList.d.ts +13 -2
  10. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  11. package/dist/CheckboxList/CheckboxList.js +10 -4
  12. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  13. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  14. package/dist/CheckboxList/CheckboxListContext.js +2 -1
  15. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  16. package/dist/CheckboxList/CheckboxListItem.js +14 -2
  17. package/dist/CheckboxList/requiredGroup.d.ts +22 -0
  18. package/dist/CheckboxList/requiredGroup.d.ts.map +1 -0
  19. package/dist/CheckboxList/requiredGroup.js +52 -0
  20. package/dist/DateInput/PickerWheel.d.ts +1 -1
  21. package/dist/DateInput/PickerWheel.d.ts.map +1 -1
  22. package/dist/DateInput/PickerWheel.js +27 -6
  23. package/dist/DateInput/Wheel.d.ts +2 -2
  24. package/dist/DateInput/Wheel.d.ts.map +1 -1
  25. package/dist/DateInput/Wheel.js +20 -6
  26. package/dist/DateInput/datePickerTouch.d.ts +13 -0
  27. package/dist/DateInput/datePickerTouch.d.ts.map +1 -0
  28. package/dist/DateInput/datePickerTouch.js +14 -0
  29. package/dist/DateInput/wheelOptions.d.ts +22 -2
  30. package/dist/DateInput/wheelOptions.d.ts.map +1 -1
  31. package/dist/DateInput/wheelOptions.js +14 -2
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +62 -119
  34. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  35. package/dist/HoverCard/useHoverCard.js +6 -1
  36. package/dist/Overlay/Overlay.d.ts.map +1 -1
  37. package/dist/Overlay/Overlay.js +7 -2
  38. package/dist/PowerSearch/PowerSearch.d.ts +4 -2
  39. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  40. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts +24 -1
  41. package/dist/PowerSearch/PowerSearchBottomSheet.d.ts.map +1 -1
  42. package/dist/PowerSearch/PowerSearchBottomSheet.js +172 -50
  43. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  44. package/dist/Resizable/ResizeHandle.js +15 -15
  45. package/dist/Resizable/createResizable.d.ts +70 -16
  46. package/dist/Resizable/createResizable.d.ts.map +1 -1
  47. package/dist/Resizable/createResizable.js +180 -22
  48. package/dist/Resizable/separatorAttributes.d.ts +45 -0
  49. package/dist/Resizable/separatorAttributes.d.ts.map +1 -0
  50. package/dist/Resizable/separatorAttributes.js +35 -0
  51. package/dist/Resizable/useResizable.js +2 -2
  52. package/dist/TimeInput/TimeWheels.d.ts +32 -0
  53. package/dist/TimeInput/TimeWheels.d.ts.map +1 -0
  54. package/dist/TimeInput/TimeWheels.js +144 -0
  55. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  56. package/dist/TimeInput/TouchTimeField.js +63 -118
  57. package/dist/TimeInput/timeWheelRules.d.ts +124 -0
  58. package/dist/TimeInput/timeWheelRules.d.ts.map +1 -0
  59. package/dist/TimeInput/timeWheelRules.js +277 -0
  60. package/dist/Tokenizer/TokenizerBottomSheet.js +8 -12
  61. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  62. package/dist/Tooltip/useTooltip.js +6 -0
  63. package/dist/astryx.css +3 -1
  64. package/dist/gestures/GestureHost.d.ts.map +1 -1
  65. package/dist/gestures/GestureHost.js +3 -0
  66. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.d.ts.map +1 -1
  67. package/dist/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.js +5 -2
  68. package/dist/i18n/generated-english/Resizable/createResizable.generated.d.ts +8 -0
  69. package/dist/i18n/generated-english/Resizable/createResizable.generated.d.ts.map +1 -0
  70. package/dist/i18n/generated-english/Resizable/createResizable.generated.js +13 -0
  71. package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.d.ts +8 -0
  72. package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.d.ts.map +1 -0
  73. package/dist/i18n/generated-english/TimeInput/TimeWheels.generated.js +17 -0
  74. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  75. package/dist/i18n/generated-locales/en.generated.js +5 -2
  76. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  77. package/dist/i18n/generated-locales/pseudo.generated.js +5 -2
  78. package/dist/keyboard/index.d.ts +17 -0
  79. package/dist/keyboard/index.d.ts.map +1 -0
  80. package/dist/keyboard/index.js +19 -0
  81. package/dist/keyboard/keyboardHeight.d.ts +149 -0
  82. package/dist/keyboard/keyboardHeight.d.ts.map +1 -0
  83. package/dist/keyboard/keyboardHeight.js +432 -0
  84. package/dist/keyboard/keyboardLayoutGuide.d.ts +59 -0
  85. package/dist/keyboard/keyboardLayoutGuide.d.ts.map +1 -0
  86. package/dist/keyboard/keyboardLayoutGuide.js +207 -0
  87. package/dist/keyboard/keyboardModel.d.ts +93 -0
  88. package/dist/keyboard/keyboardModel.d.ts.map +1 -0
  89. package/dist/keyboard/keyboardModel.js +220 -0
  90. package/dist/keyboard/rideKeyboard.d.ts +42 -0
  91. package/dist/keyboard/rideKeyboard.d.ts.map +1 -0
  92. package/dist/keyboard/rideKeyboard.js +77 -0
  93. package/dist/keyboard/useMobileKeyboard.d.ts +61 -0
  94. package/dist/keyboard/useMobileKeyboard.d.ts.map +1 -0
  95. package/dist/keyboard/useMobileKeyboard.js +699 -0
  96. package/dist/motion/android/index.d.ts +16 -0
  97. package/dist/motion/android/index.d.ts.map +1 -0
  98. package/dist/motion/android/index.js +18 -0
  99. package/dist/motion/android/keyboard.d.ts +83 -0
  100. package/dist/motion/android/keyboard.d.ts.map +1 -0
  101. package/dist/motion/android/keyboard.js +96 -0
  102. package/dist/motion/uikit/datePicker.d.ts +58 -0
  103. package/dist/motion/uikit/datePicker.d.ts.map +1 -0
  104. package/dist/motion/uikit/datePicker.js +60 -0
  105. package/dist/motion/uikit/index.d.ts +4 -3
  106. package/dist/motion/uikit/index.d.ts.map +1 -1
  107. package/dist/motion/uikit/index.js +4 -3
  108. package/dist/utils/interactionOverlay.stylex.d.ts +3 -2
  109. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  110. package/dist/utils/interactionOverlay.stylex.js +3 -2
  111. package/dist/utils/layoutViewport.d.ts +25 -0
  112. package/dist/utils/layoutViewport.d.ts.map +1 -0
  113. package/dist/utils/layoutViewport.js +102 -0
  114. package/dist/utils/pressFeedback.d.ts.map +1 -1
  115. package/dist/utils/pressFeedback.js +74 -9
  116. package/dist/utils/pressGesture.d.ts +4 -2
  117. package/dist/utils/pressGesture.d.ts.map +1 -1
  118. package/dist/utils/pressGesture.js +3 -1
  119. package/dist/utils/selectionLock.d.ts +10 -1
  120. package/dist/utils/selectionLock.d.ts.map +1 -1
  121. package/dist/utils/selectionLock.js +31 -5
  122. package/dist/utils/sharedMutationObserver.d.ts +2 -1
  123. package/dist/utils/sharedMutationObserver.d.ts.map +1 -1
  124. package/dist/utils/sharedMutationObserver.js +2 -1
  125. package/dist/utils/timeParser.d.ts +2 -1
  126. package/dist/utils/timeParser.d.ts.map +1 -1
  127. package/dist/utils/timeParser.js +29 -3
  128. package/locales/en.json +21 -9
  129. package/locales/pseudo.json +15 -6
  130. package/package.json +5 -5
  131. package/src/BottomSheet/BottomSheet.doc.mjs +1 -1
  132. package/src/BottomSheet/BottomSheetPanel.tsx +41 -13
  133. package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +5 -0
  134. package/src/BottomSheet/bottomSheetStack.ts +1 -1
  135. package/src/BottomSheet/useSheetGestures.ts +6 -0
  136. package/src/CheckboxList/CheckboxList.doc.mjs +7 -0
  137. package/src/CheckboxList/CheckboxList.tsx +34 -2
  138. package/src/CheckboxList/CheckboxListContext.tsx +9 -1
  139. package/src/CheckboxList/CheckboxListItem.tsx +23 -2
  140. package/src/CheckboxList/requiredGroup.ts +77 -0
  141. package/src/DateInput/PickerWheel.tsx +37 -4
  142. package/src/DateInput/Wheel.tsx +30 -3
  143. package/src/DateInput/datePickerTouch.ts +15 -0
  144. package/src/DateInput/wheelOptions.ts +31 -2
  145. package/src/DateTimeInput/TouchDateTimeField.tsx +94 -178
  146. package/src/HoverCard/useHoverCard.tsx +6 -1
  147. package/src/Overlay/Overlay.tsx +7 -2
  148. package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
  149. package/src/PowerSearch/PowerSearch.tsx +4 -2
  150. package/src/PowerSearch/PowerSearchBottomSheet.tsx +237 -62
  151. package/src/Resizable/ResizeHandle.tsx +18 -26
  152. package/src/Resizable/createResizable.ts +242 -27
  153. package/src/Resizable/separatorAttributes.ts +73 -0
  154. package/src/Resizable/useResizable.ts +2 -2
  155. package/src/TimeInput/TimeWheels.tsx +218 -0
  156. package/src/TimeInput/TouchTimeField.tsx +93 -157
  157. package/src/TimeInput/timeWheelRules.ts +352 -0
  158. package/src/Tokenizer/TokenizerBottomSheet.tsx +8 -12
  159. package/src/Tooltip/useTooltip.tsx +6 -0
  160. package/src/gestures/GestureHost.ts +5 -4
  161. package/src/i18n/generated-english/PowerSearch/PowerSearchBottomSheet.generated.ts +5 -2
  162. package/src/i18n/generated-english/Resizable/createResizable.generated.ts +14 -0
  163. package/src/i18n/generated-english/TimeInput/TimeWheels.generated.ts +18 -0
  164. package/src/i18n/generated-locales/en.generated.ts +6 -4
  165. package/src/i18n/generated-locales/pseudo.generated.ts +6 -4
  166. package/src/keyboard/README.md +197 -0
  167. package/src/keyboard/index.ts +48 -0
  168. package/src/keyboard/keyboardHeight.ts +543 -0
  169. package/src/keyboard/keyboardLayoutGuide.ts +266 -0
  170. package/src/keyboard/keyboardModel.ts +301 -0
  171. package/src/keyboard/rideKeyboard.ts +100 -0
  172. package/src/keyboard/useMobileKeyboard.ts +866 -0
  173. package/src/motion/MEASUREMENTS.md +10 -0
  174. package/src/motion/README.md +2 -0
  175. package/src/motion/android/index.ts +18 -0
  176. package/src/motion/android/keyboard.ts +96 -0
  177. package/src/motion/uikit/datePicker.ts +60 -0
  178. package/src/motion/uikit/index.ts +4 -3
  179. package/src/utils/interactionOverlay.stylex.ts +3 -2
  180. package/src/utils/layoutViewport.ts +104 -0
  181. package/src/utils/pressFeedback.ts +99 -21
  182. package/src/utils/pressGesture.ts +4 -2
  183. package/src/utils/selectionLock.ts +40 -6
  184. package/src/utils/sharedMutationObserver.ts +2 -1
  185. package/src/utils/timeParser.ts +40 -3
  186. package/dist/BottomSheet/useMobileKeyboard.d.ts +0 -21
  187. package/dist/BottomSheet/useMobileKeyboard.d.ts.map +0 -1
  188. package/dist/BottomSheet/useMobileKeyboard.js +0 -413
  189. package/src/BottomSheet/useMobileKeyboard.ts +0 -542
@@ -0,0 +1,144 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file TimeWheels.tsx
7
+ * @input A time of day, the hour format, the bounds and a commit callback
8
+ * @output Exports TimeWheels: UIDatePicker's `.time` wheels as one picker
9
+ * @position Internal component; the wheels under a finger in the TimeInput
10
+ * sheet and DateTimeInput's time panel. timeWheelRules.ts holds the rules.
11
+ *
12
+ * A column that moves by itself turns on the Wheel's own spin (`spinTo`). The
13
+ * spin starts before the commit, so the new value finds the column moving.
14
+ *
15
+ * SYNC: When modified, update TimeWheels.test.tsx, timeWheelRules.ts,
16
+ * TimeInput.doc.mjs and DateTimeInput.doc.mjs.
17
+ */
18
+ import { useMemo, useRef } from 'react';
19
+ import { Wheel } from "../DateInput/Wheel.js";
20
+ import { columnValue, columnsToTurn, displayedTime, restingTime, hourRows, meridiemOf, meridiemRows, minuteRows, secondRows, turnColumn, wheelBounds } from "./timeWheelRules.js";
21
+ import { registerEnglishFallback, useComponentTranslator } from "../i18n/useComponentTranslator.js";
22
+ import english from "../i18n/generated-english/TimeInput/TimeWheels.generated.js";
23
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
24
+ registerEnglishFallback(english);
25
+ function sameTime(a, b) {
26
+ return a.hour === b.hour && a.minute === b.minute && a.second === b.second;
27
+ }
28
+
29
+ /**
30
+ * UIDatePicker's time wheels.
31
+ *
32
+ * @example
33
+ * ```
34
+ * <TimeWheels time={{hour: 11, minute: 58, second: 0}} hourFormat="12h"
35
+ * hasSeconds={false} isActive onCommit={setTime} />
36
+ * ```
37
+ */
38
+ export function TimeWheels({
39
+ time,
40
+ hourFormat,
41
+ hasSeconds,
42
+ min,
43
+ max,
44
+ isActive,
45
+ onCommit
46
+ }) {
47
+ const t = useComponentTranslator();
48
+ const bounds = useMemo(() => wheelBounds(min, max), [min, max]);
49
+ const shown = displayedTime(time, hasSeconds);
50
+ const hourOptions = useMemo(() => hourRows(shown.hour, hourFormat, bounds), [shown.hour, hourFormat, bounds]);
51
+ const minuteOptions = useMemo(() => minuteRows(shown.hour, hasSeconds, bounds), [shown.hour, hasSeconds, bounds]);
52
+ const secondOptions = useMemo(() => secondRows(shown.hour, shown.minute, bounds), [shown.hour, shown.minute, bounds]);
53
+ const meridiemOptions = useMemo(() => meridiemRows({
54
+ am: t('@astryx.dateTimeInput.meridiemAM'),
55
+ pm: t('@astryx.dateTimeInput.meridiemPM')
56
+ }, bounds), [t, bounds]);
57
+ const hourRef = useRef(null);
58
+ const minuteRef = useRef(null);
59
+ const secondRef = useRef(null);
60
+ const meridiemRef = useRef(null);
61
+ const rows = {
62
+ hour: hourOptions,
63
+ minute: minuteOptions,
64
+ second: secondOptions,
65
+ meridiem: meridiemOptions
66
+ };
67
+ // The rows the columns turn onto by themselves (`spinTo`), each with the id of
68
+ // its turn. A turn's arrival is not a person's rest, so it commits nothing,
69
+ // even when the owner kept the old time.
70
+ const spinsRef = useRef({});
71
+ const nextSpinIdRef = useRef(0);
72
+ const restOn = column => value => {
73
+ if (spinsRef.current[column]?.value === value) {
74
+ return;
75
+ }
76
+ const from = shown;
77
+ // What commits is the row the wheels come to rest on: past a bound, the
78
+ // nearest time inside it that has a row (datePicker.wheels.restPastBound).
79
+ const next = restingTime(turnColumn(from, column, value, hourFormat), hasSeconds, bounds);
80
+ const handles = {
81
+ hour: hourRef.current,
82
+ minute: minuteRef.current,
83
+ second: secondRef.current,
84
+ meridiem: meridiemRef.current
85
+ };
86
+ // Turn the other columns first, so the commit's new value finds them
87
+ // already moving (a Wheel never parks a moving column).
88
+ for (const turn of columnsToTurn(from, column, value, next, {
89
+ hourFormat,
90
+ hasSeconds
91
+ })) {
92
+ const index = rows[turn.column].findIndex(row => row.value === turn.value);
93
+ const handle = handles[turn.column];
94
+ if (index >= 0 && handle != null) {
95
+ nextSpinIdRef.current += 1;
96
+ const id = nextSpinIdRef.current;
97
+ spinsRef.current[turn.column] = {
98
+ value: turn.value,
99
+ id
100
+ };
101
+ void handle.spinTo(index).finally(() => {
102
+ if (spinsRef.current[turn.column]?.id === id) {
103
+ spinsRef.current[turn.column] = undefined;
104
+ }
105
+ });
106
+ }
107
+ }
108
+ if (!sameTime(next, from)) {
109
+ onCommit(next);
110
+ }
111
+ };
112
+ return /*#__PURE__*/_jsxs(_Fragment, {
113
+ children: [/*#__PURE__*/_jsx(Wheel, {
114
+ ref: hourRef,
115
+ label: t('@astryx.dateTimeInput.hourWheel'),
116
+ options: hourOptions,
117
+ value: columnValue(shown, 'hour', hourFormat),
118
+ isActive: isActive,
119
+ onChange: restOn('hour')
120
+ }), /*#__PURE__*/_jsx(Wheel, {
121
+ ref: minuteRef,
122
+ label: t('@astryx.dateTimeInput.minuteWheel'),
123
+ options: minuteOptions,
124
+ value: shown.minute,
125
+ isActive: isActive,
126
+ onChange: restOn('minute')
127
+ }), hasSeconds && /*#__PURE__*/_jsx(Wheel, {
128
+ ref: secondRef,
129
+ label: t('@astryx.dateTimeInput.secondWheel'),
130
+ options: secondOptions,
131
+ value: shown.second,
132
+ isActive: isActive,
133
+ onChange: restOn('second')
134
+ }), hourFormat === '12h' && /*#__PURE__*/_jsx(Wheel, {
135
+ ref: meridiemRef,
136
+ label: t('@astryx.dateTimeInput.meridiemWheel'),
137
+ options: meridiemOptions,
138
+ value: meridiemOf(shown.hour),
139
+ isActive: isActive,
140
+ onChange: restOn('meridiem')
141
+ })]
142
+ });
143
+ }
144
+ TimeWheels.displayName = 'TimeWheels';
@@ -1 +1 @@
1
- {"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"AAqEA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAiHhD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAe,EACf,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,GAAG,EACH,GAAG,EACH,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAGlB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,aAAa,EAC3B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EACN,aAA0B,EAC1B,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,GAAG,SAAS,EACb,EAAE,cAAc,+BA4ZhB;yBA/be,cAAc"}
1
+ {"version":3,"file":"TouchTimeField.d.ts","sourceRoot":"","sources":["../../src/TimeInput/TouchTimeField.tsx"],"names":[],"mappings":"AAsEA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAoFhD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,aAAqB,EACrB,WAAW,EACX,UAAkB,EAClB,UAAkB,EAClB,UAAkB,EAClB,eAAe,EACf,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAiB,EACjB,GAAG,EACH,GAAG,EACH,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAGlB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,aAAa,EAC3B,WAAW,EAAE,oBAAoB,EACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EACN,aAA0B,EAC1B,YAAY,EACZ,KAAK,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,GAAG,EACH,GAAG,SAAS,EACb,EAAE,cAAc,+BAwXhB;yBA3Ze,cAAc"}
@@ -4,11 +4,11 @@
4
4
 
5
5
  /**
6
6
  * @file TouchTimeField.tsx
7
- * @input Uses React, Field, BottomSheet, Wheel, DateInput touch geometry
8
- * tokens, and shared time utilities
7
+ * @input Uses React, Field, BottomSheet, the wheels (TimeWheels under a finger,
8
+ * Wheel with a mouse), DateInput touch geometry tokens and time utilities
9
9
  * @output Exports TouchTimeField — the bottom-sheet surface behind TimeInput
10
10
  * @position Internal component; consumed by TimeInput.tsx for `presentation`
11
- * sheet surfaces (`spec:AST-043` DEC-3: the time half of DateTimeInput's sheet)
11
+ * sheet surfaces
12
12
  *
13
13
  * SYNC: When modified, update these files to stay in sync:
14
14
  * - /packages/core/src/TimeInput/TimeInput.tsx
@@ -38,15 +38,18 @@ import { useSize } from "../SizeContext/SizeContext.js";
38
38
  import { Spinner } from "../Spinner/index.js";
39
39
  import { useTooltip } from "../Tooltip/index.js";
40
40
  import { Wheel } from "../DateInput/Wheel.js";
41
+ import { useTouchPresentation } from "../utils/touchPresentation.js";
41
42
  import "../DateInput/tokens.stylex.js";
42
43
  import { dateInputTouchGeometry } from "../DateInput/tokens.stylex.js";
43
44
  import { focusOutlineStyles } from "../utils/focusOutline.stylex.js";
44
45
  import { themeProps } from "../utils/themeProps.js";
45
46
  import { clampTime, formatISOTime, getInputARIA, isTimeInRange, mergeProps, parseISOTime } from "../utils/index.js";
46
47
  import { chainEventHandlers } from "../utils/chainEventHandlers.js";
48
+ import { TimeWheels } from "../DateInput/datePickerTouch.js";
49
+ import { hour12From24, hour24From12, hourRowsInHalf, meridiemRows, minuteRows, secondRows, wheelBounds } from "./timeWheelRules.js";
47
50
  import { registerEnglishFallback, useComponentTranslator } from "../i18n/useComponentTranslator.js";
48
51
  import english from "../i18n/generated-english/TimeInput/TouchTimeField.generated.js";
49
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
52
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
50
53
  registerEnglishFallback(english);
51
54
  const sizeStyles = {
52
55
  sm: {
@@ -80,30 +83,6 @@ const styles = {
80
83
  $$css: true
81
84
  }
82
85
  };
83
- function twoDigits(value) {
84
- return String(value).padStart(2, '0');
85
- }
86
- function hour12From24(hour) {
87
- return hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
88
- }
89
- function hour24From12(hour, meridiem) {
90
- if (meridiem === 0) {
91
- return hour === 12 ? 0 : hour;
92
- }
93
- return hour === 12 ? 12 : hour + 12;
94
- }
95
- function timeToSeconds(time) {
96
- if (time == null) {
97
- return null;
98
- }
99
- const parsed = parseISOTime(time);
100
- return parsed == null ? null : parsed.hour * 3600 + parsed.minute * 60 + parsed.second;
101
- }
102
- function rangeOverlaps(startSecond, endSecond, min, max) {
103
- const minSecond = timeToSeconds(min);
104
- const maxSecond = timeToSeconds(max);
105
- return (minSecond == null || endSecond >= minSecond) && (maxSecond == null || startSecond <= maxSecond);
106
- }
107
86
 
108
87
  /**
109
88
  * The Astryx bottom-sheet time surface: a read-only closed field that opens
@@ -179,6 +158,9 @@ export function TouchTimeField({
179
158
  const mergedInputRef = useMergedRefs(ref, inputRef);
180
159
  const inputGroup = useInputGroup();
181
160
  const [isSheetOpen, setIsSheetOpen] = useState(false);
161
+ const {
162
+ isTouch
163
+ } = useTouchPresentation();
182
164
  const [, startTransition] = useTransition();
183
165
  const [optimisticValue, setOptimisticValue] = useOptimistic(value);
184
166
  const isBusy = isLoading || optimisticValue !== value;
@@ -254,61 +236,14 @@ export function TouchTimeField({
254
236
  fireChange(next);
255
237
  }
256
238
  }, [fireChange, hasSeconds, max, min, optimisticValue]);
257
- const hourOptions = useMemo(() => {
258
- if (hourFormat === '24h') {
259
- return Array.from({
260
- length: 24
261
- }, (_, hour) => ({
262
- value: hour,
263
- label: twoDigits(hour),
264
- isDisabled: !rangeOverlaps(hour * 3600, hour * 3600 + 3599, min, max)
265
- }));
266
- }
267
- const meridiem = parsedWheelTime.hour < 12 ? 0 : 1;
268
- return Array.from({
269
- length: 12
270
- }, (_, index) => {
271
- const hour = index + 1;
272
- const hour24 = hour24From12(hour, meridiem);
273
- return {
274
- value: hour,
275
- label: String(hour),
276
- // Keys step in time order: 12 PM comes before 1 PM.
277
- order: hour24,
278
- isDisabled: !rangeOverlaps(hour24 * 3600, hour24 * 3600 + 3599, min, max)
279
- };
280
- });
281
- }, [hourFormat, max, min, parsedWheelTime.hour]);
282
- const minuteOptions = useMemo(() => Array.from({
283
- length: 60
284
- }, (_, minute) => {
285
- const start = parsedWheelTime.hour * 3600 + minute * 60;
286
- return {
287
- value: minute,
288
- label: twoDigits(minute),
289
- isDisabled: !rangeOverlaps(start, start + (hasSeconds ? 59 : 0), min, max)
290
- };
291
- }), [hasSeconds, max, min, parsedWheelTime.hour]);
292
- const secondOptions = useMemo(() => Array.from({
293
- length: 60
294
- }, (_, second) => ({
295
- value: second,
296
- label: twoDigits(second),
297
- isDisabled: !isTimeInRange(formatISOTime({
298
- hour: parsedWheelTime.hour,
299
- minute: parsedWheelTime.minute,
300
- second
301
- }, true), min, max)
302
- })), [max, min, parsedWheelTime.hour, parsedWheelTime.minute]);
303
- const meridiemOptions = useMemo(() => [{
304
- value: 0,
305
- label: t('@astryx.dateTimeInput.meridiemAM'),
306
- isDisabled: !rangeOverlaps(0, 11 * 3600 + 3599, min, max)
307
- }, {
308
- value: 1,
309
- label: t('@astryx.dateTimeInput.meridiemPM'),
310
- isDisabled: !rangeOverlaps(12 * 3600, 23 * 3600 + 3599, min, max)
311
- }], [max, min, t]);
239
+ const bounds = useMemo(() => wheelBounds(min, max), [min, max]);
240
+ const hourOptions = useMemo(() => hourRowsInHalf(parsedWheelTime.hour, hourFormat, bounds), [bounds, hourFormat, parsedWheelTime.hour]);
241
+ const minuteOptions = useMemo(() => minuteRows(parsedWheelTime.hour, hasSeconds, bounds), [bounds, hasSeconds, parsedWheelTime.hour]);
242
+ const secondOptions = useMemo(() => secondRows(parsedWheelTime.hour, parsedWheelTime.minute, bounds), [bounds, parsedWheelTime.hour, parsedWheelTime.minute]);
243
+ const meridiemOptions = useMemo(() => meridiemRows({
244
+ am: t('@astryx.dateTimeInput.meridiemAM'),
245
+ pm: t('@astryx.dateTimeInput.meridiemPM')
246
+ }, bounds), [bounds, t]);
312
247
  const openSheet = useCallback(() => {
313
248
  if (!isEffectivelyDisabled) {
314
249
  setIsSheetOpen(true);
@@ -391,45 +326,55 @@ export function TouchTimeField({
391
326
  ...{
392
327
  className: "x9f619 x78zum5 xdt5ytf xh8yej3 xeuugli x1txdalj x1pzlopt x1adxfkp x1awphl8"
393
328
  },
394
- children: [/*#__PURE__*/_jsxs("div", {
329
+ children: [/*#__PURE__*/_jsx("div", {
395
330
  ...{
396
331
  className: "x78zum5 xh8yej3 xeuugli xm8gem x1txdalj"
397
332
  },
398
- children: [/*#__PURE__*/_jsx(Wheel, {
399
- label: t('@astryx.dateTimeInput.hourWheel'),
400
- options: hourOptions,
401
- value: hourFormat === '24h' ? parsedWheelTime.hour : hour12From24(parsedWheelTime.hour),
402
- isActive: isSheetOpen,
403
- onChange: hour => commitWheelTime(hourFormat === '24h' ? composeWheelTime({
404
- hour24: hour
405
- }) : composeWheelTime({
406
- hour12: hour
407
- }))
408
- }), /*#__PURE__*/_jsx(Wheel, {
409
- label: t('@astryx.dateTimeInput.minuteWheel'),
410
- options: minuteOptions,
411
- value: parsedWheelTime.minute,
333
+ children: isTouch ? /*#__PURE__*/_jsx(TimeWheels, {
334
+ time: parsedWheelTime,
335
+ hourFormat: hourFormat,
336
+ hasSeconds: hasSeconds,
337
+ min: min,
338
+ max: max,
412
339
  isActive: isSheetOpen,
413
- onChange: minute => commitWheelTime(composeWheelTime({
414
- minute
415
- }))
416
- }), hasSeconds && /*#__PURE__*/_jsx(Wheel, {
417
- label: t('@astryx.dateTimeInput.secondWheel'),
418
- options: secondOptions,
419
- value: parsedWheelTime.second,
420
- isActive: isSheetOpen,
421
- onChange: second => commitWheelTime(composeWheelTime({
422
- second
423
- }))
424
- }), hourFormat === '12h' && /*#__PURE__*/_jsx(Wheel, {
425
- label: t('@astryx.dateTimeInput.meridiemWheel'),
426
- options: meridiemOptions,
427
- value: parsedWheelTime.hour < 12 ? 0 : 1,
428
- isActive: isSheetOpen,
429
- onChange: meridiem => commitWheelTime(composeWheelTime({
430
- meridiem
431
- }))
432
- })]
340
+ onCommit: time => commitWheelTime(formatISOTime(time, hasSeconds))
341
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
342
+ children: [/*#__PURE__*/_jsx(Wheel, {
343
+ label: t('@astryx.dateTimeInput.hourWheel'),
344
+ options: hourOptions,
345
+ value: hourFormat === '24h' ? parsedWheelTime.hour : hour12From24(parsedWheelTime.hour),
346
+ isActive: isSheetOpen,
347
+ onChange: hour => commitWheelTime(hourFormat === '24h' ? composeWheelTime({
348
+ hour24: hour
349
+ }) : composeWheelTime({
350
+ hour12: hour
351
+ }))
352
+ }), /*#__PURE__*/_jsx(Wheel, {
353
+ label: t('@astryx.dateTimeInput.minuteWheel'),
354
+ options: minuteOptions,
355
+ value: parsedWheelTime.minute,
356
+ isActive: isSheetOpen,
357
+ onChange: minute => commitWheelTime(composeWheelTime({
358
+ minute
359
+ }))
360
+ }), hasSeconds && /*#__PURE__*/_jsx(Wheel, {
361
+ label: t('@astryx.dateTimeInput.secondWheel'),
362
+ options: secondOptions,
363
+ value: parsedWheelTime.second,
364
+ isActive: isSheetOpen,
365
+ onChange: second => commitWheelTime(composeWheelTime({
366
+ second
367
+ }))
368
+ }), hourFormat === '12h' && /*#__PURE__*/_jsx(Wheel, {
369
+ label: t('@astryx.dateTimeInput.meridiemWheel'),
370
+ options: meridiemOptions,
371
+ value: parsedWheelTime.hour < 12 ? 0 : 1,
372
+ isActive: isSheetOpen,
373
+ onChange: meridiem => commitWheelTime(composeWheelTime({
374
+ meridiem
375
+ }))
376
+ })]
377
+ })
433
378
  }), /*#__PURE__*/_jsx("div", {
434
379
  ...{
435
380
  className: "x78zum5 xr1yuqi xfsso4q"
@@ -0,0 +1,124 @@
1
+ /**
2
+ * @file timeWheelRules.ts
3
+ * @input A time of day, the hour format and the bounds
4
+ * @output Pure rules of UIDatePicker's time wheels: the columns' rows, the
5
+ * time a turned column makes, and which other columns turn with it
6
+ * @position Internal helper of the TimeInput and DateTimeInput sheets: the
7
+ * rows of both their finger and their mouse wheels
8
+ *
9
+ * The columns do not loop. Each rule follows a leaf of motion/uikit/
10
+ * datePicker.ts `wheels`. Pure functions keep the rules testable without a
11
+ * layout engine.
12
+ *
13
+ * SYNC: When modified, update timeWheelRules.test.ts and TimeWheels.tsx.
14
+ */
15
+ import { type ISOTimeString } from '../utils/timeParser.js';
16
+ /** A time of day, 24-hour. */
17
+ export interface TimeParts {
18
+ hour: number;
19
+ minute: number;
20
+ second: number;
21
+ }
22
+ /** A time wheel column. */
23
+ export type TimeColumn = 'hour' | 'minute' | 'second' | 'meridiem';
24
+ /** One row of a column: the value it stands for and its label. */
25
+ export interface TimeRow {
26
+ value: number;
27
+ label: string;
28
+ isDisabled: boolean;
29
+ /**
30
+ * Where the row falls when a key steps the column. An hour row holds the hour
31
+ * it stands for, so a key follows the full time: 12 PM comes before 1 PM.
32
+ */
33
+ order?: number;
34
+ }
35
+ /** Inclusive bounds in seconds since midnight; `null` is unbounded. */
36
+ export interface TimeBounds {
37
+ min: number | null;
38
+ max: number | null;
39
+ }
40
+ /** The bounds of a field's `min` and `max` times. */
41
+ export declare function wheelBounds(min: ISOTimeString | undefined, max: ISOTimeString | undefined): TimeBounds;
42
+ /** Whether the hour falls in the PM half (1) or the AM half (0). */
43
+ export declare function meridiemOf(hour: number): 0 | 1;
44
+ /** The 12-hour clock's label for a 24-hour hour: 0 → 12, 13 → 1. */
45
+ export declare function hour12From24(hour: number): number;
46
+ /** The 24-hour hour for a 12-hour hour in a half of the day: 12 AM → 0. */
47
+ export declare function hour24From12(hour: number, meridiem: number): number;
48
+ /**
49
+ * The time the wheels show for `time`: its seconds kept when the picker has a
50
+ * seconds column and dropped when it has none. A time outside the bounds is
51
+ * shown as it is, on its disabled rows: opening the sheet never moves an
52
+ * untouched value.
53
+ */
54
+ export declare function displayedTime(time: TimeParts, hasSeconds: boolean): TimeParts;
55
+ /**
56
+ * The time a column's rest commits when it would make `time`: the nearest
57
+ * time inside the bounds that the columns have a row for. Past a bound, that
58
+ * is the bound itself, or, with no seconds column, the nearest whole minute
59
+ * inside it (a 16:55:30 maximum: 16:55). Inside the bounds, `time` itself.
60
+ */
61
+ export declare function restingTime(time: TimeParts, hasSeconds: boolean, bounds: TimeBounds): TimeParts;
62
+ /**
63
+ * The hour the hour column makes when it comes to rest on `row` from the row
64
+ * that showed `from` (24-hour). In 24-hour time the row is the hour. In 12-hour
65
+ * time the half of the day changes only when the move passes between 11 and 12.
66
+ * The column (1 … 12) does not loop, so 12 counts as next to 1. A move between
67
+ * 12 and 1 to 6 keeps the half of the day. A move between 12 and 7 to 11
68
+ * changes it.
69
+ *
70
+ * @example
71
+ * ```
72
+ * hourAfterTurn(11, 12, '12h'); // 12: 11 AM → 12 PM
73
+ * hourAfterTurn(12, 1, '12h'); // 13: 12 PM → 1 PM
74
+ * hourAfterTurn(13, 9, '12h'); // 21: 1 PM → 9 PM
75
+ * ```
76
+ */
77
+ export declare function hourAfterTurn(from: number, row: number, hourFormat: '12h' | '24h'): number;
78
+ /**
79
+ * The hour column under a finger. In 24-hour time its rows are 00 … 23. In
80
+ * 12-hour time they are 1 … 12, each row standing for the hour a turn onto it
81
+ * from `from` makes (`hourAfterTurn`). A row is disabled when no minute of that
82
+ * hour is inside the bounds.
83
+ */
84
+ export declare function hourRows(from: number, hourFormat: '12h' | '24h', bounds: TimeBounds): TimeRow[];
85
+ /**
86
+ * The hour column under a mouse, whose columns turn one at a time. A 12-hour
87
+ * row stands for its hour in the half of the day that `from` is in.
88
+ */
89
+ export declare function hourRowsInHalf(from: number, hourFormat: '12h' | '24h', bounds: TimeBounds): TimeRow[];
90
+ /**
91
+ * The minute column for `hour`: every minute. A row is disabled when it is
92
+ * outside the bounds (its whole minute, when the picker shows seconds).
93
+ */
94
+ export declare function minuteRows(hour: number, hasSeconds: boolean, bounds: TimeBounds): TimeRow[];
95
+ /** The second column for `hour`:`minute`. */
96
+ export declare function secondRows(hour: number, minute: number, bounds: TimeBounds): TimeRow[];
97
+ /** The AM/PM column: a half is disabled when none of its hours is in bounds. */
98
+ export declare function meridiemRows(labels: {
99
+ am: string;
100
+ pm: string;
101
+ }, bounds: TimeBounds): TimeRow[];
102
+ /**
103
+ * The time made by turning `column` to `value` (its row's value) from `from`
104
+ * (the time the wheels show). Only that column changes, and the AM/PM column
105
+ * moves the hour by twelve.
106
+ */
107
+ export declare function turnColumn(from: TimeParts, column: TimeColumn, value: number, hourFormat: '12h' | '24h'): TimeParts;
108
+ /** What a column shows for a time: its row's value. */
109
+ export declare function columnValue(time: TimeParts, column: TimeColumn, hourFormat: '12h' | '24h'): number;
110
+ /**
111
+ * The columns that must turn after `column` came to rest on `value`. That is
112
+ * every column whose row for the committed time differs from the row it shows
113
+ * now. The column that moved shows `value`; the others show `from`. An hour
114
+ * that crossed 11 ↔ 12 turns the AM/PM column; a time brought back onto a bound
115
+ * turns every column whose row changed.
116
+ */
117
+ export declare function columnsToTurn(from: TimeParts, column: TimeColumn, value: number, committed: TimeParts, options: {
118
+ hourFormat: '12h' | '24h';
119
+ hasSeconds: boolean;
120
+ }): {
121
+ column: TimeColumn;
122
+ value: number;
123
+ }[];
124
+ //# sourceMappingURL=timeWheelRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeWheelRules.d.ts","sourceRoot":"","sources":["../../src/TimeInput/timeWheelRules.ts"],"names":[],"mappings":"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"}