@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
@@ -0,0 +1,125 @@
1
+ import { ContextualContextName, type Point, type ContextualContext, type ContextualState } from "./ContextualTypes";
2
+
3
+ interface ContextualProps {
4
+ /** An ID for the root element */
5
+ id?: string;
6
+ /** Class name(s) for the root element */
7
+ class?: string,
8
+ /** Whether the contextual is modal. If true, an overlay should be supplied to obscure outside content, and only the context content will be visible to screen readers */
9
+ modal?: boolean,
10
+ /** Whether the contextual is visible */
11
+ visible?: boolean,
12
+ /** The element to anchor this contextual to, as an alternative to supplying a ContextualTrigger */
13
+ anchor?: HTMLElement,
14
+ /** The position to display this contextual at */
15
+ position?: Point,
16
+ /** A function to be called when the user selects a response */
17
+ callback?: ((value: any) => void),
18
+ /** Raised when this contextual is opened or closed */
19
+ ontoggle?: (open: boolean) => void,
20
+ /** Raised when the contextual has opened */
21
+ onopen?: () => void,
22
+ /** Raised when the contextual has closed */
23
+ onclose?: (result: any) => void,
24
+ }
25
+
26
+ /**
27
+ * A contextual window that is shown to the user at the cursor's location.
28
+ *
29
+ * The structure of a Contextual is:
30
+ *
31
+ * ```
32
+ * <Contextual>
33
+ * <ContextualTrigger /> (optional)
34
+ * <ContextualContent />
35
+ * </Contextual>
36
+ * ```
37
+ */
38
+ export default function Contextual($props: ContextualProps | undefined) {
39
+ let $state: ContextualState = $watch({
40
+ visible: false,
41
+ position: { x: 0, y: 0 }
42
+ })
43
+
44
+ // Sync props and state
45
+ $run(() => {
46
+ $state.visible = $props?.visible ?? false;
47
+ $state.position = $props?.position ?? { x: 0, y: 0 };
48
+ });
49
+ $run(() => {
50
+ if ($props !== undefined) {
51
+ $props.visible = $state.visible;
52
+ }
53
+ });
54
+
55
+ let result: any;
56
+
57
+ // Set the context to pass down to items
58
+ let context: ContextualContext = {
59
+ handleButton,
60
+ handleClickOutside,
61
+ state: $state,
62
+ modal: $props?.modal,
63
+ anchorElement: $props?.anchor ? $unwrap($props.anchor) : undefined,
64
+ };
65
+ $context[ContextualContextName] = context;
66
+
67
+ // Dispatch the toggle event when the contextual is opened or closed
68
+ let firstToggle = true;
69
+ $run(() => {
70
+ const ontoggle = $props?.ontoggle;
71
+ const onopen = $props?.onopen;
72
+ const onclose = $props?.onclose;
73
+ const visible = $state.visible;
74
+ if (firstToggle) {
75
+ firstToggle = false;
76
+ } else {
77
+ if (ontoggle) ontoggle(visible);
78
+ if (visible) {
79
+ if (onopen) onopen()
80
+ } else {
81
+ if (onclose) onclose(result);
82
+ }
83
+ }
84
+ });
85
+
86
+ function handleButton(type: "confirm" | "cancel" | undefined, value?: any) {
87
+ // Get the button's result
88
+ switch (type) {
89
+ case "confirm": {
90
+ result = value || true;
91
+ break;
92
+ }
93
+ case "cancel": {
94
+ result = value || false;
95
+ break;
96
+ }
97
+ default: {
98
+ result = value;
99
+ break;
100
+ }
101
+ }
102
+
103
+ // Hide the contextual
104
+ $state.visible = false;
105
+
106
+ // Call the callback
107
+ if ($props?.callback) {
108
+ $props.callback(result);
109
+ }
110
+ }
111
+
112
+ function handleClickOutside(e: MouseEvent) {
113
+ e.preventDefault();
114
+ $state.visible = false;
115
+ }
116
+
117
+ @render {
118
+ <div
119
+ {$props?.id}
120
+ class={`torp-context ${$props?.class ?? ""}`.trim()}
121
+ >
122
+ <slot />
123
+ </div>
124
+ }
125
+ }
@@ -2,9 +2,9 @@
2
2
  import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
3
3
  import getScrollParent from "../utils/getScrollParent";
4
4
  import setContextPosition from "../utils/setContextPosition";
