@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,194 @@
1
+ /**
2
+ * Shared hover-to-reveal behavior for hover-activated popout triggers
3
+ * (PopoverHover, ContextualHover).
4
+ *
5
+ * Unlike press activation, hover needs:
6
+ * - an intent delay before opening, so passing over an element doesn't flash
7
+ * a popout (mirrors createHoverOpen for menu items)
8
+ * - an exit grace period after leaving, so moving the pointer from the
9
+ * trigger into the popout content doesn't close it -- entering the content
10
+ * cancels a pending close entirely
11
+ */
12
+
13
+ export interface HoverRevealOptions {
14
+ /** Shows the popout */
15
+ show: () => void;
16
+ /** Hides the popout */
17
+ hide: () => void;
18
+ /** Whether the popout is currently visible */
19
+ isVisible: () => boolean;
20
+ /** Whether hover interaction is ignored entirely */
21
+ disabled?: () => boolean;
22
+ /** The popout content element, for keeping it open when hovered */
23
+ getContent?: () => HTMLElement | null | undefined;
24
+ /** Milliseconds to wait before opening (default 500; 0 opens immediately) */
25
+ hoverDelay?: number;
26
+ /** Milliseconds to wait after the pointer leaves before closing (default 300) */
27
+ exitGrace?: number;
28
+ }
29
+
30
+ export interface HoverRevealHandlers {
31
+ /** Handles the pointer entering the trigger */
32
+ handleEnter: () => void;
33
+ /** Handles the pointer leaving the trigger; closes unless it moved into the content element */
34
+ handleLeave: (e: MouseEvent | FocusEvent) => void;
35
+ /** Handles focus landing on the trigger (opens immediately) */
36
+ handleFocus: () => void;
37
+ /** Handles focus leaving the trigger (closes unless focus moved into the content element) */
38
+ handleBlur: (e: FocusEvent) => void;
39
+ /** Cancels pending open/close timers */
40
+ dispose: () => void;
41
+ }
42
+
43
+ export function createHoverReveal(options: HoverRevealOptions): HoverRevealHandlers {
44
+ const show = options.show;
45
+ const hide = options.hide;
46
+ const isVisible = options.isVisible;
47
+ const disabled = options.disabled ?? (() => false);
48
+ const getContent = options.getContent ?? (() => undefined);
49
+ const hoverDelay = options.hoverDelay ?? 500;
50
+ const exitGrace = options.exitGrace ?? 300;
51
+
52
+ let openTimer: ReturnType<typeof setTimeout> | undefined;
53
+ let closeTimer: ReturnType<typeof setTimeout> | undefined;
54
+ let suppressFocusOpen = false;
55
+
56
+ function clearTimers() {
57
+ if (openTimer !== undefined) {
58
+ clearTimeout(openTimer);
59
+ openTimer = undefined;
60
+ }
61
+ if (closeTimer !== undefined) {
62
+ clearTimeout(closeTimer);
63
+ closeTimer = undefined;
64
+ }
65
+ }
66
+
67
+ function cancelPendingOpen() {
68
+ if (openTimer !== undefined) {
69
+ clearTimeout(openTimer);
70
+ openTimer = undefined;
71
+ }
72
+ }
73
+
74
+ /** While armed, moving into the content cancels the close */
75
+ function scheduleClose() {
76
+ if (closeTimer !== undefined) return;
77
+
78
+ closeTimer = setTimeout(() => {
79
+ closeTimer = undefined;
80
+ stopListening();
81
+ // The content hides with `refocusAnchorOnHide`, which drops
82
+ // focus back onto this trigger -- that is not user intent,
83
+ // so swallow the one focus-driven open that follows
84
+ suppressFocusOpen = true;
85
+ hide();
86
+ }, exitGrace);
87
+
88
+ // Cancel as soon as the pointer or focus lands on the content
89
+ document.addEventListener("mouseover", handleDocumentOver);
90
+ document.addEventListener("focusin", handleDocumentFocusIn);
91
+ }
92
+
93
+ function stopListening() {
94
+ document.removeEventListener("mouseover", handleDocumentOver);
95
+ document.removeEventListener("focusin", handleDocumentFocusIn);
96
+ }
97
+
98
+ function handleDocumentFocusIn(e: FocusEvent) {
99
+ if (closeTimer === undefined) {
100
+ stopListening();
101
+ return;
102
+ }
103
+ if (insideContent(e.target)) {
104
+ clearTimeout(closeTimer);
105
+ closeTimer = undefined;
106
+ stopListening();
107
+ }
108
+ }
109
+
110
+ function handleDocumentOver(e: MouseEvent) {
111
+ if (closeTimer === undefined) {
112
+ document.removeEventListener("mouseover", handleDocumentOver);
113
+ return;
114
+ }
115
+ if (insideContent(e.target)) {
116
+ clearTimeout(closeTimer);
117
+ closeTimer = undefined;
118
+ document.removeEventListener("mouseover", handleDocumentOver);
119
+ }
120
+ }
121
+
122
+ function insideContent(target: EventTarget | null): boolean {
123
+ const content = getContent();
124
+ if (!content || !target) return false;
125
+ return content === target || content.contains(target as Node);
126
+ }
127
+
128
+ return {
129
+ handleEnter() {
130
+ if (disabled()) return;
131
+
132
+ if (closeTimer !== undefined) {
133
+ clearTimeout(closeTimer);
134
+ closeTimer = undefined;
135
+ stopListening();
136
+ }
137
+ if (isVisible()) return;
138
+
139
+ if (hoverDelay <= 0) {
140
+ show();
141
+ return;
142
+ }
143
+ if (openTimer === undefined) {
144
+ openTimer = setTimeout(() => {
145
+ openTimer = undefined;
146
+ show();
147
+ }, hoverDelay);
148
+ }
149
+ },
150
+
151
+ handleLeave(_e) {
152
+ if (disabled()) return;
153
+
154
+ // Just a pass-over: cancel the pending open and stay closed
155
+ if (openTimer !== undefined) {
156
+ cancelPendingOpen();
157
+ return;
158
+ }
159
+ if (!isVisible()) return;
160
+
161
+ scheduleClose();
162
+ },
163
+
164
+ handleFocus() {
165
+ if (disabled()) return;
166
+
167
+ if (suppressFocusOpen) {
168
+ suppressFocusOpen = false;
169
+ return;
170
+ }
171
+
172
+ if (closeTimer !== undefined) {
173
+ clearTimeout(closeTimer);
174
+ closeTimer = undefined;
175
+ stopListening();
176
+ }
177
+ if (!isVisible()) {
178
+ show();
179
+ }
180
+ },
181
+
182
+ handleBlur(_e) {
183
+ if (disabled()) return;
184
+ if (!isVisible()) return;
185
+
186
+ scheduleClose();
187
+ },
188
+
189
+ dispose() {
190
+ clearTimers();
191
+ stopListening();
192
+ },
193
+ };
194
+ }
@@ -0,0 +1,113 @@
1
+ import { $async, $watch } from "@torpor/view";
2
+
3
+ /**
4
+ * Shared contract for loading data from the network, used by components that
5
+ * can take either static data or a loader function (DataGrid now, ComboBox
6
+ * auto-complete and others later).
7
+ *
8
+ * A loader receives a request object describing what to fetch -- paging,
9
+ * search text and sorting are standard; components extend it with their own
10
+ * fields -- plus an AbortSignal so stale requests can be cancelled.
11
+ */
12
+
13
+ /** The base shape of a load request. Components extend this with their own fields. */
14
+ export interface LoadRequest {
15
+ /** 1-based page number to load */
16
+ page?: number;
17
+ /** Number of items per page */
18
+ pageSize?: number;
19
+ /** Search or filter text entered by the user */
20
+ searchText?: string;
21
+ /** The field to sort by */
22
+ sortBy?: string;
23
+ /** The direction to sort in */
24
+ sortDirection?: "asc" | "desc";
25
+ }
26
+
27
+ /**
28
+ * The result of a load: the items for the current request, and optionally
29
+ * the total count (needed for paged components to show a pager).
30
+ */
31
+ export interface LoadResult<T = any> {
32
+ items: T[];
33
+ /** The total number of items across all pages, if known */
34
+ total?: number;
35
+ }
36
+
37
+ /**
38
+ * Reads data for a request. May return a promise or a plain result, and may
39
+ * return just an array of items when no total is available.
40
+ */
41
+ export type Loader<T = any, TRequest extends LoadRequest = LoadRequest> = (
42
+ request: TRequest,
43
+ signal?: AbortSignal,
44
+ ) => Promise<LoadResult<T> | T[]> | LoadResult<T> | T[];
45
+
46
+ /** Wraps a bare array in a LoadResult so consumers always get the same shape */
47
+ export function normalizeLoadResult<T>(result: LoadResult<T> | T[]): LoadResult<T> {
48
+ return Array.isArray(result) ? { items: result } : result;
49
+ }
50
+
51
+ export interface ItemLoaderOptions<T = any, TRequest extends LoadRequest = LoadRequest> {
52
+ /** The loader function to fetch items */
53
+ load: Loader<T, TRequest>;
54
+ /**
55
+ * Builds the request for the current fetch. Called inside the async
56
+ * getter, so reads of reactive state here re-fetch when they change.
57
+ */
58
+ getRequest: () => TRequest;
59
+ /** Called after each successful load with the normalized result */
60
+ onload?: (result: LoadResult<T>) => void;
61
+ }
62
+
63
+ /** Reactive loading state returned by createItemLoader */
64
+ export interface ItemLoaderState<T = any> {
65
+ /** The full result of the current load; suspends while a fetch is pending */
66
+ readonly result: LoadResult<T> | undefined;
67
+ /** The loaded items; suspends while a fetch is pending */
68
+ readonly items: T[];
69
+ }
70
+
71
+ /**
72
+ * Shared loading state for components that read items from a network loader
73
+ * (DataGrid, ComboBox auto-complete etc). Read the returned state's `items`
74
+ * or `result` inside an `@await` boundary: while a fetch is pending the read
75
+ * suspends, and when dependencies read inside `getRequest` change the fetch
76
+ * re-runs.
77
+ *
78
+ * For lazy loading (fetch only once some condition is met), gate the whole
79
+ * rendering of the region that reads this loader behind that condition
80
+ * (`@if`), so the `@await` boundary -- and with it the first fetch --
81
+ * comes into existence when the condition flips.
82
+ *
83
+ * ```
84
+ * let $load = createItemLoader({
85
+ * load: $props.load,
86
+ * getRequest: () => ({ searchText: $state.searchText }),
87
+ * });
88
+ * @await { ...$load.items... } with { <span>Loading…</span> }
89
+ * ```
90
+ */
91
+ export function createItemLoader<T = any, TRequest extends LoadRequest = LoadRequest>(
92
+ options: ItemLoaderOptions<T, TRequest>,
93
+ ): ItemLoaderState<T> {
94
+ return $watch({
95
+ get result(): LoadResult<T> | undefined {
96
+ return $async(() => {
97
+ const promise = Promise.resolve(options.load(options.getRequest())).then(
98
+ normalizeLoadResult,
99
+ );
100
+ // Report successful loads to the consumer; failures surface
101
+ // through the component's error boundary, not this chain
102
+ promise.then(
103
+ (r) => options.onload?.(r),
104
+ () => {},
105
+ );
106
+ return promise as Promise<LoadResult<T>>;
107
+ });
108
+ },
109
+ get items(): T[] {
110
+ return this.result?.items ?? [];
111
+ },
112
+ });
113
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Masked input plumbing: masks are strings where token characters accept
3
+ * user input and everything else is a literal.
4
+ *
5
+ * - `9` -- digit (0-9)
6
+ * - `L` -- letter (a-z, case preserved)
7
+ * - `a` -- alphanumeric
8
+ * - `\x` -- the literal character x
9
+ */
10
+
11
+ export interface MaskToken {
12
+ /** Literal characters render as-is; tokens accept input */
13
+ literal: boolean;
14
+ char: string;
15
+ pattern?: RegExp;
16
+ }
17
+
18
+ /** Splits a mask into literal/token parts */
19
+ export function parseMask(mask: string): MaskToken[] {
20
+ const tokens: MaskToken[] = [];
21
+ for (let i = 0; i < mask.length; i++) {
22
+ const char = mask[i];
23
+ if (char === "\\" && i + 1 < mask.length) {
24
+ tokens.push({ literal: true, char: mask[i + 1] });
25
+ i++;
26
+ continue;
27
+ }
28
+ switch (char) {
29
+ case "9":
30
+ tokens.push({ literal: false, char, pattern: /[0-9]/ });
31
+ break;
32
+ case "L":
33
+ tokens.push({ literal: false, char, pattern: /[A-Za-z]/ });
34
+ break;
35
+ case "a":
36
+ tokens.push({ literal: false, char, pattern: /[A-Za-z0-9]/ });
37
+ break;
38
+ default:
39
+ tokens.push({ literal: true, char });
40
+ }
41
+ }
42
+ return tokens;
43
+ }
44
+
45
+ /** The characters a mask can hold, ignoring literals */
46
+ export function tokenCount(tokens: MaskToken[]): number {
47
+ return tokens.filter((t) => !t.literal).length;
48
+ }
49
+
50
+ /**
51
+ * Walks user-entered text against the mask, keeping only characters that
52
+ * fit the remaining slots. Typing ahead of literals works because missing
53
+ * literals are skipped, so `5551234` formats like `(555) 123-4`.
54
+ */
55
+ export function extractRaw(tokens: MaskToken[], text: string): string {
56
+ let result = "";
57
+ let ti = 0;
58
+
59
+ for (const ch of text) {
60
+ if (ti >= tokens.length) break;
61
+
62
+ // Skip forward to the first token this character satisfies
63
+ while (ti < tokens.length && tokens[ti].literal && tokens[ti].char !== ch) {
64
+ ti++;
65
+ }
66
+ const token = tokens[ti];
67
+ if (!token) break;
68
+
69
+ if (token.literal) {
70
+ // The user typed the literal itself
71
+ ti++;
72
+ continue;
73
+ }
74
+ if (!token.pattern!.test(ch)) continue;
75
+
76
+ result += ch;
77
+ ti++;
78
+ }
79
+ return result;
80
+ }
81
+
82
+ /** Builds the displayed text from raw input characters */
83
+ export function formatMask(tokens: MaskToken[], raw: string): string {
84
+ let result = "";
85
+ let index = 0;
86
+ let lastFilledEnd = 0;
87
+
88
+ for (let i = 0; i < tokens.length && index < raw.length; i++) {
89
+ const token = tokens[i];
90
+ if (token.literal) {
91
+ result += token.char;
92
+ continue;
93
+ }
94
+ result += raw[index++];
95
+ lastFilledEnd = result.length;
96
+ }
97
+ return result.slice(0, lastFilledEnd);
98
+ }
@@ -0,0 +1,137 @@
1
+ import { addDocumentEvent, removeDocumentEvent } from "./documentEvents";
2
+ import getScrollParent from "./getScrollParent";
3
+ import setPopoverPosition from "./setPopoverPosition";
4
+
5
+ export interface PopoutContentOptions {
6
+ /** Resolves the element to position the content against */
7
+ getAnchor: () => HTMLElement | undefined;
8
+ /** Resolves the content element */
9
+ getContent: () => HTMLElement | undefined;
10
+ /** Closes the popout (e.g. by setting visible to false) */
11
+ close: (e: MouseEvent | KeyboardEvent) => void;
12
+ /** Where to show the content, relative to the anchor */
13
+ side?: "top" | "right" | "bottom" | "left";
14
+ /** How to align the content, relative to the anchor */
15
+ alignment?: "start" | "center" | "end";
16
+ /** Custom positioning, as an alternative to side/alignment */
17
+ position?: () => void;
18
+ /** Whether pressing Escape closes the popout */
19
+ closeOnEscape?: boolean;
20
+ /** Only close on Escape when the focus is inside the content */
21
+ escapeOnlyWhenFocused?: boolean;
22
+ /** Ignore clicks on the anchor element when closing on outside clicks */
23
+ ignoreAnchorClicks?: boolean;
24
+ /** Called after the content is shown, to set focus for accessibility */
25
+ focusFirstElement?: () => void;
26
+ /** Focus the anchor element after hiding */
27
+ refocusAnchorOnHide?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Shared show/hide machinery for popout content components: outside click
32
+ * handling, Escape handling, repositioning on scroll/resize and focus
33
+ * management.
34
+ *
35
+ * HACK: Because the popout could be shown with a click, we need to wait for
36
+ * another mousedown before we close on click. Otherwise the click that opened
37
+ * the popout immediately bubbles to the document and closes it again.
38
+ */
39
+ export function createPopoutContent(options: PopoutContentOptions): {
40
+ show: () => void;
41
+ hide: () => void;
42
+ } {
43
+ let shown = false;
44
+ let closeOnClick = false;
45
+
46
+ function setPosition() {
47
+ const anchor = options.getAnchor();
48
+ const content = options.getContent();
49
+ if (!anchor || !content) return;
50
+
51
+ if (options.position) {
52
+ options.position();
53
+ } else {
54
+ setPopoverPosition(anchor, content, options.side ?? "bottom", options.alignment ?? "start");
55
+ }
56
+ }
57
+
58
+ function handleDocumentMouseDown() {
59
+ closeOnClick = true;
60
+ }
61
+
62
+ function handleDocumentClick(e: MouseEvent) {
63
+ const content = options.getContent();
64
+ const anchor = options.getAnchor();
65
+ if (!closeOnClick || !content || content.contains(e.target as HTMLElement)) {
66
+ return;
67
+ }
68
+ if (options.ignoreAnchorClicks && anchor?.contains(e.target as HTMLElement)) {
69
+ return;
70
+ }
71
+ e.preventDefault();
72
+ options.close(e);
73
+ }
74
+
75
+ function handleDocumentKeyDown(e: KeyboardEvent) {
76
+ if (e.key !== "Escape") return;
77
+ const content = options.getContent();
78
+ if (options.escapeOnlyWhenFocused && content && !content.contains(e.target as Node)) {
79
+ return;
80
+ }
81
+ e.preventDefault();
82
+ options.close(e);
83
+ }
84
+
85
+ function show() {
86
+ shown = true;
87
+
88
+ closeOnClick = false;
89
+ addDocumentEvent("mousedown", handleDocumentMouseDown);
90
+ addDocumentEvent("click", handleDocumentClick);
91
+ if (options.closeOnEscape) {
92
+ addDocumentEvent("keydown", handleDocumentKeyDown);
93
+ }
94
+
95
+ // Set the position and listen for window resize and scroll to reset the position
96
+ setPosition();
97
+ addEventListener("resize", setPosition);
98
+ const anchor = options.getAnchor();
99
+ if (anchor) {
100
+ getScrollParent(anchor).addEventListener("scroll", setPosition);
101
+ }
102
+
103
+ options.focusFirstElement?.();
104
+ }
105
+
106
+ function hide() {
107
+ if (!shown) return;
108
+ shown = false;
109
+
110
+ removeDocumentEvent("mousedown", handleDocumentMouseDown);
111
+ removeDocumentEvent("click", handleDocumentClick);
112
+ removeDocumentEvent("keydown", handleDocumentKeyDown);
113
+ removeEventListener("resize", setPosition);
114
+ const anchor = options.getAnchor();
115
+ if (anchor) {
116
+ getScrollParent(anchor).removeEventListener("scroll", setPosition);
117
+ }
118
+
119
+ // Focus the anchor element again, per the WAI guidelines, unless focus
120
+ // has already moved elsewhere (e.g. to an adjacent menubar item's menu).
121
+ // If the focused element was removed with the content, focus has fallen
122
+ // back to the body, and the anchor is focused again
123
+ if (options.refocusAnchorOnHide) {
124
+ const content = options.getContent();
125
+ if (
126
+ !content ||
127
+ content.contains(document.activeElement) ||
128
+ document.activeElement === document.body
129
+ ) {
130
+ const el = options.getAnchor();
131
+ el?.focus?.();
132
+ }
133
+ }
134
+ }
135
+
136
+ return { show, hide };
137
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pure helpers for reorderable collections, used by list and tree drag &
3
+ * drop. Geometry-free on purpose: components measure their own slots with
4
+ * slotIndex and combine the result with these.
5
+ */
6
+
7
+ /** Moves an item to a final index; out-of-range targets are clamped */
8
+ export function moveItem<T>(items: T[], from: number, to: number): T[] {
9
+ const next = items.slice();
10
+ if (from < 0 || from >= next.length) return next;
11
+ const clamped = Math.min(next.length - 1, Math.max(0, to));
12
+ const [item] = next.splice(from, 1);
13
+ next.splice(clamped, 0, item);
14
+ return next;
15
+ }
16
+
17
+ /**
18
+ * Moves an item to sit at the slot `insertAt` occupied *before* the move --
19
+ * i.e. `insertAt` indexes into the original order, which is what pointer
20
+ * math produces (dropped before item N). Handles the index shift caused by
21
+ * removing the dragged element.
22
+ */
23
+ export function moveToSlot<T>(items: T[], from: number, insertAt: number): T[] {
24
+ let to = insertAt;
25
+ if (from < insertAt) {
26
+ to = insertAt - 1;
27
+ }
28
+ return moveItem(items, from, to);
29
+ }
30
+
31
+ /**
32
+ * Finds which slot a pointer sits in along an axis: the index of the first
33
+ * element whose midpoint the point has passed (or elements.length when past
34
+ * all of them). `elements` must already be in display order.
35
+ *
36
+ * ```
37
+ * const insert = slotIndex([...list.children], "y", e.clientY);
38
+ * list.set(items => moveToSlot(items, dragFrom, insert));
39
+ * ```
40
+ */
41
+ export function slotIndex(
42
+ elements: { getBoundingClientRect(): DOMRect }[],
43
+ axis: "x" | "y",
44
+ point: number,
45
+ ): number {
46
+ for (let i = 0; i < elements.length; i++) {
47
+ const rect = elements[i].getBoundingClientRect();
48
+ const mid = axis === "x" ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
49
+ if (point < mid) return i;
50
+ }
51
+ return elements.length;
52
+ }