@torpor/ui 0.3.7 → 1.0.0

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 (346) hide show
  1. package/README.md +83 -7
  2. package/dist/Accordion/Accordion.torp +43 -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 +6 -6
  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 +25 -4
  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 +70 -0
  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 +83 -0
  43. package/dist/Charts/ChartYAxis.torp +105 -0
  44. package/dist/Charts/ColumnChart.torp +70 -0
  45. package/dist/{Chart → Charts}/GridLines.torp +7 -4
  46. package/dist/Charts/LineChart.torp +73 -0
  47. package/dist/{Chart → Charts}/PieChart.torp +14 -20
  48. package/dist/{Chart → Charts}/ScatterChart.torp +45 -38
  49. package/dist/{Chart → Charts}/SparkLine.torp +16 -19
  50. package/dist/{Chart → Charts}/XAxis.torp +18 -12
  51. package/dist/{Chart → Charts}/YAxis.torp +15 -9
  52. package/dist/Charts/index.d.ts +469 -0
  53. package/dist/Charts/index.js +17 -0
  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 +112 -0
  60. package/dist/ComboBox/ComboBoxInput.torp +145 -0
  61. package/dist/ComboBox/ComboBoxTrigger.torp +49 -0
  62. package/dist/ComboBox/ComboBoxTypes.ts +39 -0
  63. package/dist/ComboBox/index.d.ts +152 -0
  64. package/dist/ComboBox/index.js +6 -0
  65. package/dist/CommandPalette/CommandPalette.torp +220 -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 +39 -52
  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 +5 -18
  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 +311 -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 +33 -30
  91. package/dist/Dialog/DialogBody.torp +5 -9
  92. package/dist/Dialog/DialogButton.torp +14 -11
  93. package/dist/Dialog/DialogFooter.torp +3 -7
  94. package/dist/Dialog/DialogHeader.torp +6 -10
  95. package/dist/Dialog/DialogTypes.ts +2 -2
  96. package/dist/Dialog/index.d.ts +28 -26
  97. package/dist/Dialog/index.js +6 -22
  98. package/dist/Disclosure/Disclosure.torp +18 -18
  99. package/dist/Disclosure/DisclosureContent.torp +5 -6
  100. package/dist/Disclosure/DisclosureHeader.torp +7 -11
  101. package/dist/Disclosure/DisclosureTrigger.torp +4 -5
  102. package/dist/Disclosure/DisclosureTypes.ts +1 -1
  103. package/dist/Disclosure/index.d.ts +14 -16
  104. package/dist/Disclosure/index.js +5 -18
  105. package/dist/Drawer/Drawer.torp +21 -51
  106. package/dist/Drawer/DrawerContent.torp +67 -13
  107. package/dist/Drawer/DrawerOverlay.torp +4 -4
  108. package/dist/Drawer/DrawerTrigger.torp +16 -4
  109. package/dist/Drawer/DrawerTypes.ts +4 -1
  110. package/dist/Drawer/index.d.ts +16 -20
  111. package/dist/Drawer/index.js +5 -18
  112. package/dist/FocusGroup/FocusGroup.torp +70 -0
  113. package/dist/FocusGroup/FocusGroupButton.torp +59 -0
  114. package/dist/FocusGroup/FocusGroupTypes.ts +14 -0
  115. package/dist/FocusGroup/index.d.ts +44 -0
  116. package/dist/FocusGroup/index.js +3 -0
  117. package/dist/Form/CheckBox.torp +28 -20
  118. package/dist/Form/Field.torp +17 -17
  119. package/dist/Form/File.torp +31 -22
  120. package/dist/Form/FileDrop.torp +187 -0
  121. package/dist/Form/Form.torp +24 -22
  122. package/dist/Form/FormTypes.ts +2 -0
  123. package/dist/Form/FormValues.torp +37 -0
  124. package/dist/Form/Hidden.torp +4 -12
  125. package/dist/Form/Input.torp +38 -32
  126. package/dist/Form/Label.torp +3 -3
  127. package/dist/Form/Message.torp +13 -5
  128. package/dist/Form/MessageSummary.torp +39 -0
  129. package/dist/Form/Option.torp +11 -7
  130. package/dist/Form/Select.torp +32 -28
  131. package/dist/Form/TextArea.torp +35 -31
  132. package/dist/Form/Toggle.torp +109 -0
  133. package/dist/Form/index.d.ts +198 -77
  134. package/dist/Form/index.js +16 -46
  135. package/dist/ListBox/ListBox.torp +374 -0
  136. package/dist/ListBox/ListBoxItem.torp +131 -0
  137. package/dist/ListBox/ListBoxSeparator.torp +29 -0
  138. package/dist/ListBox/ListBoxTypes.ts +41 -0
  139. package/dist/ListBox/LoadedListBoxItems.torp +41 -0
  140. package/dist/ListBox/index.d.ts +136 -0
  141. package/dist/ListBox/index.js +4 -0
  142. package/dist/MaskedInput/MaskedInput.torp +143 -0
  143. package/dist/MaskedInput/index.d.ts +52 -0
  144. package/dist/MaskedInput/index.js +2 -0
  145. package/dist/Menu/Menu.torp +90 -70
  146. package/dist/Menu/MenuButton.torp +17 -10
  147. package/dist/Menu/MenuCheck.torp +22 -18
  148. package/dist/Menu/MenuGroup.torp +4 -4
  149. package/dist/Menu/MenuIndicator.torp +5 -13
  150. package/dist/Menu/MenuPopout.torp +20 -50
  151. package/dist/Menu/MenuPopoutContent.torp +15 -66
  152. package/dist/Menu/MenuPopoutTrigger.torp +19 -100
  153. package/dist/Menu/MenuRadio.torp +25 -26
  154. package/dist/Menu/MenuRadioGroup.torp +25 -68
  155. package/dist/Menu/MenuSeparator.torp +3 -2
  156. package/dist/Menu/MenuTypes.ts +12 -21
  157. package/dist/Menu/index.d.ts +45 -51
  158. package/dist/Menu/index.js +12 -46
  159. package/dist/MenuBar/MenuBar.torp +31 -83
  160. package/dist/MenuBar/MenuBarItem.torp +34 -55
  161. package/dist/MenuBar/MenuBarItemContent.torp +15 -101
  162. package/dist/MenuBar/MenuBarItemTrigger.torp +20 -55
  163. package/dist/MenuBar/MenuBarTypes.ts +13 -12
  164. package/dist/MenuBar/index.d.ts +17 -19
  165. package/dist/MenuBar/index.js +5 -18
  166. package/dist/Modal/Modal.torp +28 -48
  167. package/dist/Modal/ModalContent.torp +21 -44
  168. package/dist/Modal/ModalOverlay.torp +4 -4
  169. package/dist/Modal/ModalTrigger.torp +16 -32
  170. package/dist/Modal/ModalTypes.ts +6 -17
  171. package/dist/Modal/index.d.ts +17 -29
  172. package/dist/Modal/index.js +29 -28
  173. package/dist/Modal/index.js.map +1 -1
  174. package/dist/Modal/showPrompt.ts +2 -2
  175. package/dist/NavMenu/NavMenu.torp +22 -104
  176. package/dist/NavMenu/NavMenuButton.torp +10 -8
  177. package/dist/NavMenu/NavMenuElement.torp +6 -7
  178. package/dist/NavMenu/NavMenuGroup.torp +5 -5
  179. package/dist/NavMenu/NavMenuGroupHeader.torp +5 -8
  180. package/dist/NavMenu/NavMenuLink.torp +7 -10
  181. package/dist/NavMenu/NavMenuPopout.torp +16 -61
  182. package/dist/NavMenu/NavMenuPopoutContent.torp +14 -73
  183. package/dist/NavMenu/NavMenuPopoutTrigger.torp +47 -0
  184. package/dist/NavMenu/NavMenuSeparator.torp +3 -6
  185. package/dist/NavMenu/NavMenuTypes.ts +6 -15
  186. package/dist/NavMenu/index.d.ts +55 -55
  187. package/dist/NavMenu/index.js +11 -42
  188. package/dist/Notification/Notification.torp +12 -24
  189. package/dist/Notification/NotificationClose.torp +6 -3
  190. package/dist/Notification/NotificationContainer.torp +3 -7
  191. package/dist/Notification/NotificationContent.torp +3 -7
  192. package/dist/Notification/NotificationTypes.ts +1 -1
  193. package/dist/Notification/index.d.ts +15 -12
  194. package/dist/Notification/index.js +22 -32
  195. package/dist/Notification/index.js.map +1 -1
  196. package/dist/NumberInput/NumberInput.torp +255 -0
  197. package/dist/NumberInput/index.d.ts +44 -0
  198. package/dist/NumberInput/index.js +2 -0
  199. package/dist/Pagination/Pagination.torp +152 -0
  200. package/dist/Pagination/PaginationItem.torp +137 -0
  201. package/dist/Pagination/PaginationTypes.ts +22 -0
  202. package/dist/Pagination/buildPages.ts +61 -0
  203. package/dist/Pagination/index.d.ts +63 -0
  204. package/dist/Pagination/index.js +3 -0
  205. package/dist/Popover/Popover.torp +39 -52
  206. package/dist/Popover/PopoverContent.torp +27 -76
  207. package/dist/Popover/PopoverHover.torp +90 -0
  208. package/dist/Popover/PopoverTrigger.torp +17 -40
  209. package/dist/Popover/PopoverTypes.ts +5 -18
  210. package/dist/Popover/index.d.ts +40 -25
  211. package/dist/Popover/index.js +18 -18
  212. package/dist/Popover/index.js.map +1 -1
  213. package/dist/Progress/Progress.torp +88 -0
  214. package/dist/Progress/index.d.ts +37 -0
  215. package/dist/Progress/index.js +2 -0
  216. package/dist/Rating/Rating.torp +225 -0
  217. package/dist/Rating/index.d.ts +44 -0
  218. package/dist/Rating/index.js +2 -0
  219. package/dist/SegmentedControl/SegmentedControl.torp +233 -0
  220. package/dist/SegmentedControl/SegmentedControlTypes.ts +26 -0
  221. package/dist/SegmentedControl/SegmentedItem.torp +78 -0
  222. package/dist/SegmentedControl/index.d.ts +74 -0
  223. package/dist/SegmentedControl/index.js +3 -0
  224. package/dist/SelectBox/SelectBox.torp +190 -0
  225. package/dist/SelectBox/SelectBoxContent.torp +107 -0
  226. package/dist/SelectBox/SelectBoxTrigger.torp +141 -0
  227. package/dist/SelectBox/SelectBoxTypes.ts +33 -0
  228. package/dist/SelectBox/index.d.ts +112 -0
  229. package/dist/SelectBox/index.js +5 -0
  230. package/dist/Slider/Slider.torp +248 -0
  231. package/dist/Slider/index.d.ts +45 -0
  232. package/dist/Slider/index.js +2 -0
  233. package/dist/Splitter/Splitter.torp +156 -0
  234. package/dist/Splitter/SplitterHandle.torp +161 -0
  235. package/dist/Splitter/SplitterPane.torp +63 -0
  236. package/dist/Splitter/SplitterTypes.ts +31 -0
  237. package/dist/Splitter/index.d.ts +103 -0
  238. package/dist/Splitter/index.js +4 -0
  239. package/dist/Stepper/Stepper.torp +146 -0
  240. package/dist/Stepper/StepperMarker.torp +24 -0
  241. package/dist/Stepper/StepperNext.torp +44 -0
  242. package/dist/Stepper/StepperPrevious.torp +44 -0
  243. package/dist/Stepper/StepperStep.torp +90 -0
  244. package/dist/Stepper/StepperTypes.ts +30 -0
  245. package/dist/Stepper/index.d.ts +149 -0
  246. package/dist/Stepper/index.js +6 -0
  247. package/dist/TabGroup/TabContent.torp +6 -6
  248. package/dist/TabGroup/TabGroup.torp +97 -113
  249. package/dist/TabGroup/TabGroupTypes.ts +3 -3
  250. package/dist/TabGroup/TabHeader.torp +23 -19
  251. package/dist/TabGroup/TabList.torp +8 -21
  252. package/dist/TabGroup/TabTrigger.torp +6 -7
  253. package/dist/TabGroup/index.d.ts +26 -26
  254. package/dist/TabGroup/index.js +6 -22
  255. package/dist/TagInput/TagInput.torp +378 -0
  256. package/dist/TagInput/index.d.ts +48 -0
  257. package/dist/TagInput/index.js +2 -0
  258. package/dist/TimePicker/TimePicker.torp +462 -0
  259. package/dist/TimePicker/index.d.ts +53 -0
  260. package/dist/TimePicker/index.js +2 -0
  261. package/dist/ToolBar/ToolBar.torp +117 -0
  262. package/dist/ToolBar/ToolBarButton.torp +56 -0
  263. package/dist/ToolBar/ToolBarElement.torp +51 -0
  264. package/dist/ToolBar/ToolBarPopout.torp +45 -0
  265. package/dist/ToolBar/ToolBarPopoutContent.torp +34 -0
  266. package/dist/ToolBar/ToolBarPopoutTrigger.torp +47 -0
  267. package/dist/ToolBar/ToolBarSeparator.torp +21 -0
  268. package/dist/ToolBar/ToolBarTypes.ts +28 -0
  269. package/dist/ToolBar/index.d.ts +210 -0
  270. package/dist/ToolBar/index.js +8 -0
  271. package/dist/Tree/Tree.torp +395 -0
  272. package/dist/Tree/TreeItem.torp +176 -0
  273. package/dist/Tree/TreeItemChildren.torp +60 -0
  274. package/dist/Tree/TreeItemContent.torp +46 -0
  275. package/dist/Tree/TreeItemLabel.torp +94 -0
  276. package/dist/Tree/TreeItemTrigger.torp +78 -0
  277. package/dist/Tree/TreeLoadedChildren.torp +122 -0
  278. package/dist/Tree/TreeTypes.ts +63 -0
  279. package/dist/Tree/index.d.ts +262 -0
  280. package/dist/Tree/index.js +7 -0
  281. package/dist/icons/ChevronDoubleLeft.torp +19 -0
  282. package/dist/icons/ChevronDoubleRight.torp +19 -0
  283. package/dist/icons/index.d.ts +61 -1
  284. package/dist/icons/index.js +17 -58
  285. package/dist/loader-DEeG5vFY.js +44 -0
  286. package/dist/loader-DEeG5vFY.js.map +1 -0
  287. package/dist/motion/index.d.ts +12 -8
  288. package/dist/motion/index.d.ts.map +1 -0
  289. package/dist/motion/index.js +72 -88
  290. package/dist/motion/index.js.map +1 -1
  291. package/dist/mount/draggable.ts +63 -0
  292. package/dist/mount/dropTarget.ts +108 -0
  293. package/dist/mount/hoverTrigger.ts +1 -1
  294. package/dist/utils/$handle.ts +23 -0
  295. package/dist/utils/LoadedItems.torp +47 -0
  296. package/dist/utils/PopoutClickTrigger.torp +99 -0
  297. package/dist/utils/PopoutTypes.ts +82 -0
  298. package/dist/utils/SubmenuPopout.torp +89 -0
  299. package/dist/utils/SubmenuPopoutContent.torp +87 -0
  300. package/dist/utils/SubmenuPopoutTrigger.torp +160 -0
  301. package/dist/utils/chartColors.ts +13 -13
  302. package/dist/utils/chartLayout.ts +143 -0
  303. package/dist/utils/chartUtils.ts +1 -1
  304. package/dist/utils/colorUtils.ts +103 -0
  305. package/dist/utils/createItemGroup.ts +152 -0
  306. package/dist/utils/dateUtils.ts +8 -7
  307. package/dist/utils/documentEvents.ts +4 -4
  308. package/dist/utils/dragState.ts +86 -0
  309. package/dist/utils/focusGroup.ts +204 -0
  310. package/dist/utils/focusItem.ts +44 -0
  311. package/dist/utils/formField.ts +70 -0
  312. package/dist/utils/hoverOpen.ts +50 -0
  313. package/dist/utils/hoverReveal.ts +194 -0
  314. package/dist/utils/loader.ts +113 -0
  315. package/dist/utils/maskUtils.ts +98 -0
  316. package/dist/utils/popoutContent.ts +137 -0
  317. package/dist/utils/reorderList.ts +52 -0
  318. package/package.json +116 -25
  319. package/dist/Accordion/index.js.map +0 -1
  320. package/dist/Breadcrumb/index.js.map +0 -1
  321. package/dist/Calendar/index.js.map +0 -1
  322. package/dist/Chart/BarChart.torp +0 -159
  323. package/dist/Chart/LineChart.torp +0 -196
  324. package/dist/Chart/index.d.ts +0 -220
  325. package/dist/Chart/index.js +0 -34
  326. package/dist/Chart/index.js.map +0 -1
  327. package/dist/Contextual/index.js.map +0 -1
  328. package/dist/Dialog/index.js.map +0 -1
  329. package/dist/Disclosure/index.js.map +0 -1
  330. package/dist/Drawer/index.js.map +0 -1
  331. package/dist/Form/index.js.map +0 -1
  332. package/dist/Menu/index.js.map +0 -1
  333. package/dist/MenuBar/index.js.map +0 -1
  334. package/dist/NavMenu/NavMenuPopoutButton.torp +0 -129
  335. package/dist/NavMenu/index.js.map +0 -1
  336. package/dist/TabGroup/index.js.map +0 -1
  337. package/dist/icons/index.js.map +0 -1
  338. package/dist/index.d.ts +0 -19
  339. package/dist/metafile-esm.json +0 -1
  340. package/dist/motion/fade.ts +0 -16
  341. package/dist/motion/grow.ts +0 -23
  342. package/dist/motion/measure.ts +0 -28
  343. package/dist/motion/slide.ts +0 -45
  344. package/dist/mount/index.d.ts +0 -2
  345. package/dist/utils/index.d.ts +0 -2
  346. /package/dist/{Chart → Charts}/DataSeries.ts +0 -0
