@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,88 @@
1
+ import type { ClassValue, StyleValue } from "@torpor/view";
2
+
3
+ interface ProgressProps {
4
+ /** An ID for the root element */
5
+ id?: string;
6
+ /** Classes for the root element */
7
+ class?: ClassValue;
8
+ /** Styles for the root element */
9
+ style?: StyleValue;
10
+ /** The minimum value (default 0) */
11
+ min?: number;
12
+ /** The maximum value (default 100) */
13
+ max?: number;
14
+ /** The current value; omit it for an indeterminate progress bar */
15
+ value?: number;
16
+ /** A human-readable version of the current value, e.g. "3 of 10 files" */
17
+ valuetext?: string;
18
+ /** An accessible name for the progress bar */
19
+ ariaLabel?: string;
20
+ }
21
+
22
+ /**
23
+ * A progress bar that shows how far along an operation is, either with a
24
+ * specific value or as indeterminate when the extent is unknown.
25
+ *
26
+ * See [the WAI ARIA guidelines for Progress Bars](https://www.w3.org/WAI/ARIA/apg/patterns/progressbar/).
27
+ */
28
+ export default function Progress($props: ProgressProps | undefined) {
29
+ function getMin(): number {
30
+ return $props?.min ?? 0;
31
+ }
32
+ function getMax(): number {
33
+ return $props?.max ?? 100;
34
+ }
35
+ function isIndeterminate(): boolean {
36
+ return $props?.value == null;
37
+ }
38
+
39
+ // The clamped value that is reported to assistive tech
40
+ function getValue(): number {
41
+ return Math.min(getMax(), Math.max(getMin(), $props?.value ?? getMin()));
42
+ }
43
+
44
+ // The percentage of the range that the value represents (0..100)
45
+ function getPct(): number {
46
+ const min = getMin();
47
+ const max = getMax();
48
+ if (max <= min) return 0;
49
+ return ((getValue() - min) / (max - min)) * 100;
50
+ }
51
+
52
+ @render {
53
+ <div
54
+ {$props?.id}
55
+ class={["torp-progress", $props?.class]}
56
+ {$props?.style}
57
+ role="progressbar"
58
+ aria-label={$props?.ariaLabel}
59
+ aria-valuemin={isIndeterminate() ? undefined : getMin()}
60
+ aria-valuemax={isIndeterminate() ? undefined : getMax()}
61
+ aria-valuenow={isIndeterminate() ? undefined : getValue()}
62
+ aria-valuetext={$props?.valuetext}
63
+ data-state={isIndeterminate() ? "indeterminate" : "determinate"}
64
+ >
65
+ <div
66
+ class="torp-progress-bar"
67
+ data-state={isIndeterminate() ? "indeterminate" : "determinate"}
68
+ style={isIndeterminate() ? undefined : `width: ${getPct()}%;`}
69
+ />
70
+ </div>
71
+ }
72
+
73
+ @style {
74
+ /* Headless: layout only, so the bar fills the track proportionally.
75
+ All appearance is up to the consuming theme */
76
+ .torp-progress {
77
+ position: relative;
78
+ overflow: hidden;
79
+ }
80
+ .torp-progress-bar {
81
+ position: absolute;
82
+ inset: 0 auto 0 0;
83
+ }
84
+ .torp-progress-bar[data-state="indeterminate"] {
85
+ width: 100%;
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,37 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface ProgressProps {
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 minimum value (default 0) */
12
+ min?: number;
13
+ /** The maximum value (default 100) */
14
+ max?: number;
15
+ /** The current value; omit it for an indeterminate progress bar */
16
+ value?: number;
17
+ /** A human-readable version of the current value, e.g. "3 of 10 files" */
18
+ valuetext?: string;
19
+ /** An accessible name for the progress bar */
20
+ ariaLabel?: string;
21
+ }
22
+
23
+ /**
24
+ * A progress bar that shows how far along an operation is, either with a
25
+ * specific value or as indeterminate when the extent is unknown.
26
+ *
27
+ * See [the WAI ARIA guidelines for Progress Bars](https://www.w3.org/WAI/ARIA/apg/patterns/progressbar/).
28
+ */
29
+ declare function Progress(
30
+ $parent: ParentNode,
31
+ $anchor: Node | null,
32
+ $props: ProgressProps | undefined,
33
+ $context?: Record<PropertyKey, any>,
34
+ $slots?: Record<string, SlotRender>,
35
+ ): void;
36
+
37
+ export { Progress };
@@ -0,0 +1,2 @@
1
+ import Progress from "./Progress.torp";
2
+ export { Progress };
@@ -0,0 +1,225 @@
1
+ import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
2
+ import $handle from "../utils/$handle";
3
+ import {
4
+ type FieldContext,
5
+ FieldContextName,
6
+ type FormContext,
7
+ FormContextName,
8
+ } from "../Form/FormTypes";
9
+ import FormValues from "../Form/FormValues.torp";
10
+ import createFormField from "../utils/formField";
11
+
12
+ interface RatingProps {
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
+ /** The maximum rating, i.e. the number of stars (default 5) */
20
+ max?: number;
21
+ /** The current rating */
22
+ value?: Bindable<number>;
23
+ /** If set to true, the user cannot interact with this rating */
24
+ disabled?: boolean;
25
+ /** If set to false, clicking or re-selecting the current rating does not clear it (default true) */
26
+ allowClear?: boolean;
27
+ /** An accessible name for the rating */
28
+ ariaLabel?: string;
29
+ /** The name of the component's form data field */
30
+ name?: string;
31
+ /** Raised when the rating has changed */
32
+ onchange?: (value: number) => void;
33
+ }
34
+
35
+ /**
36
+ * A star rating input, where the user selects a value from 1 to a maximum
37
+ * number of stars by clicking or by using the keyboard.
38
+ *
39
+ * Implements the radio group pattern: each star is a radio button, the star
40
+ * matching the current rating is checked, and arrow keys move the rating.
41
+ *
42
+ * See [the WAI ARIA guidelines for Rating](https://www.w3.org/WAI/ARIA/apg/patterns/rating/).
43
+ */
44
+ export default function Rating($props: RatingProps | undefined) {
45
+ let stars: (HTMLButtonElement | undefined)[] = [];
46
+ let root: HTMLElement;
47
+
48
+ // Wire up the enclosing Form and Field, if any
49
+ const form = createFormField(
50
+ $props?.name,
51
+ $context[FormContextName] as FormContext | undefined,
52
+ $context[FieldContextName] as FieldContext | undefined,
53
+ );
54
+
55
+ function getMax(): number {
56
+ const max = $props?.max ?? 5;
57
+ return max > 0 ? Math.floor(max) : 5;
58
+ }
59
+ function getDisabled(): boolean {
60
+ return $props?.disabled === true;
61
+ }
62
+
63
+ let $state = $watch({
64
+ value: 0,
65
+ hover: 0,
66
+ get stars(): number[] {
67
+ return Array.from({ length: getMax() }, (_, i) => i + 1);
68
+ },
69
+ });
70
+
71
+ $bind($state, $props, "value");
72
+
73
+ // Keep the stored value clamped, so that out-of-range initial values
74
+ // don't later surface as user changes
75
+ $run(() => {
76
+ const clamped = clamp($state.value);
77
+ if (clamped !== $state.value) {
78
+ $state.value = clamped;
79
+ }
80
+ });
81
+
82
+ // Dispatch the change event when the value changes
83
+ $handle((first) => {
84
+ const value = $state.value;
85
+ if (!first) $props?.onchange?.(value);
86
+ });
87
+
88
+ function clamp(value: number): number {
89
+ return Math.min(getMax(), Math.max(0, value));
90
+ }
91
+
92
+ function getValue(): number {
93
+ return clamp($state.value);
94
+ }
95
+
96
+ function setValue(value: number, focus = false) {
97
+ const next = clamp(value);
98
+ if (next !== $state.value) {
99
+ $state.value = next;
100
+ }
101
+ if (focus) {
102
+ stars[next - 1]?.focus();
103
+ }
104
+ }
105
+
106
+ // The number of filled stars to display, taking the hovered star into
107
+ // account for the mouse preview
108
+ function getDisplayValue(): number {
109
+ return getDisabled() ? getValue() : Math.max(getValue(), $state.hover);
110
+ }
111
+
112
+ function handleClick(value: number) {
113
+ if (getDisabled()) return;
114
+ if (value === $state.value && ($props?.allowClear ?? true)) {
115
+ setValue(0);
116
+ } else {
117
+ setValue(value);
118
+ }
119
+ form.handleInput();
120
+ }
121
+
122
+ function handleKey(e: KeyboardEvent) {
123
+ if (getDisabled()) return;
124
+
125
+ switch (e.key) {
126
+ case "ArrowRight":
127
+ case "ArrowUp": {
128
+ e.preventDefault();
129
+ setValue(Math.max(1, $state.value + 1), true);
130
+ form.handleInput();
131
+ break;
132
+ }
133
+ case "ArrowLeft":
134
+ case "ArrowDown": {
135
+ e.preventDefault();
136
+ setValue($state.value <= 1 ? 1 : $state.value - 1, true);
137
+ form.handleInput();
138
+ break;
139
+ }
140
+ case "Home": {
141
+ e.preventDefault();
142
+ setValue(1, true);
143
+ form.handleInput();
144
+ break;
145
+ }
146
+ case "End": {
147
+ e.preventDefault();
148
+ setValue(getMax(), true);
149
+ form.handleInput();
150
+ break;
151
+ }
152
+ }
153
+ }
154
+
155
+ // Only blur when focus leaves the whole rating
156
+ function handleFocusOut(e: FocusEvent) {
157
+ if (!root.contains(e.relatedTarget as Node | null)) {
158
+ form.handleBlur();
159
+ }
160
+ }
161
+
162
+ // Roving tabindex: the star matching the current rating (or the first
163
+ // star when there is no rating) is the single tab stop
164
+ function tabStop(value: number): boolean {
165
+ return value === ($state.value > 0 ? $state.value : 1);
166
+ }
167
+
168
+ @render {
169
+ <div
170
+ {$props?.id}
171
+ class={["torp-rating", getDisabled() ? "torp-rating-disabled" : undefined, $props?.class]}
172
+ {$props?.style}
173
+ role="radiogroup"
174
+ aria-label={$props?.ariaLabel}
175
+ aria-disabled={getDisabled() ? "true" : undefined}
176
+ data-valid={form.valid ? "valid" : "invalid"}
177
+ aria-describedby={form.messageId}
178
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
179
+ onmouseleave={() => ($state.hover = 0)}
180
+ onfocusout={handleFocusOut}
181
+ &ref={root}
182
+ >
183
+ @for (let value of $state.stars) {
184
+ @key = value
185
+ <button
186
+ type="button"
187
+ class="torp-rating-star"
188
+ role="radio"
189
+ tabindex={tabStop(value) ? 0 : -1}
190
+ aria-checked={value === getValue() ? "true" : "false"}
191
+ aria-label={`${value} ${value === 1 ? "star" : "stars"}`}
192
+ aria-disabled={getDisabled() ? "true" : undefined}
193
+ data-state={value <= getDisplayValue() ? "filled" : "empty"}
194
+ onmount={(el) => {
195
+ stars[value - 1] = el;
196
+ return () => {
197
+ if (stars[value - 1] === el) {
198
+ stars[value - 1] = undefined;
199
+ }
200
+ };
201
+ }}
202
+ onclick={() => handleClick(value)}
203
+ onkeydown={handleKey}
204
+ onmouseenter={() => {
205
+ if (!getDisabled()) $state.hover = value;
206
+ }}
207
+ >
208
+ <slot item={{ value: value, filled: value <= getDisplayValue() }}>
209
+ <span aria-hidden="true">★</span>
210
+ </slot>
211
+ </button>
212
+ }
213
+ </div>
214
+
215
+ <FormValues name={form.name} value={getValue() > 0 ? getValue() : undefined} />
216
+ }
217
+
218
+ @style {
219
+ /* Headless: layout only, so the stars sit in a row. All appearance is
220
+ up to the consuming theme */
221
+ .torp-rating {
222
+ display: inline-flex;
223
+ }
224
+ }
225
+ }
@@ -0,0 +1,44 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface RatingProps {
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 maximum rating, i.e. the number of stars (default 5) */
12
+ max?: number;
13
+ /** The current rating */
14
+ value?: Bindable<number>;
15
+ /** If set to true, the user cannot interact with this rating */
16
+ disabled?: boolean;
17
+ /** If set to false, clicking or re-selecting the current rating does not clear it (default true) */
18
+ allowClear?: boolean;
19
+ /** An accessible name for the rating */
20
+ ariaLabel?: string;
21
+ /** The name of the component's form data field */
22
+ name?: string;
23
+ /** Raised when the rating has changed */
24
+ onchange?: (value: number) => void;
25
+ }
26
+
27
+ /**
28
+ * A star rating input, where the user selects a value from 1 to a maximum
29
+ * number of stars by clicking or by using the keyboard.
30
+ *
31
+ * Implements the radio group pattern: each star is a radio button, the star
32
+ * matching the current rating is checked, and arrow keys move the rating.
33
+ *
34
+ * See [the WAI ARIA guidelines for Rating](https://www.w3.org/WAI/ARIA/apg/patterns/rating/).
35
+ */
36
+ declare function Rating(
37
+ $parent: ParentNode,
38
+ $anchor: Node | null,
39
+ $props: RatingProps | undefined,
40
+ $context?: Record<PropertyKey, any>,
41
+ $slots?: Record<string, SlotRender>,
42
+ ): void;
43
+
44
+ export { Rating };
@@ -0,0 +1,2 @@
1
+ import Rating from "./Rating.torp";
2
+ export { Rating };
@@ -0,0 +1,233 @@
1
+ import type { Bindable, ClassValue, StyleValue } from "@torpor/view";
2
+ import $handle from "../utils/$handle";
3
+ import {
4
+ type FieldContext,
5
+ FieldContextName,
6
+ type FormContext,
7
+ FormContextName,
8
+ } from "../Form/FormTypes";
9
+ import FormValues from "../Form/FormValues.torp";
10
+ import { createFocusGroup } from "../utils/focusGroup";
11
+ import { createItemGroup } from "../utils/createItemGroup";
12
+ import createFormField from "../utils/formField";
13
+ import type { FocusTarget } from "../utils/focusItem";
14
+ import {
15
+ type ItemState,
16
+ type SegmentedControlContext,
17
+ SegmentedControlContextName,
18
+ } from "./SegmentedControlTypes";
19
+
20
+ interface SegmentedControlProps {
21
+ /** An ID for the root element */
22
+ id?: string;
23
+ /** Classes for the root element */
24
+ class?: ClassValue;
25
+ /** Styles for the root element */
26
+ style?: StyleValue;
27
+ /** The value of the active segment */
28
+ value?: Bindable<string | undefined>;
29
+ /** If set to true, the user cannot interact with any segments */
30
+ disabled?: boolean;
31
+ /** An accessible name for the control */
32
+ ariaLabel?: string;
33
+ /** The name of the component's form data field */
34
+ name?: string;
35
+ /** Raised when the active segment has changed */
36
+ onchange?: (value: string) => void;
37
+ }
38
+
39
+ /**
40
+ * A compact group of segments, of which exactly one is active. The user moves
41
+ * between segments with the arrow keys, or selects one directly.
42
+ *
43
+ * See [the WAI ARIA guidelines for Radio Groups](https://www.w3.org/WAI/ARIA/apg/patterns/radiogroup/).
44
+ *
45
+ * The structure of a SegmentedControl is:
46
+ *
47
+ * ```
48
+ * <SegmentedControl ariaLabel="View">
49
+ * <SegmentedItem value="day">Day</SegmentedItem>
50
+ * <SegmentedItem value="week">Week</SegmentedItem>
51
+ * </SegmentedControl>
52
+ * ```
53
+ */
54
+ export default function SegmentedControl($props: SegmentedControlProps | undefined) {
55
+ let nextItemIndex = 0;
56
+ let root: HTMLElement;
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
+ let $state = $watch({
66
+ value: $props?.value ?? "",
67
+ });
68
+
69
+ $bind($state, $props, "value");
70
+
71
+ const group = createItemGroup<ItemState>({
72
+ state: $state,
73
+ type: "single",
74
+ selectedProperty: "active",
75
+ allowDeselect: false,
76
+ disabled: () => $props?.disabled,
77
+ // Select the first enabled item as items register, when nothing is
78
+ // selected yet; this is a default, not a user change, so it doesn't
79
+ // raise onchange
80
+ onRegister: (item) => {
81
+ if ($state.value === "" && !item.disabled) {
82
+ skipNextChange = true;
83
+ $state.value = item.value;
84
+ }
85
+ },
86
+ });
87
+
88
+ // The focus group owns the roving-tabindex bookkeeping; the selection
89
+ // group above stays the source of truth for the active segment
90
+ const focusGroup = createFocusGroup<ItemState>();
91
+
92
+ // Set the context to pass down to items
93
+ const context: SegmentedControlContext = {
94
+ registerItem,
95
+ removeItem: (itemIndex) => {
96
+ group.removeItem(itemIndex);
97
+ focusGroup.removeItem(itemIndex);
98
+ },
99
+ triggerItem: (value) => {
100
+ group.toggleItem(value);
101
+ form.handleInput();
102
+ },
103
+ handleItemKey,
104
+ };
105
+ $context[SegmentedControlContextName] = context;
106
+
107
+ // Dispatch the change event when the value changes
108
+ let skipNextChange = false;
109
+ $handle((first) => {
110
+ const value = $state.value;
111
+ if (skipNextChange) {
112
+ skipNextChange = false;
113
+ return;
114
+ }
115
+ if (!first && value !== "") $props?.onchange?.(value);
116
+ });
117
+
118
+ function registerItem(
119
+ index: number | undefined,
120
+ value: string | undefined,
121
+ disabled: boolean | undefined,
122
+ ): ItemState {
123
+ // If an index wasn't set for the item, set it automatically
124
+ if (index === undefined) {
125
+ index = nextItemIndex++;
126
+ }
127
+
128
+ // If we already have an item with this index, just return it
129
+ const existing = group.itemStates.find((item) => item.index === index);
130
+ if (existing) {
131
+ return existing;
132
+ }
133
+
134
+ // If a value wasn't set for the item, set it to the index
135
+ if (value === undefined) {
136
+ value = index.toString();
137
+ }
138
+
139
+ const itemState: ItemState = $watch({
140
+ index,
141
+ value,
142
+ active: false,
143
+ disabled: disabled ?? false,
144
+ parentDisabled: $props?.disabled ?? false,
145
+ setFocused: () => {},
146
+ });
147
+ group.registerItem(itemState);
148
+ focusGroup.registerItem(itemState);
149
+
150
+ return itemState;
151
+ }
152
+
153
+ function handleItemKey(index: number, e: KeyboardEvent) {
154
+ switch (e.key) {
155
+ case "ArrowRight":
156
+ case "ArrowDown": {
157
+ e.preventDefault();
158
+ move(index, "next");
159
+ break;
160
+ }
161
+ case "ArrowLeft":
162
+ case "ArrowUp": {
163
+ e.preventDefault();
164
+ move(index, "previous");
165
+ break;
166
+ }
167
+ case "Home": {
168
+ e.preventDefault();
169
+ move(index, "start");
170
+ break;
171
+ }
172
+ case "End": {
173
+ e.preventDefault();
174
+ move(index, "end");
175
+ break;
176
+ }
177
+ }
178
+ }
179
+
180
+ // Moving with the keyboard both selects and focuses the target segment,
181
+ // like a radio group
182
+ function move(index: number, target: FocusTarget) {
183
+ const before = focusGroup.getFocusIndex();
184
+ focusGroup.moveFocus(index, target);
185
+ const next = focusGroup.getFocusIndex();
186
+ if (next !== before) {
187
+ const item = focusGroup.itemStates[next];
188
+ if (item) {
189
+ group.toggleItem(item.value);
190
+ form.handleInput();
191
+ }
192
+ }
193
+ }
194
+
195
+ // Only blur when focus leaves the whole control
196
+ function handleFocusOut(e: FocusEvent) {
197
+ if (!root.contains(e.relatedTarget as Node | null)) {
198
+ form.handleBlur();
199
+ }
200
+ }
201
+
202
+ @render {
203
+ <div
204
+ {$props?.id}
205
+ class={[
206
+ "torp-segmented-control",
207
+ $props?.disabled ? "torp-segmented-control-disabled" : undefined,
208
+ $props?.class,
209
+ ]}
210
+ {$props?.style}
211
+ role="radiogroup"
212
+ aria-label={$props?.ariaLabel}
213
+ aria-disabled={$props?.disabled ? "true" : undefined}
214
+ data-valid={form.valid ? "valid" : "invalid"}
215
+ aria-describedby={form.messageId}
216
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
217
+ onfocusout={handleFocusOut}
218
+ &ref={root}
219
+ >
220
+ <slot />
221
+ </div>
222
+
223
+ <FormValues name={form.name} value={$state.value} />
224
+ }
225
+
226
+ @style {
227
+ /* Headless: layout only, so the segments sit in a row. All appearance
228
+ is up to the consuming theme */
229
+ .torp-segmented-control {
230
+ display: inline-flex;
231
+ }
232
+ }
233
+ }
@@ -0,0 +1,26 @@
1
+ export const SegmentedControlContextName: unique symbol = Symbol.for("torp.SegmentedControl");
2
+
3
+ export interface SegmentedControlContext {
4
+ /** Called from an item when it is added */
5
+ registerItem: (
6
+ index: number | undefined,
7
+ value: string | undefined,
8
+ disabled: boolean | undefined,
9
+ ) => ItemState;
10
+ /** Called from an item when it is removed */
11
+ removeItem: (index: number) => void;
12
+ /** Called from an item when it is selected */
13
+ triggerItem: (value: string) => void;
14
+ /** Called from an item when it receives a keyboard event */
15
+ handleItemKey: (index: number, e: KeyboardEvent) => void;
16
+ }
17
+
18
+ export interface ItemState {
19
+ index: number;
20
+ value: string;
21
+ active: boolean;
22
+ disabled: boolean;
23
+ parentDisabled: boolean;
24
+ /** Called in the SegmentedControl when an item needs to be focused */
25
+ setFocused: () => void;
26
+ }