@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,136 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface ListBoxProps {
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
+ /** Whether a single ListBoxItem or multiple ListBoxItems can be selected at the same time */
12
+ type?: "single" | "multiple";
13
+ /** The value of the selected ListBoxItem, or an array of values if the type is "multiple" */
14
+ value: Bindable<any | any[]>;
15
+ /** If set to true, the user cannot interact with any items in the ListBox */
16
+ disabled?: boolean;
17
+ /** An ARIA label for describing the ListBox to screen readers */
18
+ ariaLabel?: string | undefined;
19
+ /** An ID of an element that labels the ListBox */
20
+ ariaLabelledBy?: string | undefined;
21
+ /** The orientation of the ListBox */
22
+ orientation?: "vertical" | "horizontal";
23
+ /** Raised when the selected item(s) have changed */
24
+ /** The name of the component's form data field */
25
+ name?: string;
26
+ /**
27
+ * Reads options from the network (or anywhere async), receiving
28
+ * `{ searchText }` whenever the bound `searchText` changes. When set and
29
+ * no ListBoxItems are slotted in, the options are rendered automatically
30
+ */
31
+ load?: Loader;
32
+ /** Called after each successful load with the normalized result */
33
+ onload?: (result: LoadResult) => void;
34
+ /** Extracts the display text for a loaded item (defaults to `label ?? text ?? item`) */
35
+ getItemLabel?: (item: any) => any;
36
+ /** Extracts the value for a loaded item (defaults to `value ?? id`) */
37
+ getItemValue?: (item: any) => any;
38
+ /** Filter text for a `load` function; two-way bindable */
39
+ searchText?: Bindable<string | undefined>;
40
+ onchange?: (value: any | any[]) => void;
41
+ }
42
+
43
+ /**
44
+ * A list of options that a user can select.
45
+ *
46
+ * See [the WAI ARIA guidelines for ListBoxes](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/).
47
+ *
48
+ * The structure of an ListBox is:
49
+ *
50
+ * ```
51
+ * <ListBox>
52
+ * <ListBoxItem />
53
+ * <ListBoxSeparator /> (optional)
54
+ * </ListBox>
55
+ * ```
56
+ */
57
+ declare function ListBox(
58
+ $parent: ParentNode,
59
+ $anchor: Node | null,
60
+ $props: ListBoxProps | undefined,
61
+ $context?: Record<PropertyKey, any>,
62
+ $slots?: Record<string, SlotRender>,
63
+ ): void;
64
+
65
+
66
+
67
+ interface ListBoxItemProps {
68
+ /** An ID for the root element */
69
+ id?: string;
70
+ /** Classes for the root element */
71
+ class?: ClassValue;
72
+ /** Styles for the root element */
73
+ style?: StyleValue;
74
+ /** The index of this item in the ListBox. Set this if you will be inserting, removing or sorting items. If you don't set this, it will be set automatically */
75
+ index?: number;
76
+ /** The text for the item. If not set, this will come from it's text content */
77
+ text?: string;
78
+ /** A value for the item. Set this if you need more control over which item(s) are selected. If you don't set this, the value will be set to the item's initial index */
79
+ value?: string;
80
+ /** If set to true, the user cannot interact with this ListBoxItem */
81
+ disabled?: boolean;
82
+ /** Raised when this item's selected state has changed */
83
+ onchange?: (selected: boolean) => void;
84
+ }
85
+
86
+ /**
87
+ * An item in an ListBox.
88
+ */
89
+ declare function ListBoxItem(
90
+ $parent: ParentNode,
91
+ $anchor: Node | null,
92
+ $props: ListBoxItemProps | undefined,
93
+ $context?: Record<PropertyKey, any>,
94
+ $slots?: Record<string, SlotRender>,
95
+ ): void;
96
+
97
+
98
+
99
+ interface ListBoxSeparatorProps {
100
+ /** An ID for the root element */
101
+ id?: string;
102
+ /** Classes for the root element */
103
+ class?: ClassValue;
104
+ /** Styles for the root element */
105
+ style?: StyleValue;
106
+ /** The orientation of the separator */
107
+ orientation?: "vertical" | "horizontal";
108
+ }
109
+
110
+ /**
111
+ * An element in a list box that separates logical sets of items.
112
+ */
113
+ declare function ListBoxSeparator(
114
+ $parent: ParentNode,
115
+ $anchor: Node | null,
116
+ $props: ListBoxSeparatorProps | undefined,
117
+ $context?: Record<PropertyKey, any>,
118
+ $slots?: Record<string, SlotRender>,
119
+ ): void;
120
+
121
+
122
+
123
+ /**
124
+ * The internally rendered options for a ListBox that loads them from a
125
+ * network loader. Used as the slot fallback when no ListBoxItems are slotted
126
+ * in; not part of the public API.
127
+ */
128
+ declare function LoadedListBoxItems(
129
+ $parent: ParentNode,
130
+ $anchor: Node | null,
131
+ $props?: LoadedListBoxItemsProps,
132
+ $context?: Record<PropertyKey, any>,
133
+ $slots?: Record<string, SlotRender>,
134
+ ): void;
135
+
136
+ export { ListBox, ListBoxItem, ListBoxSeparator, LoadedListBoxItems };
@@ -0,0 +1,4 @@
1
+ import ListBox from "./ListBox.torp";
2
+ import ListBoxItem from "./ListBoxItem.torp";
3
+ import ListBoxSeparator from "./ListBoxSeparator.torp";
4
+ export { ListBox, ListBoxItem, ListBoxSeparator };
@@ -0,0 +1,143 @@
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 { extractRaw, formatMask, parseMask } from "../utils/maskUtils";
10
+ import createFormField from "../utils/formField";
11
+
12
+ interface MaskedInputProps {
13
+ /** An ID for the root element */
14
+ id?: string;
15
+ /** Classes for the root element */
16
+ class?: ClassValue;
17
+ /** Styles for the root element */
18
+ style?: StyleValue;
19
+ /**
20
+ * The input mask: `9` accepts a digit, `L` a letter and `a` an
21
+ * alphanumeric character; any other character is a literal shown as-is.
22
+ * Use `\x` to escape a token character as a literal. e.g. "(999) 999-9999"
23
+ */
24
+ mask: string;
25
+ /** The raw entered characters, without mask literals */
26
+ value?: Bindable<string>;
27
+ /** Text displayed when no value has been entered (defaults to the mask) */
28
+ placeholder?: string;
29
+ /** If set to true, the user cannot interact with this input */
30
+ disabled?: boolean;
31
+ /** An accessible name for the input */
32
+ ariaLabel?: string;
33
+ /** The name of the component's form data field */
34
+ name?: string;
35
+ /** Raised when the value has changed */
36
+ onchange?: (value: string) => void;
37
+ }
38
+
39
+ /**
40
+ * An input that formats its contents as the user types -- phone numbers,
41
+ * credit cards, product codes -- displaying literals automatically while
42
+ * binding only the underlying characters.
43
+ *
44
+ * ```
45
+ * <MaskedInput mask="(999) 999-9999" &value={$state.phone} />
46
+ * <MaskedInput mask="LLL-9999" name="code" />
47
+ * ```
48
+ *
49
+ * Note that a complete value is not required: the bound value grows as the
50
+ * user types, so validate completeness with a schema when needed.
51
+ */
52
+ export default function MaskedInput($props: MaskedInputProps | undefined) {
53
+ let input: HTMLInputElement;
54
+
55
+ const tokens = parseMask($props?.mask ?? "");
56
+
57
+ // Wire up the enclosing Form and Field, if any
58
+ const form = createFormField(
59
+ $props?.name,
60
+ $context[FormContextName] as FormContext | undefined,
61
+ $context[FieldContextName] as FieldContext | undefined,
62
+ );
63
+
64
+ function getDisabled(): boolean {
65
+ return $props?.disabled === true;
66
+ }
67
+
68
+ // The bound value is the raw entered characters, so the state key
69
+ // matches the prop name for $bind
70
+ let $state = $watch({
71
+ value: "",
72
+ });
73
+
74
+ $bind($state, $props, "value");
75
+
76
+ function display(): string {
77
+ return formatMask(tokens, $state.value ?? "");
78
+ }
79
+
80
+ function getPlaceholder(): string | undefined {
81
+ return $props?.placeholder ?? $props?.mask;
82
+ }
83
+
84
+ // Keep the element's text matching the formatted raw value; typing sets
85
+ // the property directly, so an attribute alone would not update it
86
+ function syncInput() {
87
+ if (input && !getDisabled() && input.value !== display()) {
88
+ input.value = display();
89
+ }
90
+ }
91
+ $run(() => {
92
+ if (!input) return;
93
+ const raw = $state.value ?? "";
94
+ const next = formatMask(tokens, raw);
95
+ if (input.value !== next) {
96
+ input.value = next;
97
+ }
98
+ });
99
+ $onmount(() => syncInput());
100
+
101
+ function handleInput(e: Event) {
102
+ if (getDisabled()) return;
103
+ const el = e.currentTarget as HTMLInputElement;
104
+
105
+ const raw = extractRaw(tokens, el.value);
106
+ if (raw !== $state.value) {
107
+ $state.value = raw;
108
+ form.handleInput();
109
+ $props?.onchange?.(raw);
110
+ }
111
+
112
+ // Rewrite the element so rejected characters disappear immediately
113
+ el.value = formatMask(tokens, raw);
114
+ }
115
+
116
+ function handleBlur() {
117
+ form.handleBlur();
118
+ }
119
+
120
+ @render {
121
+ <input
122
+ &ref={input}
123
+ id={$props?.id}
124
+ class={["torp-masked-input", $props?.class]}
125
+ {$props?.style}
126
+ type="text"
127
+ spellcheck="false"
128
+ autocomplete="off"
129
+ value={display()}
130
+ placeholder={getPlaceholder()}
131
+ disabled={getDisabled()}
132
+ tabindex={getDisabled() ? -1 : 0}
133
+ aria-label={$props?.ariaLabel}
134
+ data-valid={form.valid ? "valid" : "invalid"}
135
+ aria-describedby={form.messageId}
136
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
137
+ oninput={handleInput}
138
+ onblur={handleBlur}
139
+ />
140
+
141
+ <FormValues name={form.name} value={$state.value || undefined} />
142
+ }
143
+ }
@@ -0,0 +1,52 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface MaskedInputProps {
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 input mask: `9` accepts a digit, `L` a letter and `a` an
13
+ * alphanumeric character; any other character is a literal shown as-is.
14
+ * Use `\x` to escape a token character as a literal. e.g. "(999) 999-9999"
15
+ */
16
+ mask: string;
17
+ /** The raw entered characters, without mask literals */
18
+ value?: Bindable<string>;
19
+ /** Text displayed when no value has been entered (defaults to the mask) */
20
+ placeholder?: string;
21
+ /** If set to true, the user cannot interact with this input */
22
+ disabled?: boolean;
23
+ /** An accessible name for the input */
24
+ ariaLabel?: string;
25
+ /** The name of the component's form data field */
26
+ name?: string;
27
+ /** Raised when the value has changed */
28
+ onchange?: (value: string) => void;
29
+ }
30
+
31
+ /**
32
+ * An input that formats its contents as the user types -- phone numbers,
33
+ * credit cards, product codes -- displaying literals automatically while
34
+ * binding only the underlying characters.
35
+ *
36
+ * ```
37
+ * <MaskedInput mask="(999) 999-9999" &value={$state.phone} />
38
+ * <MaskedInput mask="LLL-9999" name="code" />
39
+ * ```
40
+ *
41
+ * Note that a complete value is not required: the bound value grows as the
42
+ * user types, so validate completeness with a schema when needed.
43
+ */
44
+ declare function MaskedInput(
45
+ $parent: ParentNode,
46
+ $anchor: Node | null,
47
+ $props: MaskedInputProps | undefined,
48
+ $context?: Record<PropertyKey, any>,
49
+ $slots?: Record<string, SlotRender>,
50
+ ): void;
51
+
52
+ export { MaskedInput };
@@ -0,0 +1,2 @@
1
+ import MaskedInput from "./MaskedInput.torp";
2
+ export { MaskedInput };
@@ -1,24 +1,17 @@
1
1
  import type { ClassValue, StyleValue } from "@torpor/view";