@@ -0,0 +1,222 @@
1
+ import { type Bindable, type ClassValue, type StyleValue } from "@torpor/view";
2
+ import {
3
+ Calendar,
4
+ CalendarDay,
5
+ CalendarDayHeader,
6
+ CalendarGrid,
7
+ CalendarGridHeader,
8
+ CalendarHeader,
9
+ CalendarHeaderTrigger,
10
+ } from "../Calendar/index";
11
+ import {
12
+ type FieldContext,
13
+ FieldContextName,
14
+ type FormContext,
15
+ FormContextName,
16
+ } from "../Form/FormTypes";
17
+ import { formatDate } from "../utils/dateUtils";
18
+ import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
19
+ import getId from "../utils/getId";
20
+ import createFormField from "../utils/formField";
21
+ import DatePickerHeader from "./DatePickerHeader.torp";
22
+
23
+ interface DatePickerProps {
24
+ /** An ID for the root element */
25
+ id?: string;
26
+ /** Classes for the root element */
27
+ class?: ClassValue;
28
+ /** Styles for the root element */
29
+ style?: StyleValue;
30
+ /** The name of the date picker's form data field */
31
+ name?: string;
32
+ /** The selected date */
33
+ value?: Bindable<Date | undefined>;
34
+ /** Text to display when the value is empty */
35
+ placeholder?: string;
36
+ /** The format for the displayed date (e.g. "d MMM yyyy") */
37
+ dateFormat?: string;
38
+ /** If set to true, the user cannot interact with this date picker */
39
+ disabled?: boolean;
40
+ /** An accessible name for the trigger button */
41
+ ariaLabel?: string;
42
+ /** Raised when the value has changed */
43
+ onchange?: (value: Date) => void;
44
+ }
45
+
46
+ /**
47
+ * An input for selecting a date from a calendar popout.
48
+ *
49
+ * The structure of a DatePicker is a trigger button that displays the
50
+ * selected date, with a selectable Calendar in a popout.
51
+ */
52
+ export default function DatePicker($props: DatePickerProps | undefined) {
53
+ let defaultId = getId();
54
+
55
+ let content: HTMLDivElement;
56
+ let closeOnClick = false;
57
+
58
+ // Wire up the enclosing Form and Field, if any
59
+ const form = createFormField(
60
+ $props?.name,
61
+ $context[FormContextName] as FormContext | undefined,
62
+ $context[FieldContextName] as FieldContext | undefined,
63
+ );
64
+
65
+ function getDateFormat(): string {
66
+ return $props?.dateFormat ?? "yyyy-MM-dd";
67
+ }
68
+ function getDisabled(): boolean {
69
+ return $props?.disabled === true;
70
+ }
71
+
72
+ let $state = $watch({
73
+ value: undefined as Date | undefined,
74
+ visible: false,
75
+ triggerId: "",
76
+ contentId: "",
77
+ });
78
+
79
+ $bind($state, $props, "value");
80
+
81
+ // Set default IDs so that the trigger can use aria-controls and the
82
+ // content can be labelled by the trigger
83
+ $run(() => {
84
+ $state.triggerId = `${$props?.id ?? defaultId}-trigger`;
85
+ $state.contentId = `${$props?.id ?? defaultId}-content`;
86
+ });
87
+
88
+ function displayText(): string {
89
+ const value = $state.value;
90
+ if (value) {
91
+ return formatDate($unwrap(value), getDateFormat());
92
+ }
93
+ return $props?.placeholder || "-";
94
+ }
95
+
96
+ function toggle() {
97
+ setVisible(!$state.visible);
98
+ }
99
+
100
+ function setVisible(visible: boolean) {
101
+ $state.visible = visible;
102
+ if (visible) {
103
+ // HACK: Because this may be shown with a click, we need to wait for
104
+ // another mousedown before we close on click. Otherwise the click
105
+ // immediately bubbles up to the document
106
+ closeOnClick = false;
107
+ addDocumentEvent("mousedown", handleDocumentMouseDown);
108
+ addDocumentEvent("click", handleDocumentClick);
109
+ focusCalendar();
110
+ } else {
111
+ removeDocumentEvent("mousedown", handleDocumentMouseDown);
112
+ removeDocumentEvent("click", handleDocumentClick);
113
+ }
114
+ }
115
+
116
+ function focusCalendar() {
117
+ // Wait for the popout to be shown before focusing it
118
+ setTimeout(() => {
119
+ const grid = content?.querySelector(".torp-calendar-grid");
120
+ if (grid instanceof HTMLElement && !getDisabled()) {
121
+ grid.focus();
122
+ }
123
+ });
124
+ }
125
+
126
+ function handleDate(date: Date) {
127
+ $state.value = new Date(date);
128
+ $props?.onchange?.($state.value);
129
+ form.handleInput();
130
+ setVisible(false);
131
+ focusTrigger();
132
+ }
133
+
134
+ function handleClose() {
135
+ setVisible(false);
136
+ focusTrigger();
137
+ }
138
+
139
+ function focusTrigger() {
140
+ const trigger = document.getElementById($state.triggerId);
141
+ if (trigger instanceof HTMLElement) {
142
+ trigger.focus();
143
+ }
144
+ }
145
+
146
+ function handleDocumentMouseDown() {
147
+ closeOnClick = true;
148
+ }
149
+
150
+ function handleDocumentClick(e: MouseEvent) {
151
+ if (closeOnClick && content && !content.contains(e.target as HTMLElement)) {
152
+ e.preventDefault();
153
+ setVisible(false);
154
+ }
155
+ }
156
+
157
+ function handleClick(e: MouseEvent) {
158
+ // Don't close when the trigger itself is clicked
159
+ e.stopPropagation();
160
+ toggle();
161
+ }
162
+
163
+ @render {
164
+ <div
165
+ {$props?.id}
166
+ class={["torp-date-picker", getDisabled() ? "torp-date-picker-disabled" : undefined, $props?.class]}
167
+ {$props?.style}
168
+ >
169
+ <button
170
+ type="button"
171
+ id={$state.triggerId}
172
+ class="torp-date-picker-trigger"
173
+ aria-haspopup="dialog"
174
+ aria-expanded={$state.visible ? "true" : "false"}
175
+ aria-controls={$state.visible ? $state.contentId : undefined}
176
+ aria-label={$props?.ariaLabel}
177
+ data-valid={form.valid ? "valid" : "invalid"}
178
+ aria-describedby={form.messageId}
179
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
180
+ onblur={() => form.handleBlur()}
181
+ disabled={getDisabled()}
182
+ onclick={handleClick}
183
+ >
184
+ {displayText()}
185
+ </button>
186
+
187
+ <div
188
+ id={$state.contentId}
189
+ class={["torp-date-picker-content", $state.visible ? undefined : "hidden"]}
190
+ aria-hidden={$state.visible ? undefined : "true"}
191
+ &ref={content}
192
+ >
193
+ <Calendar selectable {$state.value} onchange={handleDate} onclose={handleClose}>
194
+ <CalendarHeader>
195
+ <CalendarHeaderTrigger type="previous" />
196
+ <DatePickerHeader class="torp-date-picker-header" />
197
+ <CalendarHeaderTrigger type="next" />
198
+ </CalendarHeader>
199
+ <CalendarGridHeader>
200
+ @for (let day of $slot.days) {
201
+ <CalendarDayHeader {day.dayName} {day.shortDayName} />
202
+ }
203
+ </CalendarGridHeader>
204
+ <CalendarGrid>
205
+ <CalendarDay date={$slot.day.date} muted={$slot.day.muted} active={$slot.day.active} />
206
+ </CalendarGrid>
207
+ </Calendar>
208
+ </div>
209
+ </div>
210
+
211
+ @if (form.name) {
212
+ <input type="hidden" name={form.name} value={$state.value ? formatDate($unwrap($state.value), "yyyy-MM-dd") : ""} />
213
+ }
214
+ }
215
+
216
+ @style {
217
+ /* Visibility only -- see SelectBoxContent */
218
+ .torp-date-picker-content.hidden {
219
+ display: none;
220
+ }
221
+ }
222
+ }
@@ -0,0 +1,20 @@
1
+ import { CalendarContextName, type CalendarContext } from "../Calendar/CalendarTypes";
2
+ import { monthNames } from "../utils/dateUtils";
3
+
4
+ /**
5
+ * Displays the month and year of the currently visible date in a Calendar.
6
+ * Must be contained within a Calendar.
7
+ */
8
+ export default function DatePickerHeader($props: { class?: string }) {
9
+ const context = $context[CalendarContextName] as CalendarContext;
10
+ if (!context) {
11
+ throw new Error("DatePickerHeader must be contained within a Calendar");
12
+ }
13
+ const $state = context.state;
14
+
15
+ @render {
16
+ <h4 class={$props.class}>
17
+ {monthNames[$state.visibleDate.getMonth()]} {$state.visibleDate.getFullYear()}
18
+ </h4>
19
+ }
20
+ }
@@ -0,0 +1,55 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface DatePickerProps {
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
+ /** The name of the date picker's form data field */
12
+ name?: string;
13
+ /** The selected date */
14
+ value?: Bindable<Date | undefined>;
15
+ /** Text to display when the value is empty */
16
+ placeholder?: string;
17
+ /** The format for the displayed date (e.g. "d MMM yyyy") */
18
+ dateFormat?: string;
19
+ /** If set to true, the user cannot interact with this date picker */
20
+ disabled?: boolean;
21
+ /** An accessible name for the trigger button */
22
+ ariaLabel?: string;
23
+ /** Raised when the value has changed */
24
+ onchange?: (value: Date) => void;
25
+ }
26
+
27
+ /**
28
+ * An input for selecting a date from a calendar popout.
29
+ *
30
+ * The structure of a DatePicker is a trigger button that displays the
31
+ * selected date, with a selectable Calendar in a popout.
32
+ */
33
+ declare function DatePicker(
34
+ $parent: ParentNode,
35
+ $anchor: Node | null,
36
+ $props: DatePickerProps | undefined,
37
+ $context?: Record<PropertyKey, any>,
38
+ $slots?: Record<string, SlotRender>,
39
+ ): void;
40
+
41
+
42
+
43
+ /**
44
+ * Displays the month and year of the currently visible date in a Calendar.
45
+ * Must be contained within a Calendar.
46
+ */
47
+ declare function DatePickerHeader(
48
+ $parent: ParentNode,
49
+ $anchor: Node | null,
50
+ $props: { class?: string },
51
+ $context?: Record<PropertyKey, any>,
52
+ $slots?: Record<string, SlotRender>,
53
+ ): void;
54
+
55
+ export { DatePicker, DatePickerHeader };
@@ -0,0 +1,2 @@
1
+ import DatePicker from "./DatePicker.torp";
2
+ export { DatePicker };
@@ -0,0 +1,311 @@
1
+ import { type Bindable, type ClassValue, type StyleValue } from "@torpor/view";
2
+ import {
3
+ Calendar,
4
+ CalendarDayHeader,
5
+ CalendarGrid,
6
+ CalendarGridHeader,
7
+ CalendarHeader,
8
+ CalendarHeaderTrigger,
9
+ } from "../Calendar/index";
10
+ import DatePickerHeader from "../DatePicker/DatePickerHeader.torp";
11
+ import {
12
+ type FieldContext,
13
+ FieldContextName,
14
+ type FormContext,
15
+ FormContextName,
16
+ } from "../Form/FormTypes";
17
+ import { areDatesEqual, formatDate } from "../utils/dateUtils";
18
+ import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
19
+ import getId from "../utils/getId";
20
+ import createFormField from "../utils/formField";
21
+ import { type DateRange, type DateRangePickerContext, DateRangePickerContextName } from "./DateRangePickerTypes";
22
+ import DateRangePickerDay from "./DateRangePickerDay.torp";
23
+
24
+ interface DateRangePickerProps {
25
+ /** An ID for the root element */
26
+ id?: string;
27
+ /** Classes for the root element */
28
+ class?: ClassValue;
29
+ /** Styles for the root element */
30
+ style?: StyleValue;
31
+ /** The name of the date range picker's form data fields; "-start" and "-end" are appended */
32
+ name?: string;
33
+ /** The selected date range */
34
+ value?: Bindable<DateRange | undefined>;
35
+ /** Text to display when the value is empty */
36
+ placeholder?: string;
37
+ /** The format for the displayed dates (e.g. "d MMM yyyy") */
38
+ dateFormat?: string;
39
+ /** If set to true, the user cannot interact with this date range picker */
40
+ disabled?: boolean;
41
+ /** An accessible name for the trigger button */
42
+ ariaLabel?: string;
43
+ /** Raised when a complete range has been selected */
44
+ onchange?: (value: DateRange) => void;
45
+ }
46
+
47
+ /**
48
+ * An input for selecting a range of dates from a calendar popout. The first
49
+ * click sets the start of the range and the second sets its end; clicking a
50
+ * date before the start begins a new range there, and a third click starts
51
+ * over.
52
+ *
53
+ * The structure of a DateRangePicker is a trigger button that displays the
54
+ * selected range, with a selectable Calendar in a popout.
55
+ */
56
+ export default function DateRangePicker($props: DateRangePickerProps | undefined) {
57
+ let defaultId = getId();
58
+
59
+ let content: HTMLDivElement;
60
+ let closeOnClick = false;
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 getDateFormat(): string {
70
+ return $props?.dateFormat ?? "yyyy-MM-dd";
71
+ }
72
+ function getDisabled(): boolean {
73
+ return $props?.disabled === true;
74
+ }
75
+
76
+ let $state = $watch({
77
+ value: $props?.value as DateRange | undefined,
78
+ start: $props?.value?.start as Date | undefined,
79
+ end: $props?.value?.end as Date | undefined,
80
+ preview: undefined as Date | undefined,
81
+ visible: false,
82
+ triggerId: "",
83
+ contentId: "",
84
+ });
85
+
86
+ // Seed from the props so the initial value displays; $bind handles later
87
+ // pushes both ways
88
+ $bind($state, $props, "value");
89
+
90
+ // Set default IDs so that the trigger can use aria-controls and the
91
+ // content can be labelled by the trigger
92
+ $run(() => {
93
+ $state.triggerId = `${$props?.id ?? defaultId}-trigger`;
94
+ $state.contentId = `${$props?.id ?? defaultId}-content`;
95
+ });
96
+
97
+ // Set the context that days use to render range highlighting. Dates are
98
+ // unwrapped so that consumers can call date methods on them
99
+ const context: DateRangePickerContext = {
100
+ getStart,
101
+ getEnd: () => ($state.end ? ($unwrap($state.end) as Date) : undefined),
102
+ getPreview: () => ($state.preview ? ($unwrap($state.preview) as Date) : undefined),
103
+ isSelecting,
104
+ setPreview: (date) => {
105
+ if (isSelecting()) {
106
+ $state.preview = date ? new Date(date) : undefined;
107
+ }
108
+ },
109
+ };
110
+ $context[DateRangePickerContextName] = context;
111
+
112
+ function isSelecting(): boolean {
113
+ return $state.start !== undefined && $state.end === undefined;
114
+ }
115
+
116
+ // The range's endpoints count as selected, so their gridcells get
117
+ // aria-selected and screen readers announce them
118
+ function isDaySelected(date: Date): boolean {
119
+ const start = getStart();
120
+ if (start && areDatesEqual(date, start)) return true;
121
+ const end = $state.end ? ($unwrap($state.end) as Date) : undefined;
122
+ return !!end && areDatesEqual(date, end);
123
+ }
124
+
125
+ // The plain (unwrapped) start date, for comparisons
126
+ function getStart(): Date | undefined {
127
+ return $state.start ? ($unwrap($state.start) as Date) : undefined;
128
+ }
129
+
130
+ function displayText(): string {
131
+ const value = $state.value;
132
+ if (value?.start && value?.end) {
133
+ return `${formatDate($unwrap(value.start), getDateFormat())} – ${formatDate($unwrap(value.end), getDateFormat())}`;
134
+ }
135
+ return $props?.placeholder || "-";
136
+ }
137
+
138
+ function toggle() {
139
+ setVisible(!$state.visible);
140
+ }
141
+
142
+ function setVisible(visible: boolean) {
143
+ $state.visible = visible;
144
+ if (visible) {
145
+ // HACK: Because this may be shown with a click, we need to wait for
146
+ // another mousedown before we close on click. Otherwise the click
147
+ // immediately bubbles up to the document
148
+ closeOnClick = false;
149
+ addDocumentEvent("mousedown", handleDocumentMouseDown);
150
+ addDocumentEvent("click", handleDocumentClick);
151
+ focusCalendar();
152
+ } else {
153
+ removeDocumentEvent("mousedown", handleDocumentMouseDown);
154
+ removeDocumentEvent("click", handleDocumentClick);
155
+ }
156
+ }
157
+
158
+ function focusCalendar() {
159
+ // Wait for the popout to be shown before focusing it
160
+ setTimeout(() => {
161
+ const grid = content?.querySelector(".torp-calendar-grid");
162
+ if (grid instanceof HTMLElement && !getDisabled()) {
163
+ grid.focus();
164
+ }
165
+ });
166
+ }
167
+
168
+ function handleDay(date: Date) {
169
+ if (getDisabled()) return;
170
+
171
+ const start = getStart();
172
+ if (!isSelecting()) {
173
+ // Start a new range
174
+ $state.start = new Date(date);
175
+ $state.end = undefined;
176
+ $state.preview = undefined;
177
+ } else if (date < start!) {
178
+ // A date before the start begins a new range there
179
+ $state.start = new Date(date);
180
+ $state.preview = undefined;
181
+ } else {
182
+ // Complete the range
183
+ $state.end = new Date(date);
184
+ $state.preview = undefined;
185
+ complete();
186
+ }
187
+ }
188
+
189
+ function complete() {
190
+ $state.value = {
191
+ start: new Date($state.start!),
192
+ end: new Date($state.end!),
193
+ };
194
+ $props?.onchange?.($state.value);
195
+ form.handleInput();
196
+ setVisible(false);
197
+ focusTrigger();
198
+ }
199
+
200
+ function handleClose() {
201
+ setVisible(false);
202
+ focusTrigger();
203
+ }
204
+
205
+ function focusTrigger() {
206
+ const trigger = document.getElementById($state.triggerId);
207
+ if (trigger instanceof HTMLElement) {
208
+ trigger.focus();
209
+ }
210
+ }
211
+
212
+ function handleDocumentMouseDown() {
213
+ closeOnClick = true;
214
+ }
215
+
216
+ function handleDocumentClick(e: MouseEvent) {
217
+ if (closeOnClick && content && !content.contains(e.target as HTMLElement)) {
218
+ e.preventDefault();
219
+ setVisible(false);
220
+ }
221
+ }
222
+
223
+ function handleClick(e: MouseEvent) {
224
+ // Don't close when the trigger itself is clicked
225
+ e.stopPropagation();
226
+ toggle();
227
+ }
228
+
229
+ @render {
230
+ <div
231
+ {$props?.id}
232
+ class={[
233
+ "torp-date-range-picker",
234
+ getDisabled() ? "torp-date-range-picker-disabled" : undefined,
235
+ $props?.class,
236
+ ]}
237
+ {$props?.style}
238
+ >
239
+ <button
240
+ type="button"
241
+ id={$state.triggerId}
242
+ class="torp-date-range-picker-trigger"
243
+ aria-haspopup="dialog"
244
+ aria-expanded={$state.visible ? "true" : "false"}
245
+ aria-controls={$state.visible ? $state.contentId : undefined}
246
+ aria-label={$props?.ariaLabel}
247
+ data-valid={form.valid ? "valid" : "invalid"}
248
+ aria-describedby={form.messageId}
249
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
250
+ onblur={() => form.handleBlur()}
251
+ disabled={getDisabled()}
252
+ onclick={handleClick}
253
+ >
254
+ {displayText()}
255
+ </button>
256
+
257
+ <div
258
+ id={$state.contentId}
259
+ class={["torp-date-range-picker-content", $state.visible ? undefined : "hidden"]}
260
+ aria-hidden={$state.visible ? undefined : "true"}
261
+ &ref={content}
262
+ >
263
+ <Calendar
264
+ selectable
265
+ value={$state.start}
266
+ isSelected={isDaySelected}
267
+ onchange={handleDay}
268
+ onclose={handleClose}
269
+ >
270
+ <CalendarHeader>
271
+ <CalendarHeaderTrigger type="previous" />
272
+ <DatePickerHeader class="torp-date-range-picker-header" />
273
+ <CalendarHeaderTrigger type="next" />
274
+ </CalendarHeader>
275
+ <CalendarGridHeader>
276
+ @for (let day of $slot.days) {
277
+ <CalendarDayHeader {day.dayName} {day.shortDayName} />
278
+ }
279
+ </CalendarGridHeader>
280
+ <CalendarGrid>
281
+ <DateRangePickerDay
282
+ date={$slot.day.date}
283
+ muted={$slot.day.muted}
284
+ active={$slot.day.active}
285
+ />
286
+ </CalendarGrid>
287
+ </Calendar>
288
+ </div>
289
+ </div>
290
+
291
+ @if (form.name) {
292
+ <input
293
+ type="hidden"
294
+ name={`${form.name}-start`}
295
+ value={$state.value?.start ? formatDate($unwrap($state.value.start), "yyyy-MM-dd") : ""}
296
+ />
297
+ <input
298
+ type="hidden"
299
+ name={`${form.name}-end`}
300
+ value={$state.value?.end ? formatDate($unwrap($state.value.end), "yyyy-MM-dd") : ""}
301
+ />
302
+ }
303
+ }
304
+
305
+ @style {
306
+ /* Visibility only -- see SelectBoxContent */
307
+ .torp-date-range-picker-content.hidden {
308
+ display: none;
309
+ }
310
+ }
311
+ }