@torpor/ui 0.4.0 → 1.0.1

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 (342) hide show
  1. package/README.md +83 -7
  2. package/dist/Accordion/Accordion.torp +42 -165
  3. package/dist/Accordion/AccordionContent.torp +6 -6
  4. package/dist/Accordion/AccordionHeader.torp +7 -11
  5. package/dist/Accordion/AccordionItem.torp +24 -24
  6. package/dist/Accordion/AccordionTrigger.torp +17 -8
  7. package/dist/Accordion/AccordionTypes.ts +1 -1
  8. package/dist/Accordion/index.d.ts +23 -25
  9. package/dist/Accordion/index.js +6 -22
  10. package/dist/Breadcrumb/Breadcrumb.torp +12 -10
  11. package/dist/Breadcrumb/BreadcrumbItem.torp +6 -6
  12. package/dist/Breadcrumb/BreadcrumbList.torp +21 -0
  13. package/dist/Breadcrumb/BreadcrumbSeparator.torp +4 -4
  14. package/dist/Breadcrumb/index.d.ts +39 -15
  15. package/dist/Breadcrumb/index.js +5 -14
  16. package/dist/Calendar/Calendar.torp +163 -156
  17. package/dist/Calendar/CalendarDay.torp +91 -45
  18. package/dist/Calendar/CalendarDayHeader.torp +36 -0
  19. package/dist/Calendar/CalendarGrid.torp +104 -0
  20. package/dist/Calendar/CalendarGridHeader.torp +43 -0
  21. package/dist/Calendar/CalendarHeader.torp +48 -0
  22. package/dist/Calendar/CalendarHeaderTrigger.torp +54 -0
  23. package/dist/Calendar/CalendarTypes.ts +24 -3
  24. package/dist/Calendar/buildDays.ts +54 -0
  25. package/dist/Calendar/index.d.ts +191 -5
  26. package/dist/Calendar/index.js +8 -10
  27. package/dist/Carousel/Carousel.torp +180 -0
  28. package/dist/Carousel/CarouselIndicators.torp +65 -0
  29. package/dist/Carousel/CarouselNext.torp +43 -0
  30. package/dist/Carousel/CarouselPrevious.torp +44 -0
  31. package/dist/Carousel/CarouselSlide.torp +57 -0
  32. package/dist/Carousel/CarouselTypes.ts +28 -0
  33. package/dist/Carousel/index.d.ts +147 -0
  34. package/dist/Carousel/index.js +6 -0
  35. package/dist/Charts/BarChart.torp +55 -144
  36. package/dist/Charts/Chart.torp +120 -0
  37. package/dist/Charts/ChartBars.torp +78 -0
  38. package/dist/Charts/ChartColumns.torp +66 -0
  39. package/dist/Charts/ChartGrid.torp +54 -0
  40. package/dist/Charts/ChartLines.torp +60 -0
  41. package/dist/Charts/ChartTypes.ts +25 -0
  42. package/dist/Charts/ChartXAxis.torp +85 -0
  43. package/dist/Charts/ChartYAxis.torp +107 -0
  44. package/dist/Charts/ColumnChart.torp +70 -0
  45. package/dist/Charts/GridLines.torp +4 -4
  46. package/dist/Charts/LineChart.torp +55 -178
  47. package/dist/Charts/PieChart.torp +14 -20
  48. package/dist/Charts/ScatterChart.torp +45 -38
  49. package/dist/Charts/SparkLine.torp +16 -19
  50. package/dist/Charts/XAxis.torp +14 -11
  51. package/dist/Charts/YAxis.torp +10 -8
  52. package/dist/Charts/index.d.ts +328 -79
  53. package/dist/Charts/index.js +17 -34
  54. package/dist/ColorPicker/ColorPalette.torp +154 -0
  55. package/dist/ColorPicker/ColorPicker.torp +157 -0
  56. package/dist/ColorPicker/index.d.ts +85 -0
  57. package/dist/ColorPicker/index.js +3 -0
  58. package/dist/ComboBox/ComboBox.torp +205 -0
  59. package/dist/ComboBox/ComboBoxContent.torp +120 -0
  60. package/dist/ComboBox/ComboBoxInput.torp +161 -0
  61. package/dist/ComboBox/ComboBoxTrigger.torp +49 -0
  62. package/dist/ComboBox/ComboBoxTypes.ts +39 -0
  63. package/dist/ComboBox/index.d.ts +154 -0
  64. package/dist/ComboBox/index.js +6 -0
  65. package/dist/CommandPalette/CommandPalette.torp +223 -0
  66. package/dist/CommandPalette/CommandTypes.ts +14 -0
  67. package/dist/CommandPalette/index.d.ts +45 -0
  68. package/dist/CommandPalette/index.js +2 -0
  69. package/dist/Contextual/Contextual.torp +32 -50
  70. package/dist/Contextual/ContextualContent.torp +29 -73
  71. package/dist/Contextual/ContextualHover.torp +112 -0
  72. package/dist/Contextual/ContextualTrigger.torp +31 -42
  73. package/dist/Contextual/ContextualTypes.ts +4 -17
  74. package/dist/Contextual/index.d.ts +44 -27
  75. package/dist/Contextual/index.js +5 -14
  76. package/dist/DataGrid/DataGrid.torp +496 -0
  77. package/dist/DataGrid/DataGridTypes.ts +25 -0
  78. package/dist/DataGrid/index.d.ts +90 -0
  79. package/dist/DataGrid/index.js +3 -0
  80. package/dist/DataGrid/sortRows.ts +32 -0
  81. package/dist/DatePicker/DatePicker.torp +222 -0
  82. package/dist/DatePicker/DatePickerHeader.torp +20 -0
  83. package/dist/DatePicker/index.d.ts +55 -0
  84. package/dist/DatePicker/index.js +2 -0
  85. package/dist/DateRangePicker/DateRangePicker.torp +315 -0
  86. package/dist/DateRangePicker/DateRangePickerDay.torp +137 -0
  87. package/dist/DateRangePicker/DateRangePickerTypes.ts +21 -0
  88. package/dist/DateRangePicker/index.d.ts +76 -0
  89. package/dist/DateRangePicker/index.js +3 -0
  90. package/dist/Dialog/Dialog.torp +24 -17
  91. package/dist/Dialog/DialogBody.torp +5 -9
  92. package/dist/Dialog/DialogButton.torp +12 -12
  93. package/dist/Dialog/DialogFooter.torp +3 -7
  94. package/dist/Dialog/DialogHeader.torp +6 -10
  95. package/dist/Dialog/index.d.ts +27 -27
  96. package/dist/Dialog/index.js +6 -22
  97. package/dist/Disclosure/Disclosure.torp +18 -18
  98. package/dist/Disclosure/DisclosureContent.torp +5 -6
  99. package/dist/Disclosure/DisclosureHeader.torp +7 -11
  100. package/dist/Disclosure/DisclosureTrigger.torp +4 -5
  101. package/dist/Disclosure/index.d.ts +14 -16
  102. package/dist/Disclosure/index.js +5 -18
  103. package/dist/Drawer/Drawer.torp +19 -49
  104. package/dist/Drawer/DrawerContent.torp +70 -13
  105. package/dist/Drawer/DrawerOverlay.torp +4 -4
  106. package/dist/Drawer/DrawerTrigger.torp +16 -4
  107. package/dist/Drawer/DrawerTypes.ts +3 -0
  108. package/dist/Drawer/index.d.ts +16 -20
  109. package/dist/Drawer/index.js +5 -18
  110. package/dist/FocusGroup/FocusGroup.torp +70 -0
  111. package/dist/FocusGroup/FocusGroupButton.torp +59 -0
  112. package/dist/FocusGroup/FocusGroupTypes.ts +14 -0
  113. package/dist/FocusGroup/index.d.ts +44 -0
  114. package/dist/FocusGroup/index.js +3 -0
  115. package/dist/Form/CheckBox.torp +27 -18
  116. package/dist/Form/Field.torp +17 -17
  117. package/dist/Form/File.torp +31 -22
  118. package/dist/Form/FileDrop.torp +187 -0
  119. package/dist/Form/Form.torp +24 -22
  120. package/dist/Form/FormTypes.ts +2 -0
  121. package/dist/Form/FormValues.torp +37 -0
  122. package/dist/Form/Hidden.torp +4 -12
  123. package/dist/Form/Input.torp +38 -32
  124. package/dist/Form/Label.torp +3 -3
  125. package/dist/Form/Message.torp +13 -5
  126. package/dist/Form/MessageSummary.torp +5 -4
  127. package/dist/Form/Option.torp +11 -7
  128. package/dist/Form/Select.torp +32 -28
  129. package/dist/Form/TextArea.torp +35 -31
  130. package/dist/Form/Toggle.torp +109 -0
  131. package/dist/Form/index.d.ts +178 -79
  132. package/dist/Form/index.js +16 -50
  133. package/dist/ListBox/ListBox.torp +253 -150
  134. package/dist/ListBox/ListBoxItem.torp +54 -30
  135. package/dist/ListBox/ListBoxSeparator.torp +5 -2
  136. package/dist/ListBox/ListBoxTypes.ts +23 -5
  137. package/dist/ListBox/LoadedListBoxItems.torp +41 -0
  138. package/dist/ListBox/index.d.ts +56 -17
  139. package/dist/ListBox/index.js +4 -14
  140. package/dist/MaskedInput/MaskedInput.torp +143 -0
  141. package/dist/MaskedInput/index.d.ts +52 -0
  142. package/dist/MaskedInput/index.js +2 -0
  143. package/dist/Menu/Menu.torp +86 -71
  144. package/dist/Menu/MenuButton.torp +17 -10
  145. package/dist/Menu/MenuCheck.torp +22 -18
  146. package/dist/Menu/MenuGroup.torp +4 -4
  147. package/dist/Menu/MenuIndicator.torp +5 -13
  148. package/dist/Menu/MenuPopout.torp +20 -50
  149. package/dist/Menu/MenuPopoutContent.torp +15 -66
  150. package/dist/Menu/MenuPopoutTrigger.torp +19 -100
  151. package/dist/Menu/MenuRadio.torp +25 -26
  152. package/dist/Menu/MenuRadioGroup.torp +25 -68
  153. package/dist/Menu/MenuSeparator.torp +3 -2
  154. package/dist/Menu/MenuTypes.ts +9 -19
  155. package/dist/Menu/index.d.ts +45 -51
  156. package/dist/Menu/index.js +12 -46
  157. package/dist/MenuBar/MenuBar.torp +31 -83
  158. package/dist/MenuBar/MenuBarItem.torp +33 -51
  159. package/dist/MenuBar/MenuBarItemContent.torp +15 -101
  160. package/dist/MenuBar/MenuBarItemTrigger.torp +20 -55
  161. package/dist/MenuBar/MenuBarTypes.ts +11 -10
  162. package/dist/MenuBar/index.d.ts +17 -19
  163. package/dist/MenuBar/index.js +5 -18
  164. package/dist/Modal/Modal.torp +22 -47
  165. package/dist/Modal/ModalContent.torp +21 -44
  166. package/dist/Modal/ModalOverlay.torp +4 -4
  167. package/dist/Modal/ModalTrigger.torp +16 -32
  168. package/dist/Modal/ModalTypes.ts +5 -16
  169. package/dist/Modal/index.d.ts +17 -29
  170. package/dist/Modal/index.js +29 -28
  171. package/dist/Modal/index.js.map +1 -1
  172. package/dist/Modal/showPrompt.ts +2 -2
  173. package/dist/NavMenu/NavMenu.torp +22 -111
  174. package/dist/NavMenu/NavMenuButton.torp +7 -8
  175. package/dist/NavMenu/NavMenuElement.torp +6 -7
  176. package/dist/NavMenu/NavMenuGroup.torp +5 -5
  177. package/dist/NavMenu/NavMenuGroupHeader.torp +5 -8
  178. package/dist/NavMenu/NavMenuLink.torp +7 -10
  179. package/dist/NavMenu/NavMenuPopout.torp +15 -60
  180. package/dist/NavMenu/NavMenuPopoutContent.torp +14 -73
  181. package/dist/NavMenu/NavMenuPopoutTrigger.torp +17 -99
  182. package/dist/NavMenu/NavMenuSeparator.torp +3 -7
  183. package/dist/NavMenu/NavMenuTypes.ts +4 -14
  184. package/dist/NavMenu/index.d.ts +40 -44
  185. package/dist/NavMenu/index.js +11 -42
  186. package/dist/Notification/Notification.torp +12 -24
  187. package/dist/Notification/NotificationClose.torp +6 -3
  188. package/dist/Notification/NotificationContainer.torp +3 -7
  189. package/dist/Notification/NotificationContent.torp +3 -7
  190. package/dist/Notification/NotificationTypes.ts +1 -1
  191. package/dist/Notification/index.d.ts +15 -12
  192. package/dist/Notification/index.js +22 -32
  193. package/dist/Notification/index.js.map +1 -1
  194. package/dist/NumberInput/NumberInput.torp +255 -0
  195. package/dist/NumberInput/index.d.ts +44 -0
  196. package/dist/NumberInput/index.js +2 -0
  197. package/dist/Pagination/Pagination.torp +41 -34
  198. package/dist/Pagination/PaginationItem.torp +50 -13
  199. package/dist/Pagination/PaginationTypes.ts +3 -6
  200. package/dist/Pagination/buildPages.ts +9 -5
  201. package/dist/Pagination/index.d.ts +19 -9
  202. package/dist/Pagination/index.js +3 -40
  203. package/dist/Popover/Popover.torp +32 -50
  204. package/dist/Popover/PopoverContent.torp +27 -76
  205. package/dist/Popover/PopoverHover.torp +90 -0
  206. package/dist/Popover/PopoverTrigger.torp +17 -40
  207. package/dist/Popover/PopoverTypes.ts +4 -17
  208. package/dist/Popover/index.d.ts +40 -25
  209. package/dist/Popover/index.js +18 -18
  210. package/dist/Popover/index.js.map +1 -1
  211. package/dist/Progress/Progress.torp +88 -0
  212. package/dist/Progress/index.d.ts +37 -0
  213. package/dist/Progress/index.js +2 -0
  214. package/dist/Rating/Rating.torp +225 -0
  215. package/dist/Rating/index.d.ts +44 -0
  216. package/dist/Rating/index.js +2 -0
  217. package/dist/SegmentedControl/SegmentedControl.torp +233 -0
  218. package/dist/SegmentedControl/SegmentedControlTypes.ts +26 -0
  219. package/dist/SegmentedControl/SegmentedItem.torp +78 -0
  220. package/dist/SegmentedControl/index.d.ts +74 -0
  221. package/dist/SegmentedControl/index.js +3 -0
  222. package/dist/SelectBox/SelectBox.torp +107 -53
  223. package/dist/SelectBox/SelectBoxContent.torp +49 -19
  224. package/dist/SelectBox/SelectBoxTrigger.torp +108 -8
  225. package/dist/SelectBox/SelectBoxTypes.ts +26 -11
  226. package/dist/SelectBox/index.d.ts +39 -54
  227. package/dist/SelectBox/index.js +5 -18
  228. package/dist/Slider/Slider.torp +245 -0
  229. package/dist/Slider/index.d.ts +45 -0
  230. package/dist/Slider/index.js +2 -0
  231. package/dist/Splitter/Splitter.torp +156 -0
  232. package/dist/Splitter/SplitterHandle.torp +161 -0
  233. package/dist/Splitter/SplitterPane.torp +63 -0
  234. package/dist/Splitter/SplitterTypes.ts +31 -0
  235. package/dist/Splitter/index.d.ts +103 -0
  236. package/dist/Splitter/index.js +4 -0
  237. package/dist/Stepper/Stepper.torp +146 -0
  238. package/dist/Stepper/StepperMarker.torp +24 -0
  239. package/dist/Stepper/StepperNext.torp +44 -0
  240. package/dist/Stepper/StepperPrevious.torp +44 -0
  241. package/dist/Stepper/StepperStep.torp +90 -0
  242. package/dist/Stepper/StepperTypes.ts +30 -0
  243. package/dist/Stepper/index.d.ts +149 -0
  244. package/dist/Stepper/index.js +6 -0
  245. package/dist/TabGroup/TabContent.torp +6 -6
  246. package/dist/TabGroup/TabGroup.torp +97 -113
  247. package/dist/TabGroup/TabGroupTypes.ts +1 -1
  248. package/dist/TabGroup/TabHeader.torp +23 -19
  249. package/dist/TabGroup/TabList.torp +8 -21
  250. package/dist/TabGroup/TabTrigger.torp +6 -7
  251. package/dist/TabGroup/index.d.ts +26 -26
  252. package/dist/TabGroup/index.js +6 -22
  253. package/dist/TagInput/TagInput.torp +378 -0
  254. package/dist/TagInput/index.d.ts +48 -0
  255. package/dist/TagInput/index.js +2 -0
  256. package/dist/TimePicker/TimePicker.torp +462 -0
  257. package/dist/TimePicker/index.d.ts +53 -0
  258. package/dist/TimePicker/index.js +2 -0
  259. package/dist/ToolBar/ToolBar.torp +29 -110
  260. package/dist/ToolBar/ToolBarButton.torp +7 -8
  261. package/dist/ToolBar/ToolBarElement.torp +7 -11
  262. package/dist/ToolBar/ToolBarPopout.torp +16 -68
  263. package/dist/ToolBar/ToolBarPopoutContent.torp +15 -73
  264. package/dist/ToolBar/ToolBarPopoutTrigger.torp +19 -110
  265. package/dist/ToolBar/ToolBarSeparator.torp +3 -7
  266. package/dist/ToolBar/ToolBarTypes.ts +7 -14
  267. package/dist/ToolBar/index.d.ts +30 -32
  268. package/dist/ToolBar/index.js +8 -30
  269. package/dist/Tree/Tree.torp +395 -0
  270. package/dist/Tree/TreeItem.torp +176 -0
  271. package/dist/Tree/TreeItemChildren.torp +60 -0
  272. package/dist/Tree/TreeItemContent.torp +46 -0
  273. package/dist/Tree/TreeItemLabel.torp +94 -0
  274. package/dist/Tree/TreeItemTrigger.torp +78 -0
  275. package/dist/Tree/TreeLoadedChildren.torp +122 -0
  276. package/dist/Tree/TreeTypes.ts +63 -0
  277. package/dist/Tree/index.d.ts +262 -0
  278. package/dist/Tree/index.js +7 -0
  279. package/dist/icons/ChevronDoubleLeft.torp +19 -0
  280. package/dist/icons/ChevronDoubleRight.torp +19 -0
  281. package/dist/icons/index.d.ts +61 -1
  282. package/dist/icons/index.js +17 -58
  283. package/dist/loader-DEeG5vFY.js +44 -0
  284. package/dist/loader-DEeG5vFY.js.map +1 -0
  285. package/dist/motion/index.d.ts +12 -8
  286. package/dist/motion/index.d.ts.map +1 -0
  287. package/dist/motion/index.js +70 -88
  288. package/dist/motion/index.js.map +1 -1
  289. package/dist/mount/draggable.ts +63 -0
  290. package/dist/mount/dropTarget.ts +108 -0
  291. package/dist/mount/hoverTrigger.ts +1 -1
  292. package/dist/utils/$handle.ts +23 -0
  293. package/dist/utils/LoadedItems.torp +47 -0
  294. package/dist/utils/PopoutClickTrigger.torp +99 -0
  295. package/dist/utils/PopoutTypes.ts +59 -12
  296. package/dist/utils/SubmenuPopout.torp +89 -0
  297. package/dist/utils/SubmenuPopoutContent.torp +87 -0
  298. package/dist/utils/SubmenuPopoutTrigger.torp +160 -0
  299. package/dist/utils/chartColors.ts +13 -13
  300. package/dist/utils/chartLayout.ts +143 -0
  301. package/dist/utils/colorUtils.ts +103 -0
  302. package/dist/utils/createItemGroup.ts +152 -0
  303. package/dist/utils/dateUtils.ts +8 -7
  304. package/dist/utils/documentEvents.ts +4 -4
  305. package/dist/utils/dragState.ts +86 -0
  306. package/dist/utils/focusGroup.ts +204 -0
  307. package/dist/utils/focusItem.ts +44 -0
  308. package/dist/utils/formField.ts +70 -0
  309. package/dist/utils/hoverOpen.ts +50 -0
  310. package/dist/utils/hoverReveal.ts +194 -0
  311. package/dist/utils/loader.ts +113 -0
  312. package/dist/utils/maskUtils.ts +98 -0
  313. package/dist/utils/popoutContent.ts +137 -0
  314. package/dist/utils/reorderList.ts +52 -0
  315. package/package.json +97 -22
  316. package/dist/Accordion/index.js.map +0 -1
  317. package/dist/Breadcrumb/index.js.map +0 -1
  318. package/dist/Calendar/index.js.map +0 -1
  319. package/dist/Charts/index.js.map +0 -1
  320. package/dist/Contextual/index.js.map +0 -1
  321. package/dist/Dialog/index.js.map +0 -1
  322. package/dist/Disclosure/index.js.map +0 -1
  323. package/dist/Drawer/index.js.map +0 -1
  324. package/dist/Form/index.js.map +0 -1
  325. package/dist/ListBox/index.js.map +0 -1
  326. package/dist/Menu/index.js.map +0 -1
  327. package/dist/MenuBar/index.js.map +0 -1
  328. package/dist/NavMenu/index.js.map +0 -1
  329. package/dist/Pagination/index.js.map +0 -1
  330. package/dist/SelectBox/SelectBoxInput.torp +0 -95
  331. package/dist/SelectBox/index.js.map +0 -1
  332. package/dist/TabGroup/index.js.map +0 -1
  333. package/dist/ToolBar/index.js.map +0 -1
  334. package/dist/icons/index.js.map +0 -1
  335. package/dist/index.d.ts +0 -19
  336. package/dist/metafile-esm.json +0 -1
  337. package/dist/motion/fade.ts +0 -16
  338. package/dist/motion/grow.ts +0 -23
  339. package/dist/motion/measure.ts +0 -28
  340. package/dist/motion/slide.ts +0 -45
  341. package/dist/mount/index.d.ts +0 -2
  342. package/dist/utils/index.d.ts +0 -2
