@torpor/ui 0.2.1 → 0.3.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 (290) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/Accordion/Accordion.torp +29 -22
  3. package/dist/Accordion/AccordionContent.torp +7 -8
  4. package/dist/Accordion/AccordionHeader.torp +4 -6
  5. package/dist/Accordion/AccordionItem.torp +15 -12
  6. package/dist/Accordion/AccordionTrigger.torp +6 -7
  7. package/dist/Accordion/AccordionTypes.ts +4 -3
  8. package/dist/Accordion/index.d.ts +5 -5
  9. package/dist/Breadcrumb/Breadcrumb.torp +9 -10
  10. package/dist/Breadcrumb/BreadcrumbItem.torp +5 -7
  11. package/dist/Breadcrumb/BreadcrumbSeparator.torp +3 -5
  12. package/dist/Breadcrumb/index.d.ts +8 -7
  13. package/dist/Calendar/Calendar.torp +12 -13
  14. package/dist/Calendar/CalendarDay.torp +3 -3
  15. package/dist/Contextual/Contextual.torp +125 -0
  16. package/dist/{Popover/PopoverContentContextual.torp → Contextual/ContextualContent.torp} +15 -15
  17. package/dist/Contextual/ContextualTrigger.torp +66 -0
  18. package/dist/Contextual/ContextualTypes.ts +30 -0
  19. package/dist/Contextual/index.d.ts +90 -0
  20. package/dist/Contextual/index.js +14 -0
  21. package/dist/Contextual/index.js.map +1 -0
  22. package/dist/Dialog/Dialog.torp +38 -72
  23. package/dist/Dialog/DialogBody.torp +6 -8
  24. package/dist/Dialog/DialogButton.torp +14 -12
  25. package/dist/Dialog/DialogFooter.torp +3 -5
  26. package/dist/Dialog/DialogHeader.torp +6 -8
  27. package/dist/Dialog/DialogTypes.ts +3 -2
  28. package/dist/Dialog/index.d.ts +16 -11
  29. package/dist/Disclosure/Disclosure.torp +22 -23
  30. package/dist/Disclosure/DisclosureContent.torp +7 -8
  31. package/dist/Disclosure/DisclosureHeader.torp +5 -7
  32. package/dist/Disclosure/DisclosureTrigger.torp +7 -8
  33. package/dist/Disclosure/DisclosureTypes.ts +0 -1
  34. package/dist/Disclosure/index.d.ts +12 -12
  35. package/dist/Drawer/Drawer.torp +101 -70
  36. package/dist/Drawer/DrawerContent.torp +80 -0
  37. package/dist/Drawer/DrawerOverlay.torp +38 -0
  38. package/dist/Drawer/DrawerTrigger.torp +35 -0
  39. package/dist/Drawer/DrawerTypes.ts +24 -0
  40. package/dist/Drawer/index.d.ts +90 -3
  41. package/dist/Drawer/index.js +13 -1
  42. package/dist/Form/Field.torp +2 -4
  43. package/dist/Form/Form.torp +7 -9
  44. package/dist/Form/Input.torp +20 -17
  45. package/dist/Form/Label.torp +1 -1
  46. package/dist/Form/Message.torp +1 -1
  47. package/dist/Form/index.d.ts +18 -4
  48. package/dist/Menu/Menu.torp +16 -15
  49. package/dist/Menu/MenuButton.torp +19 -13
  50. package/dist/Menu/MenuCheck.torp +18 -14
  51. package/dist/Menu/MenuGroup.torp +4 -6
  52. package/dist/Menu/MenuIndicator.torp +5 -7
  53. package/dist/Menu/MenuPopout.torp +12 -15
  54. package/dist/Menu/MenuPopoutContent.torp +6 -8
  55. package/dist/Menu/MenuPopoutTrigger.torp +12 -14
  56. package/dist/Menu/MenuRadio.torp +14 -14
  57. package/dist/Menu/MenuRadioGroup.torp +22 -14
  58. package/dist/Menu/MenuSeparator.torp +3 -5
  59. package/dist/Menu/MenuTypes.ts +7 -5
  60. package/dist/Menu/index.d.ts +28 -22
  61. package/dist/MenuBar/MenuBar.torp +13 -14
  62. package/dist/MenuBar/MenuBarItem.torp +13 -16
  63. package/dist/MenuBar/MenuBarItemContent.torp +7 -9
  64. package/dist/MenuBar/MenuBarItemTrigger.torp +6 -9
  65. package/dist/MenuBar/index.d.ts +16 -16
  66. package/dist/Modal/Modal.torp +118 -0
  67. package/dist/Modal/ModalContent.torp +77 -0
  68. package/dist/Modal/ModalOverlay.torp +38 -0
  69. package/dist/Modal/ModalTrigger.torp +50 -0
  70. package/dist/Modal/ModalTypes.ts +22 -0
  71. package/dist/Modal/index.d.ts +108 -0
  72. package/dist/Modal/index.js +33 -0
  73. package/dist/Modal/index.js.map +1 -0
  74. package/dist/Modal/showModal.ts +19 -0
  75. package/dist/Modal/showPrompt.ts +23 -0
  76. package/dist/NavMenu/NavMenu.torp +7 -8
  77. package/dist/NavMenu/NavMenuButton.torp +6 -8
  78. package/dist/NavMenu/NavMenuElement.torp +8 -8
  79. package/dist/NavMenu/NavMenuGroup.torp +4 -6
  80. package/dist/NavMenu/NavMenuGroupHeader.torp +5 -6
  81. package/dist/NavMenu/NavMenuLink.torp +8 -11
  82. package/dist/NavMenu/NavMenuPopout.torp +25 -17
  83. package/dist/NavMenu/NavMenuPopoutButton.torp +12 -13
  84. package/dist/NavMenu/NavMenuPopoutContent.torp +12 -12
  85. package/dist/NavMenu/NavMenuSeparator.torp +3 -5
  86. package/dist/NavMenu/index.d.ts +17 -11
  87. package/dist/Notification/Notification.torp +22 -13
  88. package/dist/Notification/NotificationClose.torp +3 -5
  89. package/dist/Notification/NotificationContainer.torp +3 -5
  90. package/dist/Notification/NotificationContent.torp +3 -5
  91. package/dist/Notification/index.d.ts +4 -4
  92. package/dist/Popover/Popover.torp +31 -25
  93. package/dist/Popover/PopoverContent.torp +35 -10
  94. package/dist/Popover/PopoverTrigger.torp +24 -4
  95. package/dist/Popover/PopoverTypes.ts +1 -1
  96. package/dist/Popover/index.d.ts +16 -124
  97. package/dist/Popover/index.js +1 -163
  98. package/dist/Popover/index.js.map +1 -1
  99. package/dist/TabGroup/TabContent.torp +12 -9
  100. package/dist/TabGroup/TabGroup.torp +41 -35
  101. package/dist/TabGroup/TabGroupTypes.ts +10 -5
  102. package/dist/TabGroup/TabHeader.torp +15 -8
  103. package/dist/TabGroup/TabList.torp +4 -6
  104. package/dist/TabGroup/TabTrigger.torp +8 -9
  105. package/dist/TabGroup/index.d.ts +14 -6
  106. package/dist/icons/ArrowDown.torp +3 -1
  107. package/dist/icons/ArrowLeft.torp +3 -1
  108. package/dist/icons/ArrowRight.torp +3 -1
  109. package/dist/icons/ArrowUp.torp +3 -1
  110. package/dist/icons/Bars.torp +3 -1
  111. package/dist/icons/Check.torp +3 -1
  112. package/dist/icons/ChevronDown.torp +3 -1
  113. package/dist/icons/ChevronLeft.torp +3 -1
  114. package/dist/icons/ChevronRight.torp +3 -1
  115. package/dist/icons/ChevronUp.torp +3 -1
  116. package/dist/icons/Cross.torp +3 -1
  117. package/dist/icons/IconTypes.ts +6 -0
  118. package/dist/icons/Loading.torp +3 -1
  119. package/dist/icons/Minus.torp +3 -1
  120. package/dist/icons/Plus.torp +3 -1
  121. package/dist/icons/index.d.ts +271 -14
  122. package/dist/index.d.ts +17 -4
  123. package/dist/metafile-esm.json +1 -1
  124. package/dist/motion/fade.ts +15 -6
  125. package/dist/motion/grow.ts +23 -0
  126. package/dist/motion/index.d.ts +19 -2
  127. package/dist/motion/index.js +93 -3
  128. package/dist/motion/index.js.map +1 -1
  129. package/dist/motion/measure.ts +28 -0
  130. package/dist/motion/slide.ts +45 -0
  131. package/dist/mount/clickPopover.ts +51 -0
  132. package/dist/mount/contextPopover.ts +51 -0
  133. package/dist/mount/hoverPopover.ts +58 -0
  134. package/package.json +21 -14
  135. package/scripts/postbuild.ts +11 -1
  136. package/src/Accordion/Accordion.torp +29 -22
  137. package/src/Accordion/AccordionContent.torp +7 -8
  138. package/src/Accordion/AccordionHeader.torp +4 -6
  139. package/src/Accordion/AccordionItem.torp +15 -12
  140. package/src/Accordion/AccordionTrigger.torp +6 -7
  141. package/src/Accordion/AccordionTypes.ts +4 -3
  142. package/src/Breadcrumb/Breadcrumb.torp +9 -10
  143. package/src/Breadcrumb/BreadcrumbItem.torp +5 -7
  144. package/src/Breadcrumb/BreadcrumbSeparator.torp +3 -5
  145. package/src/Calendar/Calendar.torp +12 -13
  146. package/src/Calendar/CalendarDay.torp +3 -3
  147. package/src/Contextual/Contextual.torp +125 -0
  148. package/src/{Popover/PopoverContentContextual.torp → Contextual/ContextualContent.torp} +15 -15
  149. package/src/Contextual/ContextualTrigger.torp +66 -0
  150. package/src/Contextual/ContextualTypes.ts +30 -0
  151. package/src/Contextual/index.ts +5 -0
  152. package/src/Dialog/Dialog.torp +38 -72
  153. package/src/Dialog/DialogBody.torp +6 -8
  154. package/src/Dialog/DialogButton.torp +14 -12
  155. package/src/Dialog/DialogFooter.torp +3 -5
  156. package/src/Dialog/DialogHeader.torp +6 -8
  157. package/src/Dialog/DialogTypes.ts +3 -2
  158. package/src/Disclosure/Disclosure.torp +22 -23
  159. package/src/Disclosure/DisclosureContent.torp +7 -8
  160. package/src/Disclosure/DisclosureHeader.torp +5 -7
  161. package/src/Disclosure/DisclosureTrigger.torp +7 -8
  162. package/src/Disclosure/DisclosureTypes.ts +0 -1
  163. package/src/Drawer/Drawer.torp +101 -70
  164. package/src/Drawer/DrawerContent.torp +80 -0
  165. package/src/Drawer/DrawerOverlay.torp +38 -0
  166. package/src/Drawer/DrawerTrigger.torp +35 -0
  167. package/src/Drawer/DrawerTypes.ts +24 -0
  168. package/src/Drawer/index.ts +4 -1
  169. package/src/Form/Field.torp +2 -4
  170. package/src/Form/Form.torp +7 -9
  171. package/src/Form/Input.torp +20 -17
  172. package/src/Form/Label.torp +1 -1
  173. package/src/Form/Message.torp +1 -1
  174. package/src/Menu/Menu.torp +16 -15
  175. package/src/Menu/MenuButton.torp +19 -13
  176. package/src/Menu/MenuCheck.torp +18 -14
  177. package/src/Menu/MenuGroup.torp +4 -6
  178. package/src/Menu/MenuIndicator.torp +5 -7
  179. package/src/Menu/MenuPopout.torp +12 -15
  180. package/src/Menu/MenuPopoutContent.torp +6 -8
  181. package/src/Menu/MenuPopoutTrigger.torp +12 -14
  182. package/src/Menu/MenuRadio.torp +14 -14
  183. package/src/Menu/MenuRadioGroup.torp +22 -14
  184. package/src/Menu/MenuSeparator.torp +3 -5
  185. package/src/Menu/MenuTypes.ts +7 -5
  186. package/src/MenuBar/MenuBar.torp +13 -14
  187. package/src/MenuBar/MenuBarItem.torp +13 -16
  188. package/src/MenuBar/MenuBarItemContent.torp +7 -9
  189. package/src/MenuBar/MenuBarItemTrigger.torp +6 -9
  190. package/src/Modal/Modal.torp +118 -0
  191. package/src/Modal/ModalContent.torp +77 -0
  192. package/src/Modal/ModalOverlay.torp +38 -0
  193. package/src/Modal/ModalTrigger.torp +50 -0
  194. package/src/Modal/ModalTypes.ts +22 -0
  195. package/src/Modal/index.ts +10 -0
  196. package/src/Modal/showModal.ts +19 -0
  197. package/src/Modal/showPrompt.ts +23 -0
  198. package/src/NavMenu/NavMenu.torp +7 -8
  199. package/src/NavMenu/NavMenuButton.torp +6 -8
  200. package/src/NavMenu/NavMenuElement.torp +8 -8
  201. package/src/NavMenu/NavMenuGroup.torp +4 -6
  202. package/src/NavMenu/NavMenuGroupHeader.torp +5 -6
  203. package/src/NavMenu/NavMenuLink.torp +8 -11
  204. package/src/NavMenu/NavMenuPopout.torp +25 -17
  205. package/src/NavMenu/NavMenuPopoutButton.torp +12 -13
  206. package/src/NavMenu/NavMenuPopoutContent.torp +12 -12
  207. package/src/NavMenu/NavMenuSeparator.torp +3 -5
  208. package/src/Notification/Notification.torp +22 -13
  209. package/src/Notification/NotificationClose.torp +3 -5
  210. package/src/Notification/NotificationContainer.torp +3 -5
  211. package/src/Notification/NotificationContent.torp +3 -5
  212. package/src/Popover/Popover.torp +31 -25
  213. package/src/Popover/PopoverContent.torp +35 -10
  214. package/src/Popover/PopoverTrigger.torp +24 -4
  215. package/src/Popover/PopoverTypes.ts +1 -1
  216. package/src/Popover/index.ts +1 -24
  217. package/src/Slider/Slider.torp +3 -3
  218. package/src/TabGroup/TabContent.torp +12 -9
  219. package/src/TabGroup/TabGroup.torp +41 -35
  220. package/src/TabGroup/TabGroupTypes.ts +10 -5
  221. package/src/TabGroup/TabHeader.torp +15 -8
  222. package/src/TabGroup/TabList.torp +4 -6
  223. package/src/TabGroup/TabTrigger.torp +8 -9
  224. package/src/icons/ArrowDown.torp +3 -1
  225. package/src/icons/ArrowLeft.torp +3 -1
  226. package/src/icons/ArrowRight.torp +3 -1
  227. package/src/icons/ArrowUp.torp +3 -1
  228. package/src/icons/Bars.torp +3 -1
  229. package/src/icons/Check.torp +3 -1
  230. package/src/icons/ChevronDown.torp +3 -1
  231. package/src/icons/ChevronLeft.torp +3 -1
  232. package/src/icons/ChevronRight.torp +3 -1
  233. package/src/icons/ChevronUp.torp +3 -1
  234. package/src/icons/Cross.torp +3 -1
  235. package/src/icons/IconTypes.ts +6 -0
  236. package/src/icons/Loading.torp +3 -1
  237. package/src/icons/Minus.torp +3 -1
  238. package/src/icons/Plus.torp +3 -1
  239. package/src/motion/fade.ts +15 -6
  240. package/src/motion/grow.ts +23 -0
  241. package/src/motion/index.ts +4 -1
  242. package/src/motion/measure.ts +28 -0
  243. package/src/motion/slide.ts +45 -0
  244. package/src/mount/clickPopover.ts +51 -0
  245. package/src/mount/contextPopover.ts +51 -0
  246. package/src/mount/hoverPopover.ts +58 -0
  247. package/test/Accordion/components/AccordionAccessibility.torp +1 -3
  248. package/test/Accordion/components/AccordionKeyboard.torp +1 -3
  249. package/test/Accordion/components/AccordionList.torp +1 -3
  250. package/test/Accordion/components/AccordionMultiple.torp +2 -4
  251. package/test/Accordion/components/AccordionSingle.torp +2 -4
  252. package/test/Dialog/accessibility-modal.test.ts +2 -2
  253. package/test/Dialog/components/DialogAccessibility.torp +8 -14
  254. package/test/Dialog/components/DialogFunction.torp +20 -4
  255. package/test/Dialog/components/DialogModal.torp +30 -0
  256. package/test/Dialog/components/DialogVariable.torp +7 -7
  257. package/test/Disclosure/components/DisclosureTest.torp +1 -3
  258. package/test/Menu/components/MenuAccessibility.torp +5 -5
  259. package/test/Menu/components/MenuBasic.torp +5 -5
  260. package/test/Notification/components/NotificationFunction.torp +0 -3
  261. package/test/Notification/components/NotificationVariable.torp +1 -1
  262. package/test/Popover/components/PopoverFunction.torp +37 -0
  263. package/test/Popover/components/PopoverTrigger.torp +36 -0
  264. package/test/Popover/components/{PromptVariable.torp → PopoverVariable.torp} +5 -3
  265. package/test/Popover/function.test.ts +2 -7
  266. package/test/Popover/trigger.test.ts +27 -0
  267. package/test/Popover/variable.test.ts +2 -2
  268. package/test/TabGroup/components/TabGroupAccessibility.torp +1 -3
  269. package/test/TabGroup/components/TabGroupKeyboard.torp +1 -3
  270. package/test/TabGroup/components/TabGroupList.torp +1 -3
  271. package/test/TabGroup/components/TabGroupTrigger.torp +1 -3
  272. package/tsup.config.ts +2 -0
  273. package/dist/Popover/PopoverContentAnchored.torp +0 -113
  274. package/dist/Popover/PopoverOverlay.torp +0 -39
  275. package/dist/Popover/PopoverTriggerContextual.torp +0 -46
  276. package/dist/Popover/PopoverTriggerHover.torp +0 -47
  277. package/dist/Popover/mountClickPopover.ts +0 -50
  278. package/dist/Popover/mountContextPopover.ts +0 -50
  279. package/dist/Popover/mountHoverPopover.ts +0 -57
  280. package/dist/Popover/showPopoverPrompt.ts +0 -23
  281. package/src/Popover/PopoverContentAnchored.torp +0 -113
  282. package/src/Popover/PopoverOverlay.torp +0 -39
  283. package/src/Popover/PopoverTriggerContextual.torp +0 -46
  284. package/src/Popover/PopoverTriggerHover.torp +0 -47
  285. package/src/Popover/mountClickPopover.ts +0 -50
  286. package/src/Popover/mountContextPopover.ts +0 -50
  287. package/src/Popover/mountHoverPopover.ts +0 -57
  288. package/src/Popover/showPopoverPrompt.ts +0 -23
  289. package/test/Dialog/components/CustomDialog.torp +0 -20
  290. package/test/Popover/components/PromptFunction.torp +0 -19
