@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,255 @@
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 NumberInputProps {
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 current value of the input; null if the field is empty */
20
+ value?: Bindable<number | null>;
21
+ /** The minimum allowed value */
22
+ min?: number;
23
+ /** The maximum allowed value */
24
+ max?: number;
25
+ /** The amount by which the value is increased or decreased (default 1) */
26
+ step?: number;
27
+ /** If set to true, the user cannot interact with this input */
28
+ disabled?: boolean;
29
+ /** An accessible name for the input */
30
+ ariaLabel?: string;
31
+ /** The name of the component's form data field */
32
+ name?: string;
33
+ /** Raised when the value has changed */
34
+ onchange?: (value: number | null) => void;
35
+ }
36
+
37
+ /**
38
+ * An input where the user enters or steps a number between minimum and
39
+ * maximum values, by typing, by pressing the arrow keys, or by pressing the
40
+ * Home, End, PageUp or PageDown keys.
41
+ *
42
+ * See [the WAI ARIA guidelines for Spin Buttons](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).
43
+ */
44
+ export default function NumberInput($props: NumberInputProps | undefined) {
45
+ let input: HTMLInputElement;
46
+
47
+ // Wire up the enclosing Form and Field, if any
48
+ const form = createFormField(
49
+ $props?.name,
50
+ $context[FormContextName] as FormContext | undefined,
51
+ $context[FieldContextName] as FieldContext | undefined,
52
+ );
53
+
54
+ function getMin(): number | undefined {
55
+ return $props?.min;
56
+ }
57
+ function getMax(): number | undefined {
58
+ return $props?.max;
59
+ }
60
+ function getStep(): number {
61
+ const step = $props?.step ?? 1;
62
+ return step > 0 ? step : 1;
63
+ }
64
+ function getDisabled(): boolean {
65
+ return $props?.disabled === true;
66
+ }
67
+
68
+ // The number of decimal places implied by the step, e.g. 0.25 -> 2
69
+ function getPrecision(): number {
70
+ const text = String(getStep());
71
+ const dot = text.indexOf(".");
72
+ return dot === -1 ? 0 : text.length - dot - 1;
73
+ }
74
+
75
+ let $state = $watch({
76
+ value: null as number | null,
77
+ text: "",
78
+ editing: false,
79
+ });
80
+
81
+ $bind($state, $props, "value");
82
+
83
+ // Keep the stored value clamped, so that out-of-range initial values
84
+ // don't later surface as user changes
85
+ $run(() => {
86
+ const clamped = clamp($state.value);
87
+ if (clamped !== $state.value) {
88
+ $state.value = clamped;
89
+ }
90
+ });
91
+
92
+ // The text tracks the value, unless the user is typing
93
+ $run(() => {
94
+ if (!$state.editing) {
95
+ $state.text = $state.value == null ? "" : String($state.value);
96
+ }
97
+ });
98
+
99
+ // Keep the input's value in sync with the text; typing sets the property
100
+ // directly, so an attribute alone would not update it
101
+ function syncInput() {
102
+ if (input && !$state.editing && input.value !== $state.text) {
103
+ input.value = $state.text;
104
+ }
105
+ }
106
+ $run(() => {
107
+ const text = $state.text;
108
+ const editing = $state.editing;
109
+ if (input && !editing && input.value !== text) {
110
+ input.value = text;
111
+ }
112
+ });
113
+ $onmount(() => syncInput());
114
+
115
+ // Dispatch the change event when a committed value changes
116
+ $handle((first) => {
117
+ const value = $state.value;
118
+ if (!first && !$state.editing) $props?.onchange?.(value);
119
+ });
120
+
121
+ function clamp(value: number | null): number | null {
122
+ if (value == null) return null;
123
+ let result = value;
124
+ if (getMin() != null) result = Math.max(getMin()!, result);
125
+ if (getMax() != null) result = Math.min(getMax()!, result);
126
+ return round(result);
127
+ }
128
+
129
+ function round(value: number): number {
130
+ const precision = getPrecision();
131
+ return precision ? Number(value.toFixed(precision)) : value;
132
+ }
133
+
134
+ function parse(text: string): number | null {
135
+ const trimmed = text.trim();
136
+ if (trimmed === "") return null;
137
+ const parsed = Number(trimmed);
138
+ return Number.isFinite(parsed) ? parsed : null;
139
+ }
140
+
141
+ function setValue(value: number | null) {
142
+ const next = clamp(value);
143
+ $state.editing = false;
144
+ if (next !== $state.value) {
145
+ $state.value = next;
146
+ form.handleInput();
147
+ }
148
+ // The text always matches what was committed
149
+ $state.text = next == null ? "" : String(next);
150
+ }
151
+
152
+ function stepValue(delta: number) {
153
+ if (getDisabled()) return;
154
+ const base = $state.value ?? 0;
155
+ setValue(base + delta);
156
+ }
157
+
158
+ function handleInput(e: Event) {
159
+ const el = e.currentTarget as HTMLInputElement;
160
+ $state.editing = true;
161
+ $state.text = el.value;
162
+ }
163
+
164
+ // Typing commits on blur or Enter; invalid text reverts
165
+ function commit() {
166
+ const text = $state.text;
167
+ $state.editing = false;
168
+ const parsed = parse(text);
169
+ if (parsed == null && text.trim() !== "") {
170
+ // Not a number: revert to the last committed value
171
+ $state.text = $state.value == null ? "" : String($state.value);
172
+ return;
173
+ }
174
+ setValue(parsed);
175
+ }
176
+
177
+ function handleBlur() {
178
+ commit();
179
+ form.handleBlur();
180
+ }
181
+
182
+ function handleKey(e: KeyboardEvent) {
183
+ if (getDisabled()) return;
184
+
185
+ switch (e.key) {
186
+ case "ArrowUp": {
187
+ e.preventDefault();
188
+ stepValue(getStep());
189
+ break;
190
+ }
191
+ case "ArrowDown": {
192
+ e.preventDefault();
193
+ stepValue(-getStep());
194
+ break;
195
+ }
196
+ case "PageUp": {
197
+ e.preventDefault();
198
+ stepValue(getStep() * 10);
199
+ break;
200
+ }
201
+ case "PageDown": {
202
+ e.preventDefault();
203
+ stepValue(-getStep() * 10);
204
+ break;
205
+ }
206
+ case "Home": {
207
+ if (getMin() != null) {
208
+ e.preventDefault();
209
+ setValue(getMin()!);
210
+ }
211
+ break;
212
+ }
213
+ case "End": {
214
+ if (getMax() != null) {
215
+ e.preventDefault();
216
+ setValue(getMax()!);
217
+ }
218
+ break;
219
+ }
220
+ case "Enter": {
221
+ e.preventDefault();
222
+ commit();
223
+ break;
224
+ }
225
+ }
226
+ }
227
+
228
+ @render {
229
+ <input
230
+ id={$props?.id}
231
+ class={["torp-number-input", $props?.class]}
232
+ {$props?.style}
233
+ type="text"
234
+ inputmode="decimal"
235
+ role="spinbutton"
236
+ value={$state.text}
237
+ &ref={input}
238
+ tabindex={getDisabled() ? -1 : 0}
239
+ aria-label={$props?.ariaLabel}
240
+ aria-valuemin={getMin()}
241
+ aria-valuemax={getMax()}
242
+ aria-valuenow={$state.value == null ? undefined : $state.value}
243
+ aria-disabled={getDisabled() ? "true" : undefined}
244
+ data-valid={form.valid ? "valid" : "invalid"}
245
+ aria-describedby={form.messageId}
246
+ aria-invalid={form.inField && !form.valid ? "true" : undefined}
247
+ disabled={getDisabled()}
248
+ oninput={handleInput}
249
+ onblur={handleBlur}
250
+ onkeydown={handleKey}
251
+ />
252
+
253
+ <FormValues name={form.name} value={$state.value} />
254
+ }
255
+ }
@@ -0,0 +1,44 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface NumberInputProps {
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 current value of the input; null if the field is empty */
12
+ value?: Bindable<number | null>;
13
+ /** The minimum allowed value */
14
+ min?: number;
15
+ /** The maximum allowed value */
16
+ max?: number;
17
+ /** The amount by which the value is increased or decreased (default 1) */
18
+ step?: number;
19
+ /** If set to true, the user cannot interact with this input */
20
+ disabled?: boolean;
21
+ /** An accessible name for the input */
22
+ ariaLabel?: string;
23
+ /** The name of the component's form data field */
24
+ name?: string;
25
+ /** Raised when the value has changed */
26
+ onchange?: (value: number | null) => void;
27
+ }
28
+
29
+ /**
30
+ * An input where the user enters or steps a number between minimum and
31
+ * maximum values, by typing, by pressing the arrow keys, or by pressing the
32
+ * Home, End, PageUp or PageDown keys.
33
+ *
34
+ * See [the WAI ARIA guidelines for Spin Buttons](https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/).
35
+ */
36
+ declare function NumberInput(
37
+ $parent: ParentNode,
38
+ $anchor: Node | null,
39
+ $props: NumberInputProps | undefined,
40
+ $context?: Record<PropertyKey, any>,
41
+ $slots?: Record<string, SlotRender>,
42
+ ): void;
43
+
44
+ export { NumberInput };
@@ -0,0 +1,2 @@
1
+ import NumberInput from "./NumberInput.torp";
2
+ export { NumberInput };
@@ -1,27 +1,43 @@
1
1
  import type { ClassValue, StyleValue } from "@torpor/view";