2
- import { ContextualContextName } from "../Contextual/ContextualTypes";
3
- import { ModalContextName } from "../Modal/ModalTypes";
4
- import { PopoverContextName, type PopoverContext } from "../Popover/PopoverTypes";
5
- import {
6
- MenuContextName,
7
- type MenuContext,
8
- type ItemState,
9
- MenuPopoutContextName,
10
- type MenuPopoutContext
11
- } from "./MenuTypes";
2
+ import { type PopoutContext, PopoutContextName } from "../utils/PopoutTypes";
3
+ import { createFocusGroup } from "../utils/focusGroup";
4
+ import { type ItemState, type MenuContext, MenuContextName } from "./MenuTypes";
12
5
 
13
6
  interface MenuProps {
14
7
  /** An ID for the root element */
15
8
  id?: string;
16
9
  /** Classes for the root element */
17
- class?: ClassValue,
10
+ class?: ClassValue;
18
11
  /** Styles for the root element */
19
- style?: StyleValue,
12
+ style?: StyleValue;
20
13
  /** An ARIA label for describing the menu to screen readers */
21
- ariaLabel?: string,
14
+ ariaLabel?: string;
22
15
  }
23
16
 
24
17
  /**
@@ -41,85 +34,80 @@ interface MenuProps {
41
34
  */