5
- import { PopoverContextName, type PopoverContext } from "./PopoverTypes";
5
+ import { ContextualContextName, type ContextualContext } from "./ContextualTypes";
6
6
 
7
- interface PopoverContentContextualProps {
7
+ interface ContextualContentProps {
8
8
  /** An ID for the root element */
9
9
  id?: string;
10
10
  /** Class name(s) for the root element */
@@ -12,20 +12,18 @@ interface PopoverContentContextualProps {
12
12
  }
13
13
 
14
14
  /**
15
- * The content that is displayed for a Popover when it is open, displayed at the
15
+ * The content that is displayed for a Contextual when it is open, displayed at the
16
16
  * position that the user right-clicked.
17
17
  */
18
- export default function PopoverContentContextual($props: PopoverContentContextualProps) {
19
- $props ??= $watch({});
20
-
18
+ export default function ContextualContent($props: ContextualContentProps | undefined) {
21
19
  let div: HTMLDivElement;
22
20
  let shown = false;
23
21
  let closeOnClick = false;
24
22
 
25
- // Get stuff out of the PopoverContext
26
- const context = $context[PopoverContextName] as PopoverContext;
23
+ // Get stuff out of the ContextualContext
24
+ const context = $context[ContextualContextName] as ContextualContext;
27
25
  if (!context) {
28
- throw new Error("PopoverContentContextual must be contained within a Popover");
26
+ throw new Error("ContextualContent must be contained within a Contextual");
29
27
  }
30
28
  let $state = context.state;
31
29
 
@@ -46,7 +44,8 @@ export default function PopoverContentContextual($props: PopoverContentContextua
46
44
 
47
45
  // Set the position and listen for window resize and scroll to reset the position
48
46
  setPosition();
49
- const scrollParent = getScrollParent(context.anchorElement)
47
+ const anchorElement = context.anchorElement!;
48
+ const scrollParent = getScrollParent(anchorElement)
50
49
  scrollParent.addEventListener("resize", setPosition);
51
50
  scrollParent.addEventListener("scroll", setPosition);
52
51
  if (context.focusFirstElement) {
@@ -61,11 +60,12 @@ export default function PopoverContentContextual($props: PopoverContentContextua
61
60
  removeDocumentEvent("click", handleDocumentClick);
62
61
 
63
62
  // Stop listening for window resize and focus the anchor element per WAI guidelines
64
- const scrollParent = getScrollParent(context.anchorElement)
63
+ const anchorElement = context.anchorElement!;
64
+ const scrollParent = getScrollParent(anchorElement)
65
65
  scrollParent.removeEventListener("resize", setPosition);
66
66
  scrollParent.removeEventListener("scroll", setPosition);
67
- if (context.anchorElement?.focus) {
68
- context.anchorElement.focus();
67
+ if (anchorElement.focus) {
68
+ anchorElement.focus();
69
69
  }
70
70
  }
71
71
 
@@ -97,8 +97,8 @@ export default function PopoverContentContextual($props: PopoverContentContextua
97
97
  @render {
98
98
  @if ($state.visible) {
99
99
  <div
100
- {$props.id}
101
- class={`torp-popover-content-contextual ${$props.class ?? ""}`.trim()}
100
+ {$props?.id}
101
+ class={`torp-popover-content-contextual ${$props?.class ?? ""}`.trim()}
102
102
  &ref={div}
103
103
  onmount={show}
104
104
  >
@@ -0,0 +1,66 @@
1
+ import { ContextualContextName, type ContextualContext } from "./ContextualTypes";
2
+ import hoverTrigger from "../mount/hoverTrigger";
3
+
4
+ interface ContextualTriggerProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Class name(s) for the root element */
8
+ class?: string,
9
+ /** Whether the popover is activated by click/press or by hover **/
10
+ activation?: "press" | "hover"
11
+ }
12
+
13
+ /**
14
+ * The element that causes a Contextual to appear or disappear when right-clicked.
15
+ */
16
+ export default function ContextualTrigger($props: ContextualTriggerProps | undefined) {
17
+ let activation = $props?.activation ?? "press";
18
+
19
+ // Get stuff out of the ContextualContext
20
+ const context = $context[ContextualContextName] as ContextualContext;
21
+ if (!context) {
22
+ throw new Error("ContextualTrigger must be contained within a Contextual");
23
+ }
24
+ let $state = context.state;
25
+
26
+ function handleContextualMenu(e: MouseEvent & { currentTarget: EventTarget & HTMLDivElement }) {
27
+ if (activation !== "press") return;
28
+
29
+ e.preventDefault();
30
+ if ($state.position) {
31
+ // Get the position relative to the element, so we can reposition on scroll
32
+ const rect = e.currentTarget.getBoundingClientRect();
33
+ const x = e.clientX - rect.left;
34
+ const y = e.clientY - rect.top;
35
+ $state.position = { x, y };
36
+ //$state.position = { x: window.scrollX + e.clientX, y: window.scrollY + e.clientY };
37
+ }
38
+ $state.visible = !$state.visible;
39
+ }
40
+
41
+ function handleHover(node: HTMLElement, e: MouseEvent) {
42
+ if (activation !== "hover") return;
43
+
44
+ if ($state.position) {
45
+ // Get the position relative to the element, so we can reposition on scroll
46
+ const rect = node.getBoundingClientRect();
47
+ const x = e.clientX - rect.left;
48
+ const y = e.clientY - rect.top;
49
+ $state.position = { x, y };
50
+ //$state.position = { x: window.scrollX + e.clientX, y: window.scrollY + e.clientY };
51
+ }
52
+ $state.visible = !$state.visible
53
+ }
54
+
55
+ @render {
56
+ <div
57
+ {$props?.id}
58
+ class={`torp-popover-trigger-contextual ${$props?.class ?? ""}`.trim()}
59
+ oncontextmenu={handleContextualMenu}
60
+ onmount={(el) => hoverTrigger(el, { onHover: handleHover, onExit: handleHover })}
61
+ &ref={context.anchorElement}
62
+ >
63
+ <slot />
64
+ </div>
65
+ }
66
+ }
@@ -0,0 +1,30 @@
1
+ export const ContextualContextName = "Contextual";
2
+
3
+ /** The shared context for popover components (Contextual, Prompt and Contextual) */
4
+ export interface ContextualContext {
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: ContextualState;
10
+ //modal: boolean;
11
+ anchorElement?: HTMLElement;
12
+ // HACK: focusFirstElement is necessary because if we focus the first element e.g. in a Dialog's
13
+ // onMount, it will scroll the page to the focused element and mess up our careful positioning
14
+ // We need to be able to manually set the order to
15
+ // 1. set visible (so we have an element with content)
16
+ // 2. set position (so the element is in the right place)
17
+ // 3. set focus (for accessibility)
18
+ // There may be a more elegant way to accomplish this!
19
+ focusFirstElement?: () => void;
20
+ }
21
+
22
+ export interface ContextualState {
23
+ visible: boolean;
24
+ position: Point;
25
+ }
26
+
27
+ export interface Point {
28
+ x: number;
29
+ y: number;
30
+ }
@@ -0,0 +1,90 @@
1
+ import { type SlotRender } from "@torpor/view";
2
+
3
+
4
+ interface ContextualProps {
5
+ /** An ID for the root element */
6
+ id?: string;
7
+ /** Class name(s) for the root element */
8
+ class?: string,
9
+ /** Whether the contextual is modal. If true, an overlay should be supplied to obscure outside content, and only the context content will be visible to screen readers */
10
+ modal?: boolean,
11
+ /** Whether the contextual is visible */
12
+ visible?: boolean,
13
+ /** The element to anchor this contextual to, as an alternative to supplying a ContextualTrigger */
14
+ anchor?: HTMLElement,
15
+ /** The position to display this contextual at */
16
+ position?: Point,
17
+ /** A function to be called when the user selects a response */
18
+ callback?: ((value: any) => void),
19
+ /** Raised when this contextual is opened or closed */
20
+ ontoggle?: (open: boolean) => void,
21
+ /** Raised when the contextual has opened */
22
+ onopen?: () => void,
23
+ /** Raised when the contextual has closed */
24
+ onclose?: (result: any) => void,
25
+ }
26
+
27
+ /**
28
+ * A contextual window that is shown to the user at the cursor's location.
29
+ *
30
+ * The structure of a Contextual is:
31
+ *
32
+ * ```
33
+ * <Contextual>
34
+ * <ContextualTrigger /> (optional)
35
+ * <ContextualContent />
36
+ * </Contextual>
37
+ * ```
38
+ */
39
+ declare function Contextual(
40
+ $parent: ParentNode,
41
+ $anchor: Node | null,
42
+ $props: ContextualProps | undefined,
43
+ $context?: Record<PropertyKey, any>,
44
+ $slots?: Record<string, SlotRender>,
45
+ ): void;
46
+
47
+
48
+
49
+ interface ContextualContentProps {
50
+ /** An ID for the root element */
51
+ id?: string;
52
+ /** Class name(s) for the root element */
53
+ class?: string,
54
+ }
55
+
56
+ /**
57
+ * The content that is displayed for a Contextual when it is open, displayed at the
58
+ * position that the user right-clicked.
59
+ */
60
+ declare function ContextualContent(
61
+ $parent: ParentNode,
62
+ $anchor: Node | null,
63
+ $props: ContextualContentProps | undefined,
64
+ $context?: Record<PropertyKey, any>,
65
+ $slots?: Record<string, SlotRender>,
66
+ ): void;
67
+
68
+
69
+
70
+ interface ContextualTriggerProps {
71
+ /** An ID for the root element */
72
+ id?: string;
73
+ /** Class name(s) for the root element */
74
+ class?: string,
75
+ /** Whether the popover is activated by click/press or by hover **/
76
+ activation?: "press" | "hover"
77
+ }
78
+
79
+ /**
80
+ * The element that causes a Contextual to appear or disappear when right-clicked.
81
+ */
82
+ declare function ContextualTrigger(
83
+ $parent: ParentNode,
84
+ $anchor: Node | null,
85
+ $props: ContextualTriggerProps | undefined,
86
+ $context?: Record<PropertyKey, any>,
87
+ $slots?: Record<string, SlotRender>,
88
+ ): void;
89
+
90
+ export { Contextual, ContextualContent, ContextualTrigger };
@@ -0,0 +1,14 @@
1
+ // src/Contextual/Contextual.torp
2
+ import Contextual_default from "./Contextual.torp";
3
+
4
+ // src/Contextual/ContextualContent.torp
5
+ import ContextualContent_default from "./ContextualContent.torp";
6
+
7
+ // src/Contextual/ContextualTrigger.torp
8
+ import ContextualTrigger_default from "./ContextualTrigger.torp";
9
+ export {
10
+ Contextual_default as Contextual,
11
+ ContextualContent_default as ContextualContent,
12
+ ContextualTrigger_default as ContextualTrigger
13
+ };
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,8 +1,8 @@
1
1
  import getId from "../utils/getId";
2
- import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
3
2
  import { trapFocus, type TrapFocusOptions } from "../mount/trapFocus";
4
- import { PopoverContextName, type PopoverContext } from "../Popover/PopoverTypes";
5
- import { DialogContextName, type DialogContext, type DialogState } from "./DialogTypes";
3
+ import { ModalContextName, type ModalContext } from "../Modal/ModalTypes";
4
+ import { PopoverContextName } from "../Popover/PopoverTypes";
5
+ import { DialogContextName, type DialogContext, type DialogState, type HandleButtonPress } from "./DialogTypes";
6
6
 
7
7
  interface DialogProps {
8
8
  /** An ID for the root element */
@@ -16,7 +16,9 @@ interface DialogProps {
16
16
  }
17
17
 
18
18
  /**
19
- * Content for a window displayed in a Popover.
19
+ * A dialog that can be used to show a message to the user, or get information from the user.
20
+ *
21
+ * Dialogs are typically shown within a Modal or Popover.
20
22
  *
21
23
  * See [the WAI ARIA guidelines for Dialogs](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/).
22
24
  *
@@ -24,42 +26,44 @@ interface DialogProps {
24
26
  *
25
27
  * ```
26
28
  * <Dialog>
27
- * <DialogHeader /> (optional)
28
- * <DialogBody /> (optional)
29
- * <DialogFooter> (optional)
30
- * <DialogButton /> (optional)
31
- * </DialogFooter>
29
+ * <ModalOverlay /> (optional -- will set `modal` to true)
30
+ * <DialogHeader /> (optional)
31
+ * <DialogBody /> (optional)
32
+ * <DialogFooter> (optional)
33
+ * <DialogButton /> (optional)
34
+ * </DialogFooter>
32
35
  * </Dialog>
33
36
  * ```
34
37
  *
35
38
  */
36
- export default function Dialog($props: DialogProps) {
37
- $props ??= $watch({});
38
- $props.id ??= getId();
39
- $props.dialog ??= false;
39
+ export default function Dialog($props: DialogProps | undefined) {
40
+ let defaultId = getId();
40
41
 
41
- let div: HTMLDivElement;
42
+ //let div: HTMLDivElement;
42
43
 
43
44
  let trapFocusOptions: TrapFocusOptions = {
44
45
  focusableElements: []
45
46
  };
46
47
 
47
- // Get stuff out of the PopoverContext
48
- const popoverContext = $context[PopoverContextName] as PopoverContext;
49
- if (!popoverContext) {
50
- throw new Error("Dialog must be contained within a Popover");
48
+ // Maybe get stuff out of a Modal context
49
+ let modal = false;
50
+ let visible = true;
51
+ let handleButton: HandleButtonPress | undefined = undefined;
52
+ const modalContext = $context[ModalContextName] ?? $context[PopoverContextName] as ModalContext;
53
+ if (modalContext) {
54
+ modal = true;
55
+ visible = modalContext.state.visible;
56
+ handleButton = modalContext.handleButton;
57
+
58
+ modalContext.handleClickOutside = handleClickOutside;
59
+ // Set the focusFirstElement method in the ModalContext so that it can be
60
+ // called after everything has been shown
61
+ modalContext.focusFirstElement = focusFirstElement;
51
62
  }
52
- const { handleButton, modal } = popoverContext;
53
- const visible = popoverContext.state.visible;
54
-
55
- popoverContext.handleClickOutside = handleClickOutside;
56
- // Set the focusFirstElement method in the PopoverContext so that it can be
57
- // called after everything has been shown
58
- popoverContext.focusFirstElement = focusFirstElement;
59
63
 
60
64
  // Set the IDs for using in the Dialog's ARIA attributes
61
65
  let $state: DialogState = $watch({
62
- dialogId: $props.id,
66
+ dialogId: $props?.id ?? defaultId,
63
67
  headerId: "",
64
68
  bodyId: "",
65
69
  visible
@@ -69,60 +73,22 @@ export default function Dialog($props: DialogProps) {
69
73
  let context: DialogContext = {
70
74
  handleButton,
71
75
  state: $state,
72
- anchorElement: popoverContext.anchorElement
76
+ anchorElement: modalContext ? modalContext.anchorElement : undefined
73
77
  };
74
78
  $context[DialogContextName] = context;
75
79
 
76
- let closeOnClick = false;
77
-
78
- $mount(() => {
79
- if ($state.visible) {
80
- // HACK: Because this Dialog could be shown with a click, we need to
81
- // wait for another mousedown before we close on click. Otherwise
82
- // the click immediately bubbles to the document. I guess we could
83
- // require the user to preventDefault after calling showPrompt etc,
84
- // but that seems very error-prone?
85
- closeOnClick = false;
86
- addDocumentEvent("mousedown", handleDocumentMouseDown);
87
- addDocumentEvent("click", handleDocumentClick);
88
- } else {
89
- removeDocumentEvent("mousedown", handleDocumentMouseDown);
90
- removeDocumentEvent("click", handleDocumentClick);
91
- // Focus the anchor element per WAI guidelines
92
- if (context.anchorElement?.focus) {
93
- context.anchorElement.focus();
94
- }
95
- }
96
- });
97
-
98
80
  function focusFirstElement() {
99
81
  if (trapFocusOptions.focusableElements.length) {
100
82
  trapFocusOptions.focusableElements[0].focus();
101
83
  }
102
84
  }
103
85
 
104
- function handleDocumentMouseDown() {
105
- closeOnClick = true;
106
- }
107
-
108
- function handleDocumentClick(e: MouseEvent) {
109
- if (
110
- closeOnClick &&
111
- div &&
112
- !div.contains(e.target as HTMLElement) &&
113
- (!context.anchorElement || !context.anchorElement.contains(e.target as HTMLElement))
114
- ) {
115
- handleClickOutside(e);
116
- }
117
- }
118
-
119
86
  function handleClickOutside(e: MouseEvent) {
120
87
  // Don't allow the user to click outside -- they must press a button or
121
88
  // the Esc key
122
89
  e.preventDefault();
123
90
  if (!modal) {
124
- popoverContext.state.visible = false;
125
- context.state.visible = false;
91
+ modalContext.state.visible = false;
126
92
  }
127
93
  }
128
94
 
@@ -148,17 +114,17 @@ export default function Dialog($props: DialogProps) {
148
114
 
149
115
  @render {
150
116
  <div
151
- {$props.id}
152
- class={`torp-dialog ${$props.class ?? ""}`.trim()}
117
+ id={$props?.id ?? defaultId}
118
+ class={`torp-dialog ${$props?.class ?? ""}`.trim()}
153
119
  tabindex="0"
154
- role={$props.alert ? "alertdialog" : "dialog"}
120
+ role={$props?.alert === true ? "alertdialog" : "dialog"}
155
121
  aria-modal={modal}
156
- aria-label={$props.ariaLabel}
157
- aria-labelledby={!$props.ariaLabel && $state.headerId ? $state.headerId : undefined}
122
+ aria-label={$props?.ariaLabel}
123
+ aria-labelledby={!$props?.ariaLabel && $state.headerId ? $state.headerId : undefined}
158
124
  aria-describedby={$state.bodyId ? $state.bodyId : undefined}
159
125
  onkeydown={handleKey}
160
126
  onmount={(el) => trapFocus(el, trapFocusOptions)}
161
- &ref={div}
127
+ @//&ref={div}
162
128
  >
163
129
  <slot />
164
130
  </div>
@@ -14,10 +14,8 @@ interface DialogBodyProps {
14
14
  * The body information for a Dialog. Content in the body will be announced by
15
15
  * screen readers when the Dialog is opened, unless ariaAnnounce is false.
16
16
  */
17
- export default function DialogBody($props: DialogBodyProps) {
18
- $props ??= $watch({});
19
- $props.id ??= getId();
20
- $props.ariaAnnounce ??= true;
17
+ export default function DialogBody($props: DialogBodyProps | undefined) {
18
+ let defaultId = getId();
21
19
 
22
20
  // Get stuff out of the DialogContext
23
21
  const context = $context[DialogContextName] as DialogContext;
@@ -27,14 +25,14 @@ export default function DialogBody($props: DialogBodyProps) {
27
25
  let $state = context.state;
28
26
 
29
27
  // Set the body id for using in the Dialog's ARIA attributes
30
- if ($props.ariaAnnounce) {
31
- $state.bodyId = $props.id;
28
+ if ($props?.ariaAnnounce !== false) {
29
+ $state.bodyId = $props?.id || defaultId;
32
30
  }
33
31
 
34
32
  @render {
35
33
  <div
36
- {$props.id}
37
- class={`torp-dialog-body ${$props.class ?? ""}`.trim()}
34
+ id={$props?.id ?? defaultId}
35
+ class={`torp-dialog-body ${$props?.class ?? ""}`.trim()}
38
36
  >
39
37
  <slot />
40
38
  </div>
@@ -11,15 +11,15 @@ interface DialogButtonProps {
11
11
  type?: "confirm" | "cancel",
12
12
  /** Whether this button is disabled */
13
13
  disabled?: boolean,
14
+ /** Raised when the button is clicked */
15
+ onclick?: (e: MouseEvent) => void,
14
16
  }
15
17
 
16
18
  /**
17
19
  * A button that is placed in a DialogFooter and signals a response by a user.
18
20
  *
19
21
  */
20
- export default function DialogButton($props: DialogButtonProps) {
21
- $props ??= $watch({});
22
-
22
+ export default function DialogButton($props: DialogButtonProps | undefined) {
23
23
  let button: HTMLButtonElement;
24
24
 
25
25
  // Get stuff out of the DialogContext
@@ -31,26 +31,28 @@ export default function DialogButton($props: DialogButtonProps) {
31
31
 
32
32
  $mount(() => {
33
33
  // Store the confirm or cancel button in the DialogContext
34
- if ($props.type === "confirm") {
34
+ if ($props?.type === "confirm") {
35
35
  context.confirmButton = button;
36
- } else if ($props.type === "cancel") {
36
+ } else if ($props?.type === "cancel") {
37
37
  context.cancelButton = button;
38
38
  }
39
39
  });
40
40
 
41
- function handleClick(e) {
42
- if ($props.onclick) {
43
- $props.onclick(e)
41
+ function handleClick(e: MouseEvent) {
42
+ if ($props?.onclick) {
43
+ $props?.onclick(e)
44
+ }
45
+ if (handleButton) {
46
+ handleButton($props?.type, $props?.value);
44
47
  }
45
- handleButton($props.type, $props.value);
46
48
  }
47
49
 
48
50
  @render {
49
51
  <button
50
52
  type="button"
51
- {$props.id}
52
- class={`torp-dialog-button ${$props.class ?? ""}`.trim()}
53
- disabled={$props.disabled}
53
+ {$props?.id}
54
+ class={`torp-dialog-button ${$props?.class ?? ""}`.trim()}
55
+ disabled={$props?.disabled}
54
56
  onclick={handleClick}
55
57
  &ref={button}
56
58
  >