2
- import { PaginationContextName, type PaginationContext, type ItemState, type PageNumber } from "./PaginationTypes";
2
+ import {
3
+ type ItemState,
4
+ type PageNumber,
5
+ type PaginationContext,
6
+ PaginationContextName,
7
+ } from "./PaginationTypes";
8
+ import buildPages from "./buildPages";
3
9
 
4
10
  interface PaginationProps {
5
11
  /** An ID for the root element */
6
12
  id?: string;
7
13
  /** Classes for the root element */
8
- class?: ClassValue,
14
+ class?: ClassValue;
9
15
  /** Styles for the root element */
10
- style?: StyleValue,
16
+ style?: StyleValue;
11
17
  /** The total number of pages (or items if the page size is greater than 1) */
12
- count: number,
13
- /** The active page number */
14
- number: number,
18
+ count: number;
19
+ /** The active page */
20
+ page: number;
21
+ /** The number of items on each page **/
22
+ pageSize?: number;
23
+ /** The maximum number of pages to display (gaps will be indicated with ellipses) **/
24
+ maxPages?: number;
25
+ /** Accessible label for the pagination navigation */
26
+ "aria-label"?: string;
27
+ /** Called when a user wants to jump to a specific page (e.g., via ellipsis) */
28
+ onjump?: (targetPage: number, jumpTo: (page: number) => void) => void;
15
29
  }