@@ -0,0 +1,462 @@
1
+ import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
2
+ import {
3
+ type FieldContext,
4
+ FieldContextName,
5
+ type FormContext,
6
+ FormContextName,
7
+ } from "../Form/FormTypes";
8
+ import FormValues from "../Form/FormValues.torp";
9
+ import createFormField from "../utils/formField";
10
+
11
+ interface TimePickerProps {
12
+ /** An ID for the root element */
13
+ id?: string;
14
+ /** Classes for the root element */
15
+ class?: ClassValue;
16
+ /** Styles for the root element */
17
+ style?: StyleValue;
18
+ /**
19
+ * The current time as "HH:mm" or "HH:mm:ss" (24-hour), or undefined if
20
+ * the field is empty
21
+ */
22
+ value?: Bindable<string | undefined>;
23
+ /** If set to true, times are displayed and entered in 12-hour format with an AM/PM toggle */
24
+ hour12?: boolean;
25
+ /** If set to true, a seconds field is shown */
26
+ seconds?: boolean;
27
+ /** The amount by which the minute field steps (default 1) */
28
+ step?: number;
29
+ /** The earliest allowed time, as "HH:mm" or "HH:mm:ss" */
30
+ min?: string;
31
+ /** The latest allowed time, as "HH:mm" or "HH:mm:ss" */
32
+ max?: string;
33
+ /** If set to true, the user cannot interact with this input */
34
+ disabled?: boolean;
35
+ /** An accessible name for the whole input */
36
+ ariaLabel?: string;
37
+ /** The name of the component's form data field */
38
+ name?: string;
39
+ /** Raised when the value has changed */
40
+ onchange?: (value: string | undefined) => void;
41
+ }
42
+
43
+ type Part = "hour" | "minute" | "second";
44
+
45
+ const PAD = [2, 2, 2];
46
+
47
+ /**
48
+ * An input for entering a time of day, made of hour, minute and (optionally)
49
+ * second fields that follow the WAI ARIA spinbutton pattern -- each can be
50
+ * typed into or stepped with the arrow keys -- plus an AM/PM toggle in
51
+ * 12-hour mode.
52
+ *
53
+ * The value is always the canonical 24-hour form ("HH:mm" or "HH:mm:ss"),
54
+ * regardless of display format.
55
+ */
56
+ export default function TimePicker($props: TimePickerProps | undefined) {
57
+ let hourInput: HTMLInputElement;
58
+ let minuteInput: HTMLInputElement;
59
+ let secondInput: HTMLInputElement;
60
+ let periodButton: HTMLButtonElement;
61
+
62
+ // Wire up the enclosing Form and Field, if any
63
+ const form = createFormField(
64
+ $props?.name,
65
+ $context[FormContextName] as FormContext | undefined,
66
+ $context[FieldContextName] as FieldContext | undefined,
67
+ );
68
+
69
+ function getSeconds(): boolean {
70
+ return $props?.seconds === true;
71
+ }
72
+ function getDisabled(): boolean {
73
+ return $props?.disabled === true;
74
+ }
75
+
76
+ function parseTime(text: string | undefined): { h: number; m: number; s: number } | undefined {
77
+ if (!text) return undefined;
78
+ const match = text.match(/^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/);
79
+ if (!match) return undefined;
80
+ return {
81
+ h: Number(match[1]),
82
+ m: Number(match[2]),
83
+ s: match[3] ? Number(match[3]) : 0,
84
+ };
85
+ }
86
+
87
+ function clampPart(part: Part, n: number): number {
88
+ switch (part) {
89
+ case "hour":
90
+ return Math.min(23, Math.max(0, n));
91
+ case "minute":
92
+ return Math.min(59, Math.max(0, n));
93
+ case "second":
94
+ return Math.min(59, Math.max(0, n));
95
+ }
96
+ }
97
+
98
+ function format(n: number): string {
99
+ return String(n).padStart(2, "0");
100
+ }
101
+
102
+ function formatValue(h: number, m: number, s: number): string {
103
+ return getSeconds() ? `${format(h)}:${format(m)}:${format(s)}` : `${format(h)}:${format(m)}`;
104
+ }
105
+
106
+ let $state = $watch({
107
+ value: undefined as string | undefined,
108
+
109
+ // The text being typed in each segment; committed on completion
110
+ hourText: "",
111
+ minuteText: "",
112
+ secondText: "",
113
+ });
114
+
115
+ $bind($state, $props, "value");
116
+
117
+ // The committed parts of the current value; missing parts are zero
118
+ function parts(): { h: number; m: number; s: number } {
119
+ const parsed = parseTime($state.value);
120
+ return parsed ?? { h: 0, m: 0, s: 0 };
121
+ }
122
+
123
+ function getMin(): number {
124
+ const parsed = parseTime($props?.min);
125
+ if (!parsed) return 0;
126
+ return parsed.h * 3600 + parsed.m * 60 + parsed.s;
127
+ }
128
+
129
+ function getMax(): number {
130
+ const parsed = parseTime($props?.max);
131
+ if (!parsed) return 24 * 3600 - (getSeconds() ? 1 : 60);
132
+ return parsed.h * 3600 + parsed.m * 60 + parsed.s;
133
+ }
134
+
135
+ function totalSeconds(h: number, m: number, s: number): number {
136
+ return h * 3600 + m * 60 + s;
137
+ }
138
+
139
+ function setParts(h: number, m: number, s: number) {
140
+ // Clamp the assembled time between min and max
141
+ let t = totalSeconds(
142
+ clampPart("hour", h),
143
+ clampPart("minute", m),
144
+ clampPart("second", s),
145
+ );
146
+ t = Math.min(getMax(), Math.max(getMin(), t));
147
+
148
+ const next = formatValue(Math.floor(t / 3600), Math.floor((t % 3600) / 60), t % 60);
149
+ if (next !== $state.value) {
150
+ $state.value = next;
151
+ form.handleInput();
152
+ $props?.onchange?.(next);
153
+ }
154
+ }
155
+
156
+ // --- Spinbutton segments ---
157
+
158
+ function partDisplay(part: Part): string {
159
+ const p = parts();
160
+ const n = part === "hour" ? p.h : part === "minute" ? p.m : p.s;
161
+ if (part !== "hour") {
162
+ return format(n);
163
+ }
164
+ if ($props?.hour12) {
165
+ const twelve = n % 12 === 0 ? 12 : n % 12;
166
+ return format(twelve);
167
+ }
168
+ return format(n);
169
+ }
170
+
171
+ function isPM(): boolean {
172
+ return parts().h >= 12;
173
+ }
174
+
175
+ function commitBuffer(part: Part, buffer: string): boolean {
176
+ if (buffer.trim() === "") return false;
177
+ const n = Number(buffer);
178
+ if (!Number.isFinite(n)) return false;
179
+ setPartValue(part, n, false);
180
+ return true;
181
+ }
182
+
183
+ /**
184
+ * Steps a segment by delta hours/minutes/seconds, wrapping within its
185
+ * range like a clock face
186
+ */
187
+ function stepPart(part: Part, delta: number) {
188
+ if (getDisabled()) return;
189
+ const p = parts();
190
+ let h = p.h;
191
+ let m = p.m;
192
+ let s = p.s;
193
+ switch (part) {
194
+ case "hour": {
195
+ h = ((h + delta) % 24 + 24) % 24;
196
+ break;
197
+ }
198
+ case "minute": {
199
+ m = ((m + delta) % 60 + 60) % 60;
200
+ break;
201
+ }
202
+ case "second": {
203
+ s = ((s + delta) % 60 + 60) % 60;
204
+ break;
205
+ }
206
+ }
207
+ setParts(h, m, s);
208
+ syncInputs();
209
+ }
210
+
211
+ function setPartValue(part: Part, n: number, sync: boolean) {
212
+ const p = parts();
213
+ switch (part) {
214
+ case "hour":
215
+ setParts(clampPart("hour", n), p.m, p.s);
216
+ break;
217
+ case "minute":
218
+ setParts(p.h, clampPart("minute", n), p.s);
219
+ break;
220
+ case "second":
221
+ setParts(p.h, p.m, clampPart("second", n));
222
+ break;
223
+ }
224
+ if (sync) syncInputs();
225
+ }
226
+
227
+ // Keep segment inputs showing the committed values unless typing there
228
+ function syncInputs() {
229
+ if (!hourInput || !minuteInput) return;
230
+ const editing = (el: HTMLInputElement, buffer: string) =>
231
+ document.activeElement === el && el.value !== buffer;
232
+ if (!editing(hourInput, $state.hourText)) {
233
+ hourInput.value = partDisplay("hour");
234
+ }
235
+ if (!editing(minuteInput, $state.minuteText)) {
236
+ minuteInput.value = partDisplay("minute");
237
+ }
238
+ if (secondInput && !editing(secondInput, $state.secondText)) {
239
+ secondInput.value = partDisplay("second");
240
+ }
241
+ }
242
+
243
+ function handleInput(part: Part, e: Event) {
244
+ if (getDisabled()) return;
245
+ const el = e.currentTarget as HTMLInputElement;
246
+
247
+ // Accept digits only; strip everything else as it is typed
248
+ let text = el.value.replace(/\D/g, "");
249
+ const limit = part === "hour" ? ($props?.hour12 ? 12 : 23) : 59;
250
+ while (text.length > 0 && Number(text) > limit && text.length > 1) {
251
+ text = text.slice(1);
252
+ }
253
+
254
+ el.value = text;
255
+ setBuffer(part, text);
256
+
257
+ // Two digits fills the segment; move on to the next one
258
+ if (text.length >= (PAD[partIndex(part)] ?? 2)) {
259
+ commitBuffer(part, text);
260
+ focusNextSegment(part);
261
+ }
262
+ }
263
+
264
+ function handleBlur(part: Part, e: FocusEvent) {
265
+ const el = e.target as HTMLInputElement;
266
+ commitBuffer(part, el.value);
267
+ setBuffer(part, "");
268
+ el.value = partDisplay(part);
269
+ form.handleBlur();
270
+ }
271
+
272
+ function handleSegmentKey(part: Part, e: KeyboardEvent) {
273
+ if (getDisabled()) return;
274
+ const el = e.currentTarget as HTMLInputElement;
275
+
276
+ switch (e.key) {
277
+ case "ArrowUp": {
278
+ e.preventDefault();
279
+ const s = $props?.step ?? 1;
280
+ stepPart(part, part === "minute" ? s : 1);
281
+ break;
282
+ }
283
+ case "ArrowDown": {
284
+ e.preventDefault();
285
+ const s = $props?.step ?? 1;
286
+ stepPart(part, -(part === "minute" ? s : 1));
287
+ break;
288
+ }
289
+ case "Home": {
290
+ e.preventDefault();
291
+ setPartValue(part, 0, true);
292
+ break;
293
+ }
294
+ case "End": {
295
+ e.preventDefault();
296
+ setPartValue(part, part === "hour" ? 23 : 59, true);
297
+ break;
298
+ }
299
+ case "Enter": {
300
+ e.preventDefault();
301
+ commitBuffer(part, el.value);
302
+ setBuffer(part, "");
303
+ el.value = partDisplay(part);
304
+ break;
305
+ }
306
+ }
307
+ }
308
+
309
+ function setBuffer(part: Part, text: string) {
310
+ switch (part) {
311
+ case "hour":
312
+ $state.hourText = text;
313
+ break;
314
+ case "minute":
315
+ $state.minuteText = text;
316
+ break;
317
+ case "second":
318
+ $state.secondText = text;
319
+ break;
320
+ }
321
+ }
322
+
323
+ function partIndex(part: Part): number {
324
+ switch (part) {
325
+ case "hour":
326
+ return 0;
327
+ case "minute":
328
+ return 1;
329
+ case "second":
330
+ return 2;
331
+ }
332
+ }
333
+
334
+ function focusNextSegment(part: Part) {
335
+ if (part === "hour") {
336
+ minuteInput?.focus();
337
+ } else if (part === "minute" && getSeconds()) {
338
+ secondInput?.focus();
339
+ } else if (part === "minute" && $props?.hour12) {
340
+ periodButton?.focus();
341
+ }
342
+ }
343
+
344
+ // --- AM/PM toggle ---
345
+
346
+ function togglePeriod() {
347
+ if (getDisabled()) return;
348
+ const p = parts();
349
+ setParts(isPM() ? p.h - 12 : p.h + 12, p.m, p.s);
350
+ }
351
+
352
+ function handlePeriodKey(e: KeyboardEvent) {
353
+ if (getDisabled()) return;
354
+ switch (e.key) {
355
+ case "ArrowUp":
356
+ case "ArrowDown": {
357
+ e.preventDefault();
358
+ togglePeriod();
359
+ break;
360
+ }
361
+ }
362
+ }
363
+
364
+ function ariaNow(part: Part): number {
365
+ const p = parts();
366
+ return part === "hour" ? p.h : part === "minute" ? p.m : p.s;
367
+ }
368
+
369
+ function ariaText(part: Part): string {
370
+ if (part === "hour" && $props?.hour12) {
371
+ return `${partDisplay("hour")} ${isPM() ? "PM" : "AM"}`;
372
+ }
373
+ return String(ariaNow(part));
374
+ }
375
+
376
+ @render {
377
+ <div
378
+ id={$props?.id}
379
+ class={["torp-time-picker", $props?.class]}
380
+ {$props?.style}
381
+ role="group"
382
+ aria-label={$props?.ariaLabel}
383
+ data-valid={form.valid ? "valid" : "invalid"}
384
+ aria-describedby={form.messageId}
385
+ >
386
+ <input
387
+ &ref={hourInput}
388
+ class="torp-time-picker-part"
389
+ type="text"
390
+ inputmode="numeric"
391
+ role="spinbutton"
392
+ value={partDisplay("hour")}
393
+ aria-label={$props?.hour12 ? "Hour (12-hour)" : "Hour"}
394
+ aria-valuemin={0}
395
+ aria-valuemax={23}
396
+ aria-valuenow={ariaNow("hour")}
397
+ aria-valuetext={ariaText("hour")}
398
+ disabled={getDisabled()}
399
+ tabindex={getDisabled() ? -1 : 0}
400
+ oninput={(e: Event) => handleInput("hour", e)}
401
+ onblur={(e: FocusEvent) => handleBlur("hour", e)}
402
+ onkeydown={(e: KeyboardEvent) => handleSegmentKey("hour", e)}
403
+ />
404
+ <span class="torp-time-picker-separator">:</span>
405
+ <input
406
+ &ref={minuteInput}
407
+ class="torp-time-picker-part"
408
+ type="text"
409
+ inputmode="numeric"
410
+ role="spinbutton"
411
+ value={partDisplay("minute")}
412
+ aria-label="Minute"
413
+ aria-valuemin={0}
414
+ aria-valuemax={59}
415
+ aria-valuenow={ariaNow("minute")}
416
+ aria-valuetext={ariaText("minute")}
417
+ disabled={getDisabled()}
418
+ tabindex={getDisabled() ? -1 : 0}
419
+ oninput={(e: Event) => handleInput("minute", e)}
420
+ onblur={(e: FocusEvent) => handleBlur("minute", e)}
421
+ onkeydown={(e: KeyboardEvent) => handleSegmentKey("minute", e)}
422
+ />
423
+ @if (getSeconds()) {
424
+ <span class="torp-time-picker-separator">:</span>
425
+ <input
426
+ &ref={secondInput}
427
+ class="torp-time-picker-part"
428
+ type="text"
429
+ inputmode="numeric"
430
+ role="spinbutton"
431
+ value={partDisplay("second")}
432
+ aria-label="Second"
433
+ aria-valuemin={0}
434
+ aria-valuemax={59}
435
+ aria-valuenow={ariaNow("second")}
436
+ aria-valuetext={ariaText("second")}
437
+ disabled={getDisabled()}
438
+ tabindex={getDisabled() ? -1 : 0}
439
+ oninput={(e: Event) => handleInput("second", e)}
440
+ onblur={(e: FocusEvent) => handleBlur("second", e)}
441
+ onkeydown={(e: KeyboardEvent) => handleSegmentKey("second", e)}
442
+ />
443
+ }
444
+ @if ($props?.hour12) {
445
+ <button
446
+ &ref={periodButton}
447
+ type="button"
448
+ class="torp-time-picker-period"
449
+ disabled={getDisabled()}
450
+ tabindex={getDisabled() ? -1 : 0}
451
+ aria-label={isPM() ? "PM" : "AM"}
452
+ onclick={togglePeriod}
453
+ onkeydown={handlePeriodKey}
454
+ >
455
+ {isPM() ? "PM" : "AM"}
456
+ </button>
457
+ }
458
+ </div>
459
+
460
+ <FormValues name={form.name} value={$state.value} />
461
+ }
462
+ }
@@ -0,0 +1,53 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface TimePickerProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Classes for the root element */
8
+ class?: ClassValue;
9
+ /** Styles for the root element */
10
+ style?: StyleValue;
11
+ /**
12
+ * The current time as "HH:mm" or "HH:mm:ss" (24-hour), or undefined if
13
+ * the field is empty
14
+ */
15
+ value?: Bindable<string | undefined>;
16
+ /** If set to true, times are displayed and entered in 12-hour format with an AM/PM toggle */
17
+ hour12?: boolean;
18
+ /** If set to true, a seconds field is shown */
19
+ seconds?: boolean;
20
+ /** The amount by which the minute field steps (default 1) */
21
+ step?: number;
22
+ /** The earliest allowed time, as "HH:mm" or "HH:mm:ss" */
23
+ min?: string;
24
+ /** The latest allowed time, as "HH:mm" or "HH:mm:ss" */
25
+ max?: string;
26
+ /** If set to true, the user cannot interact with this input */
27
+ disabled?: boolean;
28
+ /** An accessible name for the whole input */
29
+ ariaLabel?: string;
30
+ /** The name of the component's form data field */
31
+ name?: string;
32
+ /** Raised when the value has changed */
33
+ onchange?: (value: string | undefined) => void;
34
+ }
35
+
36
+ /**
37
+ * An input for entering a time of day, made of hour, minute and (optionally)
38
+ * second fields that follow the WAI ARIA spinbutton pattern -- each can be
39
+ * typed into or stepped with the arrow keys -- plus an AM/PM toggle in
40
+ * 12-hour mode.
41
+ *
42
+ * The value is always the canonical 24-hour form ("HH:mm" or "HH:mm:ss"),
43
+ * regardless of display format.
44
+ */
45
+ declare function TimePicker(
46
+ $parent: ParentNode,
47
+ $anchor: Node | null,
48
+ $props: TimePickerProps | undefined,
49
+ $context?: Record<PropertyKey, any>,
50
+ $slots?: Record<string, SlotRender>,
51
+ ): void;
52
+
53
+ export { TimePicker };
@@ -0,0 +1,2 @@
1
+ import TimePicker from "./TimePicker.torp";
2
+ export { TimePicker };