@@ -13,19 +13,27 @@ interface MenuRadioGroupProps {
13
13
  ariaLabel?: string,
14
14
  /** The value of the checked MenuRadio */
15
15
  value?: string | undefined,
16
- /** Called when the checked item has changed */
16
+ /** Raised when the checked item has changed */
17
17
  onchange?: (value: any) => void,
18
18
  }
19
19
 
20
20
  /**
21
21
  * A container for a logical group of menu RadioGroup items.
22
22
  */
23
- export default function MenuRadioGroup($props: MenuRadioGroupProps) {
24
- $props ??= $watch({});
25
-
23
+ export default function MenuRadioGroup($props: MenuRadioGroupProps | undefined) {
26
24
  // A collection containing the state and some functions for each item
27
25
  let itemStates: RadioGroupItemState[] = [];
28
26
 
27
+ let $state = $watch({
28
+ value: $props?.value
29
+ });
30
+
31
+ $run(() => {
32
+ if ($props !== undefined) {
33
+ $props.value = $state.value;
34
+ }
35
+ });
36
+
29
37
  // Set the context to pass down to items
30
38
  let context: MenuRadioGroupContext = {
31
39
  registerItemInGroup,
@@ -35,11 +43,11 @@ export default function MenuRadioGroup($props: MenuRadioGroupProps) {
35
43
 
36
44
  $mount(() => {
37
45
  // Set the initial checked item
38
- setValue($props.value);
46
+ setValue($props?.value);
39
47
  });
40
48
 
41
49
  function registerItemInGroup(itemValue: string, setChecked: (value: boolean) => void) {
42
- // Add the item"s state to the array
50
+ // Add the item's state to the array
43
51
  const newItemState: RadioGroupItemState = {
44
52
  value: itemValue,
45
53
  checked: false,
@@ -63,11 +71,11 @@ export default function MenuRadioGroup($props: MenuRadioGroupProps) {
63
71
  }
64
72
 
65
73
  // Set the value
66
- if ($props.value !== newValue) {
67
- $props.value = newValue;
74
+ if ($state.value !== newValue) {
75
+ $state.value = newValue;
68
76
 
69
- if ($props.onchange) {
70
- $props.onchange($props.value);
77
+ if ($props?.onchange) {
78
+ $props?.onchange($props?.value);
71
79
  }
72
80
  }
73
81
  }
@@ -81,15 +89,15 @@ export default function MenuRadioGroup($props: MenuRadioGroupProps) {
81
89
  newValue = item.value;
82
90
  }
83
91
  }
84
- $props.value = newValue;
92
+ $state.value = newValue;
85
93
  }
86
94
 
87
95
  @render {
88
96
  <div
89
- {$props.id}
90
- class={`torp-menu-radio-group ${$props.class ?? ""}`.trim()}
97
+ {$props?.id}
98
+ class={`torp-menu-radio-group ${$props?.class ?? ""}`.trim()}
91
99
  role="group"
92
- aria-label={$props.ariaLabel}
100
+ aria-label={$props?.ariaLabel}
93
101
  >
94
102
  <slot />
95
103
  </div>
@@ -9,13 +9,11 @@ interface MenuSeparatorProps {
9
9
  /**
10
10
  * An element in a menu that separates logical sets of menu items.
11
11
  */
12
- export default function MenuSeparator($props: MenuSeparatorProps) {
13
- $props ??= $watch({});
14
-
12
+ export default function MenuSeparator($props: MenuSeparatorProps | undefined) {
15
13
  @render {
16
14
  <div
17
- {$props.id}
18
- class={`torp-menu-separator ${$props.class ?? ""}`.trim()}
15
+ {$props?.id}
16
+ class={`torp-menu-separator ${$props?.class ?? ""}`.trim()}
19
17
  role="separator"
20
18
  >
21
19
  <slot />
@@ -7,7 +7,7 @@ export interface MenuContext {
7
7
  setVisible: (value: boolean, result?: any) => void;
8
8
 
9
9
  /** Called from a MenuButton to close the Menu and return a result */
10
- handleTrigger: (type: "confirm" | "cancel" | undefined, value?: any) => void;
10
+ handleButton: (type: "confirm" | "cancel" | undefined, value?: any) => void;
11
11
 
12
12
  /** Called from a MenuButton when its button is focused */
13
13
  handleItemFocus: (index: number) => void;
@@ -15,10 +15,7 @@ export interface MenuContext {
15
15
  /** Called from a MenuButton when a key is pressed */
16
16
  handleItemKey: (e: KeyboardEvent) => void;
17
17
 
18
- state: {
19
- /** Whether the Menu is visible. Writable so that it can be subscribed to by children */
20
- visible: boolean;
21
- };
18
+ state: MenuState;
22
19
 
23
20
  /** The element that caused this Menu to be shown */
24
21
  anchorElement?: HTMLElement;
@@ -27,6 +24,11 @@ export interface MenuContext {
27
24
  registerItem: (setFocused: () => void) => { index: number };
28
25
  }
29
26
 
27
+ export interface MenuState {
28
+ /** Whether the Menu is visible */
29
+ visible: boolean;
30
+ }
31
+
30
32
  export interface MenuButtonContext {
31
33
  state: {
32
34
  checked: boolean;
@@ -20,18 +20,17 @@ interface MenuBarProps {
20
20
  *
21
21
  * ```
22
22
  * <MenuBar>
23
- * <MenuBarItem>
24
- * <MenuBarItemTrigger />
25
- * <MenuBarItemContent>
26
- * <Menu />
27
- * </MenuBarItemContent>
28
- * </MenuBarItem>
23
+ * <MenuBarItem>
24
+ * <MenuBarItemTrigger />
25
+ * <MenuBarItemContent>
26
+ * <Menu />
27
+ * </MenuBarItemContent>
28
+ * </MenuBarItem>
29
29
  * </MenuBar>
30
30
  * ```
31
31
  */
32
- export default function MenuBar($props: MenuBarProps) {
33
- $props ??= $watch({});
34
- $props.orientation ??= "horizontal";
32
+ export default function MenuBar($props: MenuBarProps | undefined) {
33
+ let orientation = $props?.orientation ?? "horizontal";
35
34
 
36
35
  let $state = $watch({
37
36
  active: false,
@@ -52,7 +51,7 @@ export default function MenuBar($props: MenuBarProps) {
52
51
  $context[MenuBarContextName] = context;
53
52
 
54
53
  function registerItem(setVisible: (value: boolean) => void, setFocused: () => void) {
55
- // Add the item"s state to the array and return its index
54
+ // Add the item's state to the array and return its index
56
55
  const newItemState: ItemState = {
57
56
  setVisible,
58
57
  setFocused
@@ -146,12 +145,12 @@ export default function MenuBar($props: MenuBarProps) {
146
145
 
147
146
  @render {
148
147
  <div
149
- {$props.id}
150
- class={`torp-menu-bar ${$props.class ?? ""}`.trim()}
148
+ {$props?.id}
149
+ class={`torp-menu-bar ${$props?.class ?? ""}`.trim()}
151
150
  tabindex="-1"
152
151
  role="menubar"
153
- aria-label={$props.ariaLabel}
154
- aria-orientation={$props.orientation}
152
+ aria-label={$props?.ariaLabel}
153
+ aria-orientation={orientation}
155
154
  >
156
155
  <slot />
157
156
  </div>
@@ -13,12 +13,12 @@ interface MenuBarItemProps {
13
13
  class?: string,
14
14
  /** Whether the item is visible */
15
15
  visible?: boolean,
16
- /** Called when the item is opened or closed */
16
+ /** Raised when the item is opened or closed */
17
17
  ontoggle?: (visible: boolean) => void,
18
- /** Called when the item has opened */
18
+ /** Raised when the item has opened */
19
19
  onopen?: () => void,
20
- /** Called when the item has closed */
21
- onclose?: () => void,
20
+ /** Raised when the item has closed */
21
+ onclose?: (result: any) => void,
22
22
  }
23
23
 
24
24
  /**
@@ -28,17 +28,14 @@ interface MenuBarItemProps {
28
28
  *
29
29
  * ```
30
30
  * <MenuBarItem>
31
- * <MenuBarItemTrigger /> (optional)
32
- * <MenuBarItemContent />
31
+ * <MenuBarItemTrigger /> (optional)
32
+ * <MenuBarItemContent />
33
33
  * </MenuBarItem>
34
34
  * ```
35
35
  */
36
- export default function MenuBarItem($props: MenuBarItemProps) {
37
- $props ??= $watch({});
38
- $props.visible ??= false;
39
-
36
+ export default function MenuBarItem($props: MenuBarItemProps | undefined) {
40
37
  let $state = $watch({
41
- visible: $props.visible
38
+ visible: $props?.visible ?? false
42
39
  });
43
40
 
44
41
  let result: any;
@@ -83,9 +80,9 @@ export default function MenuBarItem($props: MenuBarItemProps) {
83
80
 
84
81
  let firstToggle = true;
85
82
  $run(() => {
86
- const ontoggle = $props.ontoggle;
87
- const onopen = $props.onopen;
88
- const onclose = $props.onclose;
83
+ const ontoggle = $props?.ontoggle;
84
+ const onopen = $props?.onopen;
85
+ const onclose = $props?.onclose;
89
86
  const visible = $state.visible;
90
87
  if (firstToggle) {
91
88
  firstToggle = false;
@@ -106,8 +103,8 @@ export default function MenuBarItem($props: MenuBarItemProps) {
106
103
 
107
104
  @render {
108
105
  <div
109
- {$props.id}
110
- class={`torp-menu-bar-item ${$props.class ?? ""}`.trim()}
106
+ {$props?.id}
107
+ class={`torp-menu-bar-item ${$props?.class ?? ""}`.trim()}
111
108
  >
112
109
  <slot />
113
110
  </div>
@@ -21,11 +21,7 @@ interface MenuBarItemContentProps {
21
21
  /**
22
22
  * The content that is displayed for a MenuBarItem when it is open.
23
23
  */
24
- export default function MenuBarItemContent($props: MenuBarItemContentProps) {
25
- $props ??= $watch({});
26
- $props.side ??= "bottom";
27
- $props.alignment ??= "start";
28
-
24
+ export default function MenuBarItemContent($props: MenuBarItemContentProps | undefined) {
29
25
  let div: HTMLDivElement;
30
26
  let closeOnClick = false;
31
27
  let shown = false;
@@ -89,7 +85,7 @@ export default function MenuBarItemContent($props: MenuBarItemContentProps) {
89
85
 
90
86
  function handleDocumentClick(e: MouseEvent) {
91
87
  if (closeOnClick && !div.contains(e.target as HTMLElement)) {
92
- context.active = false;
88
+ //context.active = false;
93
89
  setVisible(false);
94
90
  }
95
91
  }
@@ -99,14 +95,16 @@ export default function MenuBarItemContent($props: MenuBarItemContentProps) {
99
95
  throw new Error("MenuBarItemContent must have an anchor element");
100
96
  }
101
97
 
102
- setPopoverPosition(itemContext.anchorElement, div, $props.side, $props.alignment);
98
+ let side = $props?.side ?? "bottom";
99
+ let alignment = $props?.alignment ?? "start";
100
+ setPopoverPosition(itemContext.anchorElement, div, side, alignment);
103
101
  }
104
102
 
105
103
  @render {
106
104
  @if ($state.visible) {
107
105
  <div
108
- {$props.id}
109
- class={`torp-menu-bar-item-content ${$props.class ?? ""}`.trim()}
106
+ {$props?.id}
107
+ class={`torp-menu-bar-item-content ${$props?.class ?? ""}`.trim()}
110
108
  &ref={div}
111
109
  onmount={show}
112
110
  >
@@ -18,10 +18,7 @@ interface MenuBarItemTriggerProps {
18
18
  * A button that is placed in a MenuBar and opens a submenu.
19
19
  *
20
20
  */
21
- export default function MenuBarItemTrigger($props: MenuBarItemTriggerProps) {
22
- $props ??= $watch({});
23
- $props.disabled ??= false;
24
-
21
+ export default function MenuBarItemTrigger($props: MenuBarItemTriggerProps | undefined) {
25
22
  let button: HTMLButtonElement;
26
23
 
27
24
  // Get stuff out of the MenuBarContext
@@ -76,15 +73,15 @@ export default function MenuBarItemTrigger($props: MenuBarItemTriggerProps) {
76
73
 
77
74
  @render {
78
75
  <button
79
- {$props.id}
80
- class={`torp-menu-bar-item-button ${$props.class ?? ""}`.trim()}
76
+ {$props?.id}
77
+ class={`torp-menu-bar-item-button ${$props?.class ?? ""}`.trim()}
81
78
  type="button"
82
- {$props.disabled}
79
+ {$props?.disabled}
83
80
  role="menuitem"
84
81
  aria-haspopup="true"
85
82
  aria-expanded={($state.visible ?? false).toString()}
86
- aria-disabled={$props.disabled ? "true" : undefined}
87
- data-disabled={$props.disabled ? "true" : undefined}
83
+ aria-disabled={$props?.disabled ? "true" : undefined}
84
+ data-disabled={$props?.disabled ? "true" : undefined}
88
85
  onkeydown={handleKey}
89
86
  onclick={handleClick}
90
87
  onfocus={() => handleButtonFocus(index)}
@@ -0,0 +1,118 @@
1
+ import { ModalContextName, type ModalContext, type ModalState } from "./ModalTypes";
2
+
3
+ interface ModalProps {
4
+ /** An ID for the root element */
5
+ id?: string;
6
+ /** Class name(s) for the root element */
7
+ class?: string,
8
+ /** Whether the modal is visible */
9
+ visible?: boolean,
10
+ /** A function to be called when the user selects a response */
11
+ callback?: ((value: any) => void),
12
+ /** Raised when the modal is opened or closed */
13
+ ontoggle?: (open: boolean) => void,
14
+ /** Raised when the modal has opened */
15
+ onopen?: () => void,
16
+ /** Raised when the modal has closed */
17
+ onclose?: (result: any) => void,
18
+ }
19
+
20
+ /**
21
+ * A modal window that can be used to show rich content or obtain information from a user.
22
+ *
23
+ * See [the WAI ARIA guidelines for Modals](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/).
24
+ *
25
+ * The structure of a Modal is:
26
+ *
27
+ * ```
28
+ * <Modal>
29
+ * <ModalOverlay />
30
+ * <ModalTrigger /> (optional)
31
+ * <ModalContent />
32
+ * </Modal>
33
+ * ```
34
+ */
35
+ export default function Modal($props: ModalProps | undefined) {
36
+ let $state: ModalState = $watch({
37
+ visible: false,
38
+ })
39
+
40
+ // Sync props and state
41
+ $run(() => {
42
+ $state.visible = $props?.visible ?? false;
43
+ });
44
+ $run(() => {
45
+ if ($props !== undefined) {
46
+ $props.visible = $state.visible;
47
+ }
48
+ });
49
+
50
+ let result: any;
51
+
52
+ // Set the context to pass down to items
53
+ let context: ModalContext = {
54
+ handleButton,
55
+ handleClickOutside,
56
+ state: $state,
57
+ };
58
+ $context[ModalContextName] = context;
59
+
60
+ // Dispatch the toggle event when the modal is opened or closed
61
+ let firstToggle = true;
62
+ $run(() => {
63
+ const ontoggle = $props?.ontoggle;
64
+ const onopen = $props?.onopen;
65
+ const onclose = $props?.onclose;
66
+ const visible = $state.visible;
67
+ if (firstToggle) {
68
+ firstToggle = false;
69
+ } else {
70
+ if (ontoggle) ontoggle(visible);
71
+ if (visible) {
72
+ if (onopen) onopen()
73
+ } else {
74
+ if (onclose) onclose(result);
75
+ }
76
+ }
77
+ });
78
+
79
+ function handleButton(type: "confirm" | "cancel" | undefined, value?: any) {
80
+ // Get the button's result
81
+ switch (type) {
82
+ case "confirm": {
83
+ result = value || true;
84
+ break;
85
+ }
86
+ case "cancel": {
87
+ result = value || false;
88
+ break;
89
+ }
90
+ default: {
91
+ result = value;
92
+ break;
93
+ }
94
+ }
95
+
96
+ // Hide the modal
97
+ $state.visible = false;
98
+
99
+ // Call the callback
100
+ if ($props?.callback) {
101
+ $props?.callback(result);
102
+ }
103
+ }
104
+
105
+ function handleClickOutside(e: MouseEvent) {
106
+ e.preventDefault();
107
+ $state.visible = false;
108
+ }
109
+
110
+ @render {
111
+ <div
112
+ {$props?.id}
113
+ class={`torp-modal ${$props?.class ?? ""}`.trim()}
114
+ >
115
+ <slot />
116
+ </div>
117
+ }
118
+ }
@@ -0,0 +1,77 @@
1
+ import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
2
+ import { ModalContextName, type ModalContext } from "./ModalTypes";
3
+
4
+ interface ModalContentProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Class name(s) for the root element */
8
+ class?: string,
9
+ }
10
+
11
+ /**
12
+ * The content that is displayed for a Modal when it is open, displayed
13
+ * alongside an element.
14
+ */
15
+ export default function ModalContent($props: ModalContentProps | undefined) {
16
+ let div: HTMLDivElement;
17
+ let shown = false;
18
+ let closeOnClick = false;
19
+
20
+ // Get stuff out of the ModalContext
21
+ const context = $context[ModalContextName] as ModalContext;
22
+ if (!context) {
23
+ throw new Error("ModalContent must be contained within a Modal");
24
+ }
25
+ let $state = context.state;
26
+
27
+ $run(() => {
28
+ if (!$state.visible) {
29
+ hide();
30
+ }
31
+ });
32
+
33
+ function show() {
34
+ shown = true;
35
+
36
+ // HACK: Because this Modal could be shown with a click, we need to wait for another mousedown
37
+ // before we close on click. Otherwise the click immediately bubbles to the document
38
+ closeOnClick = false;
39
+ addDocumentEvent("mousedown", handleDocumentMouseDown);
40
+ addDocumentEvent("click", handleDocumentClick);
41
+ }
42
+
43
+ function hide() {
44
+ if (!shown) return;
45
+
46
+ removeDocumentEvent("mousedown", handleDocumentMouseDown);
47
+ removeDocumentEvent("click", handleDocumentClick);
48
+ }
49
+
50
+ function handleDocumentMouseDown() {
51
+ closeOnClick = true;
52
+ }
53
+
54
+ function handleDocumentClick(e: MouseEvent) {
55
+ if (
56
+ closeOnClick &&
57
+ div &&
58
+ !div.contains(e.target as HTMLElement)
59
+ ) {
60
+ e.preventDefault();
61
+ context.handleClickOutside!(e);
62
+ }
63
+ }
64
+
65
+ @render {
66
+ @if ($state.visible) {
67
+ <div
68
+ {$props?.id}
69
+ class={`torp-modal-content ${$props?.class ?? ""}`.trim()}
70
+ &ref={div}
71
+ onmount={show}
72
+ >
73
+ <slot />
74
+ </div>
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,38 @@
1
+ import { disableScroll } from "../mount/disableScroll";
2
+ import { ModalContextName, type ModalContext } from "./ModalTypes";
3
+
4
+ interface ModalOverlayProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Class name(s) for the root element */
8
+ class?: string,
9
+ }
10
+
11
+ /**
12
+ * The background overlay for a modal popup component (e.g. Context, Dialog or Popover).
13
+ * Adding an overlay automatically sets the component's `modal` value to true.
14
+ */
15
+ export default function ModalOverlay($props: ModalOverlayProps | undefined) {
16
+ // Get stuff out of the ModalContext
17
+ const context = $context[ModalContextName] as ModalContext;
18
+ if (!context) {
19
+ throw new Error("ModalOverlay must be contained within a Modal");
20
+ }
21
+ let $state = context.state;
22
+
23
+ // Any popup with an overlay is modal
24
+ $state.modal = true;
25
+
26
+ @render {
27
+ @if ($state.visible) {
28
+ <div
29
+ {$props?.id}
30
+ class={`torp-modal-overlay ${$props?.class ?? ""}`.trim()}
31
+ tabindex="-1"
32
+ onmount={(el) => disableScroll(el)}
33
+ >
34
+ <slot />
35
+ </div>
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,50 @@
1
+ import { ModalContextName, type ModalContext } from "./ModalTypes";
2
+ import hoverTrigger from "../mount/hoverTrigger";
3
+
4
+ interface ModalTriggerProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Class name(s) for the root element */
8
+ class?: string,
9
+ /** Whether the modal is activated by click/press or by hover **/
10
+ activation?: "press" | "hover"
11
+ }
12
+
13
+ /**
14
+ * The button that causes a Modal to appear or disappear.
15
+ */
16
+ export default function ModalTrigger($props: ModalTriggerProps | undefined) {
17
+ let activation = $props?.activation ?? "press";
18
+
19
+ // Get stuff out of the ModalContext
20
+ const context = $context[ModalContextName] as ModalContext;
21
+ if (!context) {
22
+ throw new Error("ModalTrigger must be contained within a Modal");
23
+ }
24
+ let $state = context.state;
25
+
26
+ function handleClick() {
27
+ if (activation !== "press") return;
28
+
29
+ $state.visible = !$state.visible;
30
+ }
31
+
32
+ function handleHover() {
33
+ if (activation !== "hover") return;
34
+
35
+ $state.visible = !$state.visible
36
+ }
37
+
38
+ @render {
39
+ <button
40
+ type="button"
41
+ {$props?.id}
42
+ class={`torp-modal-trigger ${$props?.class ?? ""}`.trim()}
43
+ onclick={handleClick}
44
+ onmount={(el) => hoverTrigger(el, { onHover: handleHover, onExit: handleHover })}
45
+ &ref={context.anchorElement}
46
+ >
47
+ <slot />
48
+ </button>
49
+ }
50
+ }
@@ -0,0 +1,22 @@
1
+ export const ModalContextName = "Modal";
2
+
3
+ /** The shared context for modal components (Contextual, Prompt and Modal) */
4
+ export interface ModalContext {
5
+ handleButton: (type: "confirm" | "cancel" | undefined, value?: any) => void;
6
+ // HACK: We need to be able to let children (i.e. Dialogs) override the click outside functionality
7
+ // There may be a more elegant way to accomplish this too
8
+ handleClickOutside?: (e: MouseEvent) => void;
9
+ state: ModalState;
10
+ // HACK: focusFirstElement is necessary because if we focus the first element e.g. in a Dialog's
11
+ // onMount, it will scroll the page to the focused element and mess up our careful positioning
12
+ // We need to be able to manually set the order to
13
+ // 1. set visible (so we have an element with content)
14
+ // 2. set position (so the element is in the right place)
15
+ // 3. set focus (for accessibility)
16
+ // There may be a more elegant way to accomplish this!
17
+ focusFirstElement?: () => void;
18
+ }
19
+
20
+ export interface ModalState {
21
+ visible: boolean;
22
+ }
@@ -0,0 +1,10 @@
1
+ import Modal from "./Modal.torp";
2
+ import ModalContent from "./ModalContent.torp";
3
+ import ModalOverlay from "./ModalOverlay.torp";
4
+ import ModalTrigger from "./ModalTrigger.torp";
5
+ import showModal from "./showModal";
6
+ import showPrompt from "./showPrompt";
7
+
8
+ export { Modal, ModalContent, ModalOverlay, ModalTrigger };
9
+
10
+ export { showModal, showPrompt };
@@ -0,0 +1,19 @@
1
+ // TODO: Allow passing in a variable -- so that you can show an inline modal
2
+ import type { Component } from "@torpor/view";
3
+
4
+ /**
5
+ * Shows a modal component in code.
6
+ * @param modal The modal component
7
+ * @param props The properties for the modal component
8
+ * @param target The target element to show the modal in
9
+ * @returns The value selected by the user
10
+ */
11
+ export default function showModal(
12
+ modal: Component,
13
+ props?: Record<PropertyKey, any>,
14
+ target?: Element,
15
+ ): void {
16
+ // Create the modal
17
+ // TODO: This needs to clean up after itself, like the mount methods do
18
+ modal(target || document.body, null, props);
19
+ }
@@ -0,0 +1,23 @@
1
+ import type { Component } from "@torpor/view";
2
+
3
+ /**
4
+ * Shows a modal component in code, and asynchronously returns the value selected by the user.
5
+ * @param modal The modal component
6
+ * @param props The properties for the modal component
7
+ * @param target The target element to show the modal in
8
+ * @returns The value selected by the user
9
+ */
10
+ export default function showPrompt(
11
+ modal: Component,
12
+ props?: Record<PropertyKey, any>,
13
+ target?: Element,
14
+ ): Promise<any> {
15
+ return new Promise((resolve) => {
16
+ // Add the promise as the callback
17
+ const allProps = Object.assign({}, props, { callback: resolve });
18
+
19
+ // Create the modal
20
+ // TODO: This needs to clean up after itself, like the use methods do
21
+ modal(target || document.body, null, allProps);
22
+ });
23
+ }