42
35
  export default function Menu($props: MenuProps | undefined) {
43
36
  // A collection containing the state and some functions for each item
44
- let itemStates: ItemState[] = [];
45
- let focusIndex = -1;
37
+ const group = createFocusGroup<ItemState>();
38
+ const { itemStates } = group;
46
39
 
47
- // Get stuff out of the PopoverContext
48
- // HACK: share state in PopupState and extend in context/modal/popover
49
- const popoverContext = $context[ContextualContextName] ?? $context[ModalContextName] ?? $context[PopoverContextName] as PopoverContext;
50
- if (!popoverContext) {
51
- throw new Error("Menu must be contained within a Context, Modal or Popover");
40
+ // The text typed for type-ahead, which is reset after a short pause between key presses
41
+ let searchText = "";
42
+ let searchTimer: ReturnType<typeof setTimeout> | undefined;
43
+
44
+ // Get stuff out of the PopoutContext
45
+ const popoutContext = $context[PopoutContextName] as PopoutContext;
46
+ if (!popoutContext) {
47
+ throw new Error("Menu must be contained within a popout component");
52
48
  }
53
- const { setVisible, handleButton, visible } = popoverContext;
49
+ const { handleButton, state } = popoutContext;
54
50
 
55
- // Check whether this is a menu in a popout
56
- const popoutContext = $context[MenuPopoutContextName] as MenuPopoutContext;
57
- const inPopout = !!popoutContext;
58
- const setPopoutVisible = popoutContext?.setVisible;
51
+ const inPopout = state.contentRole === "menu";
52
+ state.contentRole = "menu";
59
53
 
60
54
  // Set the focusFirstElement method in the PopoverContext(s) so that it can be called after everything has been shown
61
- popoverContext.focusFirstElement = focusFirstElement;
62
- if (popoutContext) {
63
- popoutContext.focusFirstElement = focusFirstElement;
64
- }
55
+ popoutContext.focusFirstElement = group.focusFirstItem;
56
+ popoutContext.focusLastElement = group.focusLastItem;
65
57
 
66
58
  // Set the context to pass down to items
67
59
  let context: MenuContext = {
68
- setVisible,
69
60
  handleButton,
70
61
  handleItemFocus,
71
62
  handleItemKey,
72
- visible,
73
- registerItem
63
+ handlePopout,
64
+ state,
65
+ registerItem,
74
66
  };
75
67
  $context[MenuContextName] = context;
76
68
 
77
- function focusFirstElement() {
78
- if (itemStates.length) {
79
- itemStates[0].setFocused();
80
- }
81
- }
82
-
83
- function registerItem(setFocused: () => void) {
84
- // Add the item's state to the array and return its index
85
- const newItemState: ItemState = {
86
- setFocused
87
- };
88
- itemStates.push(newItemState);
89
- itemStates = itemStates;
90
- return { index: itemStates.length - 1 };
69
+ function registerItem(item: ItemState) {
70
+ return group.registerItem(item);
91
71
  }
92
72
 
93
73
  function handleItemFocus(index: number) {
94
- focusIndex = index;
74
+ group.handleItemFocus(index);
95
75
  }
96
76
 
97
77
  function handleItemKey(e: KeyboardEvent) {
98
78
  switch (e.key) {
99
- case "Esc":
100
79
  case "Escape": {
101
80
  e.preventDefault();
102
- setVisible(false);
81
+ state.visible = false;
103
82
  break;
104
83
  }
105
- case "Down":
106
84
  case "ArrowDown": {
107
85
  e.preventDefault();
108
86
  focusItem("next");
109
87
  break;
110
88
  }
111
- case "Up":
112
89
  case "ArrowUp": {
113
90
  e.preventDefault();
114
91
  focusItem("previous");
115
92
  break;
116
93
  }
117
- case "Left":
118
94
  case "ArrowLeft": {
119
- // TODO: This should be based on what side the menu shows on
120
- if (inPopout) {
95
+ if (popoutContext.navigateMenuBar) {
96
+ // In a menu under a menubar item: move to the previous item and open its menu
97
+ e.preventDefault();
98
+ popoutContext.navigateMenuBar("previous");
99
+ } else if (inPopout) {
100
+ // TODO: This should be based on what side the menu shows on
121
101
  e.preventDefault();
122
- setPopoutVisible(false);
102
+ state.visible = false;
103
+ }
104
+ break;
105
+ }
106
+ case "ArrowRight": {
107
+ if (popoutContext.navigateMenuBar) {
108
+ // In a menu under a menubar item: move to the next item and open its menu
109
+ e.preventDefault();
110
+ popoutContext.navigateMenuBar("next");
123
111
  }
124
112
  break;
125
113
  }
@@ -133,29 +121,60 @@ export default function Menu($props: MenuProps | undefined) {
133
121
  focusItem("end");
134
122
  break;
135
123
  }
136
- }
137
- }
138
-
139
- function focusItem(target: "start" | "previous" | "next" | "end") {
140
- switch (target) {
141
- case "start": {
142
- focusIndex = 0;
124
+ case "Tab": {
125
+ state.visible = false;
143
126
  break;
144
127
  }
145
- case "previous": {
146
- focusIndex = Math.max(0, focusIndex - 1);
128
+ default: {
129
+ // Type-ahead: typing a character focuses the next item whose label starts with it
130
+ if (e.key.length === 1 && e.key !== " ") {
131
+ e.preventDefault();
132
+ search(e.key);
133
+ }
147
134
  break;
148
135
  }
149
- case "next": {
150
- focusIndex = Math.min(itemStates.length - 1, focusIndex + 1);
151
- break;
136
+ }
137
+ }
138
+
139
+ function search(key: string) {
140
+ const character = key.toLocaleLowerCase();
141
+ if (searchTimer) {
142
+ clearTimeout(searchTimer);
143
+ // Typing the same character repeatedly cycles through the items that
144
+ // start with it, instead of searching for a repeated-character string
145
+ if (searchText.length !== 1 || character !== searchText) {
146
+ searchText += character;
152
147
  }
153
- case "end": {
154
- focusIndex = itemStates.length - 1;
148
+ } else {
149
+ searchText = character;
150
+ }
151
+ searchTimer = setTimeout(() => {
152
+ searchTimer = undefined;
153
+ searchText = "";
154
+ }, 1000);
155
+
156
+ // Focus the next item whose label starts with the search text, wrapping
157
+ // around to the start and skipping disabled items
158
+ for (let offset = 1; offset <= itemStates.length; offset++) {
159
+ const item = itemStates[(group.getFocusIndex() + offset) % itemStates.length];
160
+ if (!item.disabled && item.text?.toLocaleLowerCase().startsWith(searchText)) {
161
+ item.setFocused();
155
162
  break;
156
163
  }
157
164
  }
158
- itemStates[focusIndex]?.setFocused();
165
+ }
166
+
167
+ function focusItem(target: "start" | "previous" | "next" | "end") {
168
+ group.moveFocus(group.getFocusIndex(), target);
169
+ }
170
+
171
+ function handlePopout(index: number) {
172
+ // Hide all other submenus
173
+ for (let item of itemStates) {
174
+ if (item.index !== index) {
175
+ item.setVisible?.(false);
176
+ }
177
+ }
159
178
  }
160
179
 
161
180
  @render {
@@ -165,6 +184,7 @@ export default function Menu($props: MenuProps | undefined) {
165
184
  {$props?.style}
166
185
  tabindex="-1"
167
186
  role="menu"
187
+ aria-labelledby={state.triggerId}
168
188
  aria-label={$props?.ariaLabel}
169
189
  aria-orientation="vertical"
170
190
  >
@@ -1,21 +1,21 @@
1
1
  import type { ClassValue, StyleValue } from "@torpor/view";
2
- import { MenuContextName, type MenuContext } from "./MenuTypes";
2
+ import { type ItemState, type MenuContext, MenuContextName } from "./MenuTypes";
3
3
 
4
4
  interface MenuButtonProps {
5
5
  /** An ID for the root element */
6
6
  id?: string;
7
7
  /** Classes for the root element */
8
- class?: ClassValue,
8
+ class?: ClassValue;
9
9
  /** Styles for the root element */
10
- style?: StyleValue,
10
+ style?: StyleValue;
11
11
  /** The value that should be returned when this button is pressed and the Dialog was invoked by calling showContextual, showPrompt or showPopover */
12
- value?: any,
12
+ value?: any;
13
13
  /** Whether this button is disabled */
14
- disabled?: boolean,
14
+ disabled?: boolean;
15
15
  /** Raised when the button is clicked */
16
- onclick?: (e: MouseEvent) => void,
16
+ onclick?: (e: MouseEvent) => void;
17
17
  /** Raised when a key is pressed down */
18
- onkeydown?: (e: KeyboardEvent) => void,
18
+ onkeydown?: (e: KeyboardEvent) => void;
19
19
  }
20
20
 
21
21
  /**
@@ -33,7 +33,8 @@ export default function MenuButton($props: MenuButtonProps | undefined) {
33
33
  const { registerItem, handleButton, handleItemKey, handleItemFocus } = menuContext;
34
34
 
35
35
  // Register this item with the parent Menu and store the index
36
- const { index } = registerItem(setFocused);
36
+ const item: ItemState = { setFocused, disabled: $props?.disabled };
37
+ const { index } = registerItem(item);
37
38
 
38
39
  function handleKeyDown(e: KeyboardEvent) {
39
40
  handleItemKey(e);
@@ -43,7 +44,7 @@ export default function MenuButton($props: MenuButtonProps | undefined) {
43
44
  }
44
45
 
45
46
  function handleClick(e: MouseEvent) {
46
- handleButton($props?.value);
47
+ handleButton("confirm", $props?.value);
47
48
  if ($props?.onclick) {
48
49
  $props.onclick(e);
49
50
  }
@@ -53,6 +54,11 @@ export default function MenuButton($props: MenuButtonProps | undefined) {
53
54
  button.focus();
54
55
  }
55
56
 
57
+ // Store the item's label for type-ahead
58
+ function handleMount(el: HTMLButtonElement) {
59
+ item.text = el.textContent.trim();
60
+ }
61
+
56
62
  @render {
57
63
  <button
58
64
  {$props?.id}
@@ -61,11 +67,12 @@ export default function MenuButton($props: MenuButtonProps | undefined) {
61
67
  type="button"
62
68
  {$props?.disabled}
63
69
  role="menuitem"
70
+ tabindex="-1"
64
71
  aria-disabled={$props?.disabled ? "true" : undefined}
65
- data-disabled={$props?.disabled ? "true" : undefined}
66
72
  onkeydown={handleKeyDown}
67
73
  onclick={handleClick}
68
74
  onfocus={() => handleItemFocus(index)}
75
+ onmount={handleMount}
69
76
  &ref={button}
70
77
  >
71
78
  <slot />