16
30
 
17
31
  /**
18
32
  * Buttons with page numbers that can be used to move between pages.
19
33
  */
20
34
  export default function Pagination($props: PaginationProps) {
21
- // TODO: Should we (by default) create PaginationItems?
35
+ let $state = $watch({
36
+ get pages() {
37
+ return buildPages($props.count, $props.page, $props.pageSize, $props.maxPages);
38
+ },
39
+ });
22
40
 
23
- // TODO: Do we actually need ItemStates when we're creating the item collection ourselves??
24
- // A collection containing the active state and some functions for each item
25
41
  let itemStates: ItemState[] = [];
26
42
 
27
43
  // Put stuff into a PaginationContext
@@ -29,23 +45,16 @@ export default function Pagination($props: PaginationProps) {
29
45
  registerItem,
30
46
  removeItem,
31
47
  toggleItem,
32
- handleItemKey
48
+ handleItemKey,
49
+ jumpToPage: (page: number) => {
50
+ $props.page = Math.max(1, Math.min($props.count, page));
51
+ },
33
52
  };
34
53
  $context[PaginationContextName] = context;
35
54
 
36
- function registerItem(
37
- target: PageNumber,
38
- setActive: (value: boolean) => void,
39
- setFocused: () => void
40
- ) {
55
+ function registerItem(item: ItemState) {
41
56
  // Add the item's state to the array
42
- const newItemState: ItemState = {
43
- target,
44
- active: false,
45
- setActive,
46
- setFocused
47
- };
48
- itemStates.push(newItemState);
57
+ itemStates.push(item);
49
58
  }
50
59
 
51
60
  function removeItem(target: PageNumber) {
@@ -59,31 +68,30 @@ export default function Pagination($props: PaginationProps) {
59
68
  function toggleItem(target: PageNumber) {
60
69
  switch (target) {
61
70
  case "start": {
62
- $props.number = 1;
71
+ $props.page = 1;
63
72
  break;
64
73
  }
65
74
  case "previous": {
66
- $props.number = Math.max(1, $props.number - 1);
75
+ $props.page = Math.max(1, $props.page - 1);
67
76
  break;
68
77
  }
69
78
  case "next": {
70
- $props.number = Math.min($props.count, $props.number + 1);
79
+ $props.page = Math.min($props.count, $props.page + 1);
71
80
  break;
72
81
  }
73
82
  case "end": {
74
- $props.number = $props.count;
83
+ $props.page = $props.count;
75
84
  break;
76
85
  }
77
86
  case "startgap":
78
87
  case "endgap": {
79
- let input = prompt("Go to page:");
80
- if (input) {
81
- $props.number = parseInt(input);
88
+ if ($props.onjump) {
89
+ $props.onjump($props.page, context.jumpToPage);
82
90
  }
83
91
  break;
84
92
  }
85
93
  default: {
86
- $props.number = target;
94
+ $props.page = target;
87
95
  }
88
96
  }
89
97
 
@@ -102,15 +110,12 @@ export default function Pagination($props: PaginationProps) {
102
110
  }
103
111
 
104
112
  function handleItemKey(e: KeyboardEvent) {
105
- // TODO: Actually just change focus??
106
113
  switch (e.key) {
107
- case "Left":
108
114
  case "ArrowLeft": {
109
115
  toggleItem("previous");
110
116
  e.preventDefault();
111
117
  break;
112
118
  }
113
- case "Right":
114
119
  case "ArrowRight": {
115
120
  toggleItem("next");
116
121
  e.preventDefault();
@@ -135,10 +140,12 @@ export default function Pagination($props: PaginationProps) {
135
140
  {$props?.id}
136
141
  class={["torp-pagination", $props?.class]}
137
142
  {$props?.style}
143
+ role="navigation"
144
+ aria-label={$props["aria-label"] ?? "Pagination"}
138
145
  tabindex="0"
139
146
  onfocus={handleFocus}
140
147
  >
141
- <slot />
148
+ <slot {$state.pages} />
142
149
  </div>
143
150
  }
144
151
  }
@@ -1,20 +1,26 @@
1
1
  import type { ClassValue, StyleValue } from "@torpor/view";
2
- import { PaginationContextName, type PaginationContext, type PageNumber } from "./PaginationTypes";
2
+ import ChevronDoubleLeft from "../icons/ChevronDoubleLeft.torp";
3
+ import ChevronDoubleRight from "../icons/ChevronDoubleRight.torp";
4
+ import ChevronLeft from "../icons/ChevronLeft.torp";
5
+ import ChevronRight from "../icons/ChevronRight.torp";
6
+ import { type PageNumber, type PaginationContext, PaginationContextName } from "./PaginationTypes";
3
7
 
4
8
  interface PaginationItemProps {
5
9
  /** An ID for the root element */
6
10
  id?: string;
7
11
  /** Classes for the root element */
8
- class?: ClassValue,
12
+ class?: ClassValue;
9
13
  /** Styles for the root element */
10
- style?: StyleValue,
14
+ style?: StyleValue;
11
15
  /** The page for the button */
12
16
  page: PageNumber;
13
17
  /** Whether this is the active item */
14
18
  active?: boolean;
19
+ /** Whether this item is disabled */
20
+ disabled?: boolean;
15
21
  /** Raised when this item is made active */
16
- ontoggle?: (active: boolean) => void,
17
- };
22
+ onchange?: (active: boolean) => void;
23
+ }
18
24
 
19
25
  export default function PaginationItem($props: PaginationItemProps) {
20
26
  let button: HTMLButtonElement;
@@ -24,10 +30,28 @@ export default function PaginationItem($props: PaginationItemProps) {
24
30
  if (!context) {
25
31
  throw new Error("PaginationItem must be contained within a Pagination");
26
32
  }
27
- const { registerItem, /*removeItem,*/ toggleItem, handleItemKey } = context;
33
+ const { registerItem, removeItem, toggleItem, handleItemKey } = context;
28
34
 
29
35
  // Register this item with the parent Pagination, which will handle setting this item's focus on keyboard events
30
- registerItem($props.page, setActive, setFocused);
36
+ registerItem({ target: $props.page, active: false, setActive, setFocused });
37
+
38
+ $run(() => {
39
+ return () => {
40
+ removeItem($props.page);
41
+ };
42
+ });
43
+
44
+ // Call onchange when the active state changes
45
+ let prevActive = $props.active ?? false;
46
+ $run(() => {
47
+ const active = $props.active ?? false;
48
+ if (active !== prevActive) {
49
+ prevActive = active;
50
+ if ($props?.onchange) {
51
+ $props.onchange(active);
52
+ }
53
+ }
54
+ });
31
55
 
32
56
  // This function is called by the parent Pagination to set this item's active value
33
57
  function setActive(value: boolean) {
@@ -36,8 +60,8 @@ export default function PaginationItem($props: PaginationItemProps) {
36
60
 
37
61
  /** Raised when this item is made active */
38
62
  //dispatch("toggle", value);
39
- if ($props?.ontoggle) {
40
- $props.ontoggle(value);
63
+ if ($props?.onchange) {
64
+ $props.onchange(value);
41
65
  }
42
66
  }
43
67
  }
@@ -64,6 +88,9 @@ export default function PaginationItem($props: PaginationItemProps) {
64
88
  {$props?.id}
65
89
  class={["torp-pagination-item", $props?.class]}
66
90
  {$props?.style}
91
+ role="button"
92
+ aria-current="page"
93
+ aria-label={`Page ${$props.page}`}
67
94
  data-state={$props.active ? "active" : "inactive"}
68
95
  >
69
96
  {$props.page}
@@ -73,6 +100,16 @@ export default function PaginationItem($props: PaginationItemProps) {
73
100
  type="button"
74
101
  {$props?.id}
75
102
  class={["torp-pagination-item", $props?.class]}
103
+ {$props?.disabled}
104
+ aria-label={
105
+ $props.page === "start" ? "First page" :
106
+ $props.page === "previous" ? "Previous page" :
107
+ $props.page === "next" ? "Next page" :
108
+ $props.page === "end" ? "Last page" :
109
+ $props.page === "startgap" || $props.page === "endgap" ? "More pages" :
110
+ `Page ${$props.page}`
111
+ }
112
+ aria-disabled={$props.disabled ? "true" : undefined}
76
113
  data-type={$props.page}
77
114
  data-state={$props.active ? "active" : "inactive"}
78
115
  &ref={button}
@@ -81,13 +118,13 @@ export default function PaginationItem($props: PaginationItemProps) {
81
118
  >
82
119
  <slot>
83
120
  @if ($props.page === "start") {
84
- <span>&lt;&lt;</span>
121
+ <ChevronDoubleLeft />
85
122
  } else if ($props.page === "previous") {
86
- <span>&lt;</span>
123
+ <ChevronLeft />
87
124
  } else if ($props.page === "next") {
88
- <span>&gt;</span>
125
+ <ChevronRight />
89
126
  } else if ($props.page === "end") {
90
- <span>&gt;&gt;</span>
127
+ <ChevronDoubleRight />
91
128
  } else if ($props.page === "startgap" || $props.page === "endgap") {
92
129
  <span>…</span>
93
130
  } else {
@@ -2,17 +2,14 @@ export const PaginationContextName: unique symbol = Symbol.for("torp.Pagination"
2
2
 
3
3
  export interface PaginationContext {
4
4
  // The registerItem function is called from each SeriesItem to register itself and its
5
- // methods with this component
6
- registerItem: (
7
- target: PageNumber,
8
- setActive: (value: boolean) => void,
9
- setFocused: () => void,
10
- ) => void;
5
+ // state with this component
6
+ registerItem: (item: ItemState) => void;
11
7
  // The removeItem function is called from a SeriesItem when it has been unloaded so that we
12
8
  // can remove it from the itemStates array
13
9
  removeItem: (target: PageNumber) => void;
14
10
  toggleItem: (target: PageNumber) => void;
15
11
  handleItemKey: (e: KeyboardEvent) => void;
12
+ jumpToPage: (page: number) => void;
16
13
  }
17
14
 
18
15
  export interface ItemState {
@@ -4,13 +4,13 @@ import { type PageNumber } from "./PaginationTypes";
4
4
  * Builds the pages that should be displayed for pagination.
5
5
  *
6
6
  * @param count The total number of pages (or items if the page size is greater than 1)
7
- * @param number The active page number
7
+ * @param page The active page
8
8
  * @param pageSize The number of items on each page
9
9
  * @param maxPages The maximum number of pages to display (gaps will be indicated with ellipses)
10
10
  */
11
11
  export default function buildPages(
12
12
  count: number,
13
- number: number,
13
+ page: number,
14
14
  pageSize: number = 1,
15
15
  maxPages: number = 9,
16
16
  ): PageNumber[] {
@@ -18,7 +18,7 @@ export default function buildPages(
18
18
  const pageCount = Math.ceil(count / pageSize);
19
19
 
20
20
  if (pageCount <= maxPages) {
21
- // There"s no need for ellipses, just return the numbers
21
+ // There's no need for ellipses, just return the numbers
22
22
  return Array.from({ length: pageCount })
23
23
  .fill(0)
24
24
  .map((_, i) => i + 1);
@@ -26,8 +26,8 @@ export default function buildPages(
26
26
  // Get the start and end around the current page number
27
27
  // Use floor and ceil in case there is an even number of pages (e.g. if
28
28
  // maxPages is 8, there will be 3 numbers at the start and 4 at the end)
29
- let start = number - Math.floor((maxPages - 1) / 2);
30
- let end = number + Math.ceil((maxPages - 1) / 2);
29
+ let start = page - Math.floor((maxPages - 1) / 2);
30
+ let end = page + Math.ceil((maxPages - 1) / 2);
31
31
  if (start < 1) {
32
32
  end += Math.abs(start) + 1;
33
33
  start = 1;
@@ -44,12 +44,16 @@ export default function buildPages(
44
44
  // Replace numbers at the start and end with ellipses if necessary
45
45
  if (numbers[0] !== 1) {
46
46
  numbers[0] = 1;
47
+ //if (numbers.length > 1) {
47
48
  numbers[1] = "startgap";
49
+ //}
48
50
  }
49
51
  const l = numbers.length;
50
52
  if (numbers[l - 1] !== pageCount) {
51
53
  numbers[l - 1] = pageCount;
54
+ //if (l > 2) {
52
55
  numbers[l - 2] = "endgap";
56
+ //}
53
57
  }
54
58
 
55
59
  return numbers;
@@ -5,13 +5,21 @@ interface PaginationProps {
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 total number of pages (or items if the page size is greater than 1) */
12
- count: number,
13
- /** The active page number */
14
- number: number,
12
+ count: number;
13
+ /** The active page */
14
+ page: number;
15
+ /** The number of items on each page **/
16
+ pageSize?: number;
17
+ /** The maximum number of pages to display (gaps will be indicated with ellipses) **/
18
+ maxPages?: number;
19
+ /** Accessible label for the pagination navigation */
20
+ "aria-label"?: string;
21
+ /** Called when a user wants to jump to a specific page (e.g., via ellipsis) */
22
+ onjump?: (targetPage: number, jumpTo: (page: number) => void) => void;
15
23
  }
16
24
 
17
25
  /**
@@ -31,15 +39,17 @@ interface PaginationItemProps {
31
39
  /** An ID for the root element */
32
40
  id?: string;
33
41
  /** Classes for the root element */
34
- class?: ClassValue,
42
+ class?: ClassValue;
35
43
  /** Styles for the root element */
36
- style?: StyleValue,
44
+ style?: StyleValue;
37
45
  /** The page for the button */
38
46
  page: PageNumber;
39
47
  /** Whether this is the active item */
40
48
  active?: boolean;
49
+ /** Whether this item is disabled */
50
+ disabled?: boolean;
41
51
  /** Raised when this item is made active */
42
- ontoggle?: (active: boolean) => void,
52
+ onchange?: (active: boolean) => void;
43
53
  }
44
54
 
45
55
  declare function PaginationItem(
@@ -50,4 +60,4 @@ declare function PaginationItem(
50
60
  $slots?: Record<string, SlotRender>,
51
61
  ): void;
52
62
 
53
- export { Pagination, PaginationItem };
63
+ export { Pagination, PaginationItem };