@torpor/ui 0.2.0 → 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.
- package/CHANGELOG.md +35 -0
- package/dist/Accordion/Accordion.torp +44 -25
- package/dist/Accordion/AccordionContent.torp +18 -12
- package/dist/Accordion/AccordionHeader.torp +15 -8
- package/dist/Accordion/AccordionItem.torp +34 -17
- package/dist/Accordion/AccordionTrigger.torp +17 -10
- package/dist/Accordion/AccordionTypes.ts +6 -3
- package/dist/Accordion/index.d.ts +100 -35
- package/dist/Breadcrumb/Breadcrumb.torp +18 -10
- package/dist/Breadcrumb/BreadcrumbItem.torp +14 -7
- package/dist/Breadcrumb/BreadcrumbSeparator.torp +10 -3
- package/dist/Breadcrumb/index.d.ts +50 -21
- package/dist/Calendar/Calendar.torp +21 -14
- package/dist/Calendar/CalendarDay.torp +12 -4
- package/dist/Calendar/index.d.ts +16 -18
- package/dist/Contextual/Contextual.torp +125 -0
- package/dist/{Popover/PopoverContentContextual.torp → Contextual/ContextualContent.torp} +21 -12
- package/dist/Contextual/ContextualTrigger.torp +66 -0
- package/dist/Contextual/ContextualTypes.ts +30 -0
- package/dist/Contextual/index.d.ts +90 -0
- package/dist/Contextual/index.js +14 -0
- package/dist/Contextual/index.js.map +1 -0
- package/dist/Dialog/Dialog.torp +49 -73
- package/dist/Dialog/DialogBody.torp +15 -9
- package/dist/Dialog/DialogButton.torp +27 -13
- package/dist/Dialog/DialogFooter.torp +11 -3
- package/dist/Dialog/DialogHeader.torp +15 -8
- package/dist/Dialog/DialogTypes.ts +3 -2
- package/dist/Dialog/index.d.ts +83 -35
- package/dist/Disclosure/Disclosure.torp +34 -25
- package/dist/Disclosure/DisclosureContent.torp +18 -12
- package/dist/Disclosure/DisclosureHeader.torp +16 -9
- package/dist/Disclosure/DisclosureTrigger.torp +15 -8
- package/dist/Disclosure/DisclosureTypes.ts +0 -1
- package/dist/Disclosure/index.d.ts +67 -28
- package/dist/Drawer/Drawer.torp +109 -68
- package/dist/Drawer/DrawerContent.torp +80 -0
- package/dist/Drawer/DrawerOverlay.torp +38 -0
- package/dist/Drawer/DrawerTrigger.torp +35 -0
- package/dist/Drawer/DrawerTypes.ts +24 -0
- package/dist/Drawer/index.d.ts +96 -8
- package/dist/Drawer/index.js +13 -1
- package/dist/Form/Field.torp +91 -0
- package/dist/Form/Form.torp +129 -0
- package/dist/Form/FormTypes.ts +30 -0
- package/dist/Form/Input.torp +97 -0
- package/dist/Form/Label.torp +31 -0
- package/dist/Form/Message.torp +35 -0
- package/dist/Form/StandardSchemaV1.ts +70 -0
- package/dist/Form/index.d.ts +131 -0
- package/dist/Form/index.js +22 -0
- package/dist/Form/index.js.map +1 -0
- package/dist/Menu/Menu.torp +25 -15
- package/dist/Menu/MenuButton.torp +30 -13
- package/dist/Menu/MenuCheck.torp +29 -14
- package/dist/Menu/MenuGroup.torp +14 -6
- package/dist/Menu/MenuIndicator.torp +14 -7
- package/dist/Menu/MenuPopout.torp +23 -15
- package/dist/Menu/MenuPopoutContent.torp +17 -10
- package/dist/Menu/MenuPopoutTrigger.torp +23 -16
- package/dist/Menu/MenuRadio.torp +27 -16
- package/dist/Menu/MenuRadioGroup.torp +34 -15
- package/dist/Menu/MenuSeparator.torp +11 -3
- package/dist/Menu/MenuTypes.ts +7 -5
- package/dist/Menu/index.d.ts +176 -77
- package/dist/MenuBar/MenuBar.torp +24 -16
- package/dist/MenuBar/MenuBarItem.torp +24 -16
- package/dist/MenuBar/MenuBarItemContent.torp +18 -11
- package/dist/MenuBar/MenuBarItemTrigger.torp +15 -9
- package/dist/MenuBar/index.d.ts +79 -28
- package/dist/Modal/Modal.torp +118 -0
- package/dist/Modal/ModalContent.torp +77 -0
- package/dist/Modal/ModalOverlay.torp +38 -0
- package/dist/Modal/ModalTrigger.torp +50 -0
- package/dist/Modal/ModalTypes.ts +22 -0
- package/dist/Modal/index.d.ts +108 -0
- package/dist/Modal/index.js +33 -0
- package/dist/Modal/index.js.map +1 -0
- package/dist/Modal/showModal.ts +19 -0
- package/dist/Modal/showPrompt.ts +23 -0
- package/dist/NavMenu/NavMenu.torp +18 -10
- package/dist/NavMenu/NavMenuButton.torp +15 -10
- package/dist/NavMenu/NavMenuElement.torp +19 -11
- package/dist/NavMenu/NavMenuGroup.torp +13 -6
- package/dist/NavMenu/NavMenuGroupHeader.torp +15 -8
- package/dist/NavMenu/NavMenuLink.torp +21 -16
- package/dist/NavMenu/NavMenuPopout.torp +34 -18
- package/dist/NavMenu/NavMenuPopoutButton.torp +23 -18
- package/dist/NavMenu/NavMenuPopoutContent.torp +23 -13
- package/dist/NavMenu/NavMenuSeparator.torp +10 -3
- package/dist/NavMenu/index.d.ts +153 -70
- package/dist/Notification/Notification.torp +35 -16
- package/dist/Notification/NotificationClose.torp +10 -3
- package/dist/Notification/NotificationContainer.torp +11 -3
- package/dist/Notification/NotificationContent.torp +11 -3
- package/dist/Notification/index.d.ts +46 -28
- package/dist/Popover/Popover.torp +48 -28
- package/dist/Popover/PopoverContent.torp +42 -8
- package/dist/Popover/PopoverTrigger.torp +32 -3
- package/dist/Popover/PopoverTypes.ts +1 -1
- package/dist/Popover/index.d.ts +48 -111
- package/dist/Popover/index.js +1 -163
- package/dist/Popover/index.js.map +1 -1
- package/dist/TabGroup/TabContent.torp +20 -11
- package/dist/TabGroup/TabGroup.torp +58 -40
- package/dist/TabGroup/TabGroupTypes.ts +10 -5
- package/dist/TabGroup/TabHeader.torp +26 -10
- package/dist/TabGroup/TabList.torp +14 -7
- package/dist/TabGroup/TabTrigger.torp +17 -10
- package/dist/TabGroup/index.d.ts +101 -35
- package/dist/icons/ArrowDown.torp +5 -3
- package/dist/icons/ArrowLeft.torp +5 -3
- package/dist/icons/ArrowRight.torp +5 -3
- package/dist/icons/ArrowUp.torp +5 -3
- package/dist/icons/Bars.torp +5 -3
- package/dist/icons/Check.torp +5 -3
- package/dist/icons/ChevronDown.torp +5 -3
- package/dist/icons/ChevronLeft.torp +5 -3
- package/dist/icons/ChevronRight.torp +5 -3
- package/dist/icons/ChevronUp.torp +5 -3
- package/dist/icons/Cross.torp +5 -3
- package/dist/icons/IconTypes.ts +6 -0
- package/dist/icons/Loading.torp +5 -3
- package/dist/icons/Minus.torp +5 -3
- package/dist/icons/Plus.torp +5 -3
- package/dist/icons/index.d.ts +271 -126
- package/dist/index.d.ts +17 -4
- package/dist/metafile-esm.json +1 -1
- package/dist/motion/fade.ts +15 -6
- package/dist/motion/grow.ts +23 -0
- package/dist/motion/index.d.ts +19 -2
- package/dist/motion/index.js +93 -3
- package/dist/motion/index.js.map +1 -1
- package/dist/motion/measure.ts +28 -0
- package/dist/motion/slide.ts +45 -0
- package/dist/mount/clickPopover.ts +51 -0
- package/dist/mount/contextPopover.ts +51 -0
- package/dist/mount/hoverPopover.ts +58 -0
- package/package.json +28 -12
- package/scripts/postbuild.ts +11 -1
- package/src/Accordion/Accordion.torp +44 -25
- package/src/Accordion/AccordionContent.torp +18 -12
- package/src/Accordion/AccordionHeader.torp +15 -8
- package/src/Accordion/AccordionItem.torp +34 -17
- package/src/Accordion/AccordionTrigger.torp +17 -10
- package/src/Accordion/AccordionTypes.ts +6 -3
- package/src/Breadcrumb/Breadcrumb.torp +18 -10
- package/src/Breadcrumb/BreadcrumbItem.torp +14 -7
- package/src/Breadcrumb/BreadcrumbSeparator.torp +10 -3
- package/src/Calendar/Calendar.torp +21 -14
- package/src/Calendar/CalendarDay.torp +12 -4
- package/src/Contextual/Contextual.torp +125 -0
- package/src/{Popover/PopoverContentContextual.torp → Contextual/ContextualContent.torp} +21 -12
- package/src/Contextual/ContextualTrigger.torp +66 -0
- package/src/Contextual/ContextualTypes.ts +30 -0
- package/src/Contextual/index.ts +5 -0
- package/src/Dialog/Dialog.torp +49 -73
- package/src/Dialog/DialogBody.torp +15 -9
- package/src/Dialog/DialogButton.torp +27 -13
- package/src/Dialog/DialogFooter.torp +11 -3
- package/src/Dialog/DialogHeader.torp +15 -8
- package/src/Dialog/DialogTypes.ts +3 -2
- package/src/Disclosure/Disclosure.torp +34 -25
- package/src/Disclosure/DisclosureContent.torp +18 -12
- package/src/Disclosure/DisclosureHeader.torp +16 -9
- package/src/Disclosure/DisclosureTrigger.torp +15 -8
- package/src/Disclosure/DisclosureTypes.ts +0 -1
- package/src/Drawer/Drawer.torp +109 -68
- package/src/Drawer/DrawerContent.torp +80 -0
- package/src/Drawer/DrawerOverlay.torp +38 -0
- package/src/Drawer/DrawerTrigger.torp +35 -0
- package/src/Drawer/DrawerTypes.ts +24 -0
- package/src/Drawer/index.ts +4 -1
- package/src/Form/Field.torp +91 -0
- package/src/Form/Form.torp +129 -0
- package/src/Form/FormTypes.ts +30 -0
- package/src/Form/Input.torp +97 -0
- package/src/Form/Label.torp +31 -0
- package/src/Form/Message.torp +35 -0
- package/src/Form/StandardSchemaV1.ts +70 -0
- package/src/Form/index.ts +7 -0
- package/src/Menu/Menu.torp +25 -15
- package/src/Menu/MenuButton.torp +30 -13
- package/src/Menu/MenuCheck.torp +29 -14
- package/src/Menu/MenuGroup.torp +14 -6
- package/src/Menu/MenuIndicator.torp +14 -7
- package/src/Menu/MenuPopout.torp +23 -15
- package/src/Menu/MenuPopoutContent.torp +17 -10
- package/src/Menu/MenuPopoutTrigger.torp +23 -16
- package/src/Menu/MenuRadio.torp +27 -16
- package/src/Menu/MenuRadioGroup.torp +34 -15
- package/src/Menu/MenuSeparator.torp +11 -3
- package/src/Menu/MenuTypes.ts +7 -5
- package/src/MenuBar/MenuBar.torp +24 -16
- package/src/MenuBar/MenuBarItem.torp +24 -16
- package/src/MenuBar/MenuBarItemContent.torp +18 -11
- package/src/MenuBar/MenuBarItemTrigger.torp +15 -9
- package/src/Modal/Modal.torp +118 -0
- package/src/Modal/ModalContent.torp +77 -0
- package/src/Modal/ModalOverlay.torp +38 -0
- package/src/Modal/ModalTrigger.torp +50 -0
- package/src/Modal/ModalTypes.ts +22 -0
- package/src/Modal/index.ts +10 -0
- package/src/Modal/showModal.ts +19 -0
- package/src/Modal/showPrompt.ts +23 -0
- package/src/NavMenu/NavMenu.torp +18 -10
- package/src/NavMenu/NavMenuButton.torp +15 -10
- package/src/NavMenu/NavMenuElement.torp +19 -11
- package/src/NavMenu/NavMenuGroup.torp +13 -6
- package/src/NavMenu/NavMenuGroupHeader.torp +15 -8
- package/src/NavMenu/NavMenuLink.torp +21 -16
- package/src/NavMenu/NavMenuPopout.torp +34 -18
- package/src/NavMenu/NavMenuPopoutButton.torp +23 -18
- package/src/NavMenu/NavMenuPopoutContent.torp +23 -13
- package/src/NavMenu/NavMenuSeparator.torp +10 -3
- package/src/Notification/Notification.torp +35 -16
- package/src/Notification/NotificationClose.torp +10 -3
- package/src/Notification/NotificationContainer.torp +11 -3
- package/src/Notification/NotificationContent.torp +11 -3
- package/src/Popover/Popover.torp +48 -28
- package/src/Popover/PopoverContent.torp +42 -8
- package/src/Popover/PopoverTrigger.torp +32 -3
- package/src/Popover/PopoverTypes.ts +1 -1
- package/src/Popover/index.ts +1 -24
- package/src/Slider/Slider.torp +3 -3
- package/src/TabGroup/TabContent.torp +20 -11
- package/src/TabGroup/TabGroup.torp +58 -40
- package/src/TabGroup/TabGroupTypes.ts +10 -5
- package/src/TabGroup/TabHeader.torp +26 -10
- package/src/TabGroup/TabList.torp +14 -7
- package/src/TabGroup/TabTrigger.torp +17 -10
- package/src/icons/ArrowDown.torp +5 -3
- package/src/icons/ArrowLeft.torp +5 -3
- package/src/icons/ArrowRight.torp +5 -3
- package/src/icons/ArrowUp.torp +5 -3
- package/src/icons/Bars.torp +5 -3
- package/src/icons/Check.torp +5 -3
- package/src/icons/ChevronDown.torp +5 -3
- package/src/icons/ChevronLeft.torp +5 -3
- package/src/icons/ChevronRight.torp +5 -3
- package/src/icons/ChevronUp.torp +5 -3
- package/src/icons/Cross.torp +5 -3
- package/src/icons/IconTypes.ts +6 -0
- package/src/icons/Loading.torp +5 -3
- package/src/icons/Minus.torp +5 -3
- package/src/icons/Plus.torp +5 -3
- package/src/motion/fade.ts +15 -6
- package/src/motion/grow.ts +23 -0
- package/src/motion/index.ts +4 -1
- package/src/motion/measure.ts +28 -0
- package/src/motion/slide.ts +45 -0
- package/src/mount/clickPopover.ts +51 -0
- package/src/mount/contextPopover.ts +51 -0
- package/src/mount/hoverPopover.ts +58 -0
- package/test/Accordion/components/AccordionAccessibility.torp +1 -1
- package/test/Accordion/components/AccordionKeyboard.torp +1 -1
- package/test/Accordion/components/AccordionList.torp +1 -1
- package/test/Accordion/components/AccordionMultiple.torp +2 -2
- package/test/Accordion/components/AccordionSingle.torp +2 -2
- package/test/Dialog/accessibility-modal.test.ts +2 -2
- package/test/Dialog/components/DialogAccessibility.torp +8 -14
- package/test/Dialog/components/DialogFunction.torp +20 -4
- package/test/Dialog/components/DialogModal.torp +30 -0
- package/test/Dialog/components/DialogVariable.torp +7 -7
- package/test/Disclosure/accessibility.test.ts +3 -2
- package/test/Disclosure/components/DisclosureTest.torp +1 -1
- package/test/Disclosure/toggle.test.ts +3 -2
- package/test/Form/components/FieldTest.torp +25 -0
- package/test/Form/components/FormTest.torp +31 -0
- package/test/Form/components/InputTest.torp +9 -0
- package/test/Form/field.test.ts +34 -0
- package/test/Form/form.test.ts +34 -0
- package/test/Form/input.test.ts +19 -0
- package/test/Menu/accessibility.test.ts +3 -2
- package/test/Menu/basic.test.ts +11 -10
- package/test/Menu/components/MenuAccessibility.torp +5 -5
- package/test/Menu/components/MenuBasic.torp +5 -5
- package/test/MenuBar/accessibility.test.ts +3 -2
- package/test/MenuBar/basic.test.ts +3 -2
- package/test/NavMenu/basic.test.ts +1 -1
- package/test/NavMenu/components/{NavMenu.torp → NavMenuBasic.torp} +1 -1
- package/test/Notification/components/NotificationFunction.torp +0 -3
- package/test/Notification/components/NotificationVariable.torp +1 -1
- package/test/Popover/components/PopoverFunction.torp +37 -0
- package/test/Popover/components/PopoverTrigger.torp +36 -0
- package/test/Popover/components/{PromptVariable.torp → PopoverVariable.torp} +5 -3
- package/test/Popover/function.test.ts +2 -7
- package/test/Popover/trigger.test.ts +27 -0
- package/test/Popover/variable.test.ts +2 -2
- package/test/TabGroup/components/TabGroupAccessibility.torp +1 -1
- package/test/TabGroup/components/TabGroupKeyboard.torp +1 -1
- package/test/TabGroup/components/TabGroupList.torp +1 -1
- package/test/TabGroup/components/TabGroupTrigger.torp +1 -1
- package/tsup.config.ts +3 -1
- package/dist/Popover/PopoverContentAnchored.torp +0 -106
- package/dist/Popover/PopoverOverlay.torp +0 -30
- package/dist/Popover/PopoverTriggerContextual.torp +0 -37
- package/dist/Popover/PopoverTriggerHover.torp +0 -38
- package/dist/Popover/mountClickPopover.ts +0 -50
- package/dist/Popover/mountContextPopover.ts +0 -50
- package/dist/Popover/mountHoverPopover.ts +0 -57
- package/dist/Popover/showPopoverPrompt.ts +0 -23
- package/src/Popover/PopoverContentAnchored.torp +0 -106
- package/src/Popover/PopoverOverlay.torp +0 -30
- package/src/Popover/PopoverTriggerContextual.torp +0 -37
- package/src/Popover/PopoverTriggerHover.torp +0 -38
- package/src/Popover/mountClickPopover.ts +0 -50
- package/src/Popover/mountContextPopover.ts +0 -50
- package/src/Popover/mountHoverPopover.ts +0 -57
- package/src/Popover/showPopoverPrompt.ts +0 -23
- package/test/Dialog/components/CustomDialog.torp +0 -20
- package/test/Popover/components/PromptFunction.torp +0 -19
package/src/Drawer/Drawer.torp
CHANGED
|
@@ -1,82 +1,123 @@
|
|
|
1
|
+
import { DrawerContextName, type DrawerContext, type DrawerState } from "./DrawerTypes";
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
interface DrawerProps {
|
|
4
|
+
/** An ID for the root element */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** Class name(s) for the root element */
|
|
7
|
+
class?: string,
|
|
8
|
+
/** Whether the drawer is modal. If true, an overlay should be supplied to obscure outside content, and only the drawer content will be visible to screen readers */
|
|
9
|
+
modal?: boolean,
|
|
10
|
+
/** Whether the drawer is visible */
|
|
11
|
+
visible?: boolean,
|
|
12
|
+
/** Which side of the window to show the drawer from */
|
|
13
|
+
position?: "left" | "right" | "top" | "bottom",
|
|
14
|
+
/** A function to be called when the user selects a response */
|
|
15
|
+
callback?: ((value: any) => void),
|
|
16
|
+
/** Raised when the drawer is opened or closed */
|
|
17
|
+
ontoggle?: (open: boolean) => void,
|
|
18
|
+
/** Raised when the drawer has opened */
|
|
19
|
+
onopen?: () => void,
|
|
20
|
+
/** Raised when the drawer has closed */
|
|
21
|
+
onclose?: (result: any) => void,
|
|
22
|
+
}
|
|
5
23
|
|
|
6
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A drawer that can be used to show rich content or obtain information from a user.
|
|
26
|
+
*
|
|
27
|
+
* See [the WAI ARIA guidelines for Dialogs](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/).
|
|
28
|
+
*
|
|
29
|
+
* The structure of a Drawer is:
|
|
30
|
+
*
|
|
31
|
+
* ```
|
|
32
|
+
* <Drawer>
|
|
33
|
+
* <DrawerTrigger /> (optional)
|
|
34
|
+
* <DrawerContent />
|
|
35
|
+
* </Drawer>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export default function Drawer($props: DrawerProps | undefined) {
|
|
39
|
+
let $state: DrawerState = $watch({
|
|
40
|
+
visible: false,
|
|
41
|
+
modal: $props?.modal ?? false,
|
|
42
|
+
position: $props?.position ?? "left",
|
|
43
|
+
});
|
|
7
44
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
45
|
+
// Sync props and state
|
|
46
|
+
$run(() => {
|
|
47
|
+
$state.visible = $props?.visible ?? false;
|
|
48
|
+
});
|
|
49
|
+
$run(() => {
|
|
50
|
+
if ($props !== undefined) {
|
|
51
|
+
$props.visible = $state.visible;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
12
54
|
|
|
13
|
-
|
|
14
|
-
let oldBefore = el.nextNode;
|
|
55
|
+
let result: any;
|
|
15
56
|
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
// Set the context to pass down to items
|
|
58
|
+
let context: DrawerContext = {
|
|
59
|
+
handleButton,
|
|
60
|
+
handleClickOutside,
|
|
61
|
+
state: $state,
|
|
62
|
+
};
|
|
63
|
+
$context[DrawerContextName] = context;
|
|
18
64
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
65
|
+
// Dispatch the toggle event when the drawer is opened or closed
|
|
66
|
+
let firstToggle = true;
|
|
67
|
+
$run(() => {
|
|
68
|
+
const ontoggle = $props?.ontoggle;
|
|
69
|
+
const onopen = $props?.onopen;
|
|
70
|
+
const onclose = $props?.onclose;
|
|
71
|
+
const visible = $state.visible;
|
|
72
|
+
if (firstToggle) {
|
|
73
|
+
firstToggle = false;
|
|
74
|
+
} else {
|
|
75
|
+
if (ontoggle) ontoggle(visible);
|
|
76
|
+
if (visible) {
|
|
77
|
+
if (onopen) onopen()
|
|
78
|
+
} else {
|
|
79
|
+
if (onclose) onclose(result);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
function handleButton(type: "confirm" | "cancel" | undefined, value?: any) {
|
|
85
|
+
// Get the button's result
|
|
86
|
+
switch (type) {
|
|
87
|
+
case "confirm": {
|
|
88
|
+
result = value || true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "cancel": {
|
|
92
|
+
result = value || false;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
default: {
|
|
96
|
+
result = value;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
22
100
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
el.style.position = oldPosition;
|
|
101
|
+
// Hide the drawer
|
|
102
|
+
$state.visible = false;
|
|
26
103
|
|
|
27
|
-
|
|
104
|
+
// Call the callback
|
|
105
|
+
if ($props?.callback) {
|
|
106
|
+
$props?.callback(result);
|
|
107
|
+
}
|
|
28
108
|
}
|
|
29
109
|
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
case "left": {
|
|
34
|
-
offscreen.style.left = 0;
|
|
35
|
-
const width = measure(offscreen, (el) => el.offsetWidth);
|
|
36
|
-
return [
|
|
37
|
-
{ transform: `translateX(-${width}px)` },
|
|
38
|
-
{ transform: "translateX(0px)" }
|
|
39
|
-
];
|
|
40
|
-
}
|
|
41
|
-
case "top": {
|
|
42
|
-
offscreen.style.top = 0;
|
|
43
|
-
const height = measure(offscreen, (el) => el.offsetHeight);
|
|
44
|
-
return [
|
|
45
|
-
{ transform: `translateY(-${height}px)` },
|
|
46
|
-
{ transform: "translateY(0px)" }
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
|
-
case "right": {
|
|
50
|
-
offscreen.style.right = 0;
|
|
51
|
-
const width = measure(offscreen, (el) => el.offsetWidth);
|
|
52
|
-
return [
|
|
53
|
-
{ transform: `translateX(${width}px)` },
|
|
54
|
-
{ transform: "translateX(0px)" }
|
|
55
|
-
];
|
|
56
|
-
}
|
|
57
|
-
case "bottom": {
|
|
58
|
-
offscreen.style.bottom = 0;
|
|
59
|
-
const height = measure(offscreen, (el) => el.offsetHeight);
|
|
60
|
-
return [
|
|
61
|
-
{ transform: `translateY(${height}px)` },
|
|
62
|
-
{ transform: "translateY(0px)" }
|
|
63
|
-
];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
110
|
+
function handleClickOutside(e: MouseEvent) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
$state.visible = false;
|
|
67
113
|
}
|
|
68
114
|
|
|
69
115
|
@render {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
&ref={offscreen}
|
|
77
|
-
>
|
|
78
|
-
<slot />
|
|
79
|
-
</div>
|
|
80
|
-
}
|
|
116
|
+
<div
|
|
117
|
+
{$props?.id}
|
|
118
|
+
class={`torp-drawer ${$props?.class ?? ""}`.trim()}
|
|
119
|
+
>
|
|
120
|
+
<slot />
|
|
121
|
+
</div>
|
|
81
122
|
}
|
|
82
|
-
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Animation } from "@torpor/view";
|
|
2
|
+
import { slide } from "../motion";
|
|
3
|
+
import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
|
|
4
|
+
import { DrawerContextName, type DrawerContext } from "./DrawerTypes";
|
|
5
|
+
|
|
6
|
+
interface DrawerContentProps {
|
|
7
|
+
/** An ID for the root element */
|
|
8
|
+
id?: string;
|
|
9
|
+
/** Class name(s) for the root element */
|
|
10
|
+
class?: string,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The content that is displayed for a Drawer when it is open.
|
|
15
|
+
*/
|
|
16
|
+
export default function DrawerContent($props: DrawerContentProps | undefined) {
|
|
17
|
+
let drawer: HTMLElement;
|
|
18
|
+
let shown = false;
|
|
19
|
+
let closeOnClick = false;
|
|
20
|
+
|
|
21
|
+
// Get stuff out of the DrawerContext
|
|
22
|
+
const context = $context[DrawerContextName] as DrawerContext;
|
|
23
|
+
if (!context) {
|
|
24
|
+
throw new Error("DrawerContent must be contained within a Drawer");
|
|
25
|
+
}
|
|
26
|
+
let $state = context.state;
|
|
27
|
+
|
|
28
|
+
$run(() => {
|
|
29
|
+
if (!$state.visible) {
|
|
30
|
+
hide();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function show() {
|
|
35
|
+
shown = true;
|
|
36
|
+
|
|
37
|
+
// HACK: Because this Drawer could be shown with a click, we need to wait for another mousedown
|
|
38
|
+
// before we close on click. Otherwise the click immediately bubbles to the document
|
|
39
|
+
closeOnClick = false;
|
|
40
|
+
addDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
41
|
+
addDocumentEvent("click", handleDocumentClick);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hide() {
|
|
45
|
+
if (!shown) return;
|
|
46
|
+
|
|
47
|
+
removeDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
48
|
+
removeDocumentEvent("click", handleDocumentClick);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleDocumentMouseDown() {
|
|
52
|
+
closeOnClick = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleDocumentClick(e: MouseEvent) {
|
|
56
|
+
if (
|
|
57
|
+
closeOnClick &&
|
|
58
|
+
drawer &&
|
|
59
|
+
!drawer.contains(e.target as HTMLElement)
|
|
60
|
+
) {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
context.handleClickOutside!(e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@render {
|
|
67
|
+
@if ($state.visible) {
|
|
68
|
+
<div
|
|
69
|
+
{$props?.id}
|
|
70
|
+
class={`torp-drawer-content ${$props?.class ?? ""}`.trim()}
|
|
71
|
+
style="position: fixed;"
|
|
72
|
+
transition={(e) => slide(e, { side: $state.position })}
|
|
73
|
+
&ref={drawer}
|
|
74
|
+
onmount={show}
|
|
75
|
+
>
|
|
76
|
+
<slot />
|
|
77
|
+
</div>
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { disableScroll } from "../mount/disableScroll";
|
|
2
|
+
import { DrawerContextName, type DrawerContext } from "./DrawerTypes";
|
|
3
|
+
|
|
4
|
+
interface DrawerOverlayProps {
|
|
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 Drawer component.
|
|
13
|
+
* Adding an overlay automatically sets the component's `modal` value to true.
|
|
14
|
+
*/
|
|
15
|
+
export default function DrawerOverlay($props: DrawerOverlayProps | undefined) {
|
|
16
|
+
// Get stuff out of the DrawerContext
|
|
17
|
+
const context = $context[DrawerContextName] as DrawerContext;
|
|
18
|
+
if (!context) {
|
|
19
|
+
throw new Error("DrawerOverlay must be contained within a Drawer");
|
|
20
|
+
}
|
|
21
|
+
let $state = context.state;
|
|
22
|
+
|
|
23
|
+
// Any drawer with an overlay is modal
|
|
24
|
+
$state.modal = true;
|
|
25
|
+
|
|
26
|
+
@render {
|
|
27
|
+
@if ($state.visible) {
|
|
28
|
+
<div
|
|
29
|
+
{$props?.id}
|
|
30
|
+
class={`torp-drawer-overlay ${$props?.class ?? ""}`.trim()}
|
|
31
|
+
tabindex="-1"
|
|
32
|
+
onmount={(el) => disableScroll(el)}
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</div>
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DrawerContextName, type DrawerContext } from "./DrawerTypes";
|
|
2
|
+
|
|
3
|
+
interface DrawerTriggerProps {
|
|
4
|
+
/** An ID for the root element */
|
|
5
|
+
id?: string;
|
|
6
|
+
/** Class name(s) for the root element */
|
|
7
|
+
class?: string,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The button that causes a Drawer to appear or disappear.
|
|
12
|
+
*/
|
|
13
|
+
export default function DrawerTrigger($props: DrawerTriggerProps | undefined) {
|
|
14
|
+
// Get stuff out of the DrawerContext
|
|
15
|
+
const context = $context[DrawerContextName] as DrawerContext;
|
|
16
|
+
if (!context) {
|
|
17
|
+
throw new Error("DrawerTrigger must be contained within a Drawer");
|
|
18
|
+
}
|
|
19
|
+
let $state = context.state;
|
|
20
|
+
|
|
21
|
+
function handleClick() {
|
|
22
|
+
$state.visible = !$state.visible;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@render {
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
{$props?.id}
|
|
29
|
+
class={`torp-drawer-trigger ${$props?.class ?? ""}`.trim()}
|
|
30
|
+
onclick={handleClick}
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</button>
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const DrawerContextName = "Drawer";
|
|
2
|
+
|
|
3
|
+
/** The shared context for drawer components (Contextual, Prompt and Drawer) */
|
|
4
|
+
export interface DrawerContext {
|
|
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: DrawerState;
|
|
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 DrawerState {
|
|
21
|
+
visible: boolean;
|
|
22
|
+
modal: boolean;
|
|
23
|
+
position: "left" | "right" | "top" | "bottom";
|
|
24
|
+
}
|
package/src/Drawer/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import Drawer from "./Drawer.torp";
|
|
2
|
+
import DrawerContent from "./DrawerContent.torp";
|
|
3
|
+
import DrawerOverlay from "./DrawerOverlay.torp";
|
|
4
|
+
import DrawerTrigger from "./DrawerTrigger.torp";
|
|
2
5
|
|
|
3
|
-
export { Drawer };
|
|
6
|
+
export { Drawer, DrawerContent, DrawerOverlay, DrawerTrigger };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FormContextName,
|
|
3
|
+
FieldContextName,
|
|
4
|
+
type ValidationIssue,
|
|
5
|
+
type FormContext,
|
|
6
|
+
type FieldContext,
|
|
7
|
+
} from "./FormTypes";
|
|
8
|
+
import { StandardSchemaV1 } from "./StandardSchemaV1"
|
|
9
|
+
|
|
10
|
+
interface FieldProps {
|
|
11
|
+
/** An ID for the root element */
|
|
12
|
+
id?: string;
|
|
13
|
+
/** Class name(s) for the root element */
|
|
14
|
+
class?: string,
|
|
15
|
+
/** The name of the data that this field contains */
|
|
16
|
+
name: string,
|
|
17
|
+
/** The data value that this field is bound to */
|
|
18
|
+
value?: any,
|
|
19
|
+
/** The schema validator to use for this form */
|
|
20
|
+
schema?: StandardSchemaV1,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A field in a form which automatically links up a Label and/or an input
|
|
25
|
+
* component and/or a Message.
|
|
26
|
+
*/
|
|
27
|
+
export default function Field($props: FieldProps) {
|
|
28
|
+
let $state = $watch({
|
|
29
|
+
inputId: "-1",
|
|
30
|
+
value: $props.value,
|
|
31
|
+
issues: [] as ValidationIssue[],
|
|
32
|
+
get valid() {
|
|
33
|
+
return this.issues.find((i) => i.path === $props.name) === undefined;
|
|
34
|
+
},
|
|
35
|
+
get message() {
|
|
36
|
+
return this.issues.filter((i) => i.path === $props.name).map((i) => i.message).join("\n");
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Binding
|
|
41
|
+
$run(() => {
|
|
42
|
+
$props.value = $state.value;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Get stuff out of the FormContext, if applicable
|
|
46
|
+
const formContext = $context[FormContextName] as FormContext;
|
|
47
|
+
if (formContext) {
|
|
48
|
+
$run(() => {
|
|
49
|
+
$state.issues = formContext.state.issues;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Set the context to pass down to Labels, Inputs and Messages
|
|
54
|
+
let context: FieldContext = {
|
|
55
|
+
name: $props.name,
|
|
56
|
+
state: $state,
|
|
57
|
+
validate: handleValidate
|
|
58
|
+
};
|
|
59
|
+
$context[FieldContextName] = context;
|
|
60
|
+
|
|
61
|
+
async function validate() {
|
|
62
|
+
if ($props.schema) {
|
|
63
|
+
const validator = $props.schema["~standard"];
|
|
64
|
+
let result = validator.validate($props.value);
|
|
65
|
+
if (result instanceof Promise) {
|
|
66
|
+
result = await result;
|
|
67
|
+
}
|
|
68
|
+
let issues: ValidationIssue[] = [];
|
|
69
|
+
if (result.issues) {
|
|
70
|
+
for (let issue of result.issues) {
|
|
71
|
+
issues.push({ path: $props.name, message: issue.message });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
$state.issues = $state.issues.filter((e) => e.path !== $props.name).concat(issues);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function handleValidate() {
|
|
79
|
+
await validate();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@render {
|
|
83
|
+
<div
|
|
84
|
+
{$props?.id}
|
|
85
|
+
class={`torp-field ${$props?.class ?? ""}`.trim()}
|
|
86
|
+
data-valid={$state.valid ? "valid" : "invalid"}
|
|
87
|
+
>
|
|
88
|
+
<slot />
|
|
89
|
+
</div>
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { FormContextName, type ValidationIssue, type FormContext } from "./FormTypes";
|
|
2
|
+
import { StandardSchemaV1 } from "./StandardSchemaV1"
|
|
3
|
+
|
|
4
|
+
interface FormProps {
|
|
5
|
+
/** An ID for the root element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/** Class name(s) for the root element */
|
|
8
|
+
class?: string,
|
|
9
|
+
/** The data to display in the form */
|
|
10
|
+
data?: any,
|
|
11
|
+
/** Where to post the form's data **/
|
|
12
|
+
action?: string,
|
|
13
|
+
/** The schema validator to use for this form */
|
|
14
|
+
schema?: StandardSchemaV1,
|
|
15
|
+
onsubmit?: (e: SubmitEvent) => boolean | void | Promise<boolean | void>,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A form for submitting values.
|
|
20
|
+
*/
|
|
21
|
+
export default function Form($props: FormProps | undefined) {
|
|
22
|
+
let $state = $watch({
|
|
23
|
+
data: $props?.data ?? {},
|
|
24
|
+
issues: [] as ValidationIssue[],
|
|
25
|
+
get valid() {
|
|
26
|
+
return this.issues.length === 0;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
let submitted = false;
|
|
31
|
+
|
|
32
|
+
// TODO: Load context from build with a submit method that submits to a form action
|
|
33
|
+
|
|
34
|
+
// Set the context to pass down to Fields
|
|
35
|
+
let context: FormContext = {
|
|
36
|
+
schema: $props?.schema,
|
|
37
|
+
state: $state,
|
|
38
|
+
validate: handleValidate
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
$context[FormContextName] = context;
|
|
42
|
+
|
|
43
|
+
async function validate() {
|
|
44
|
+
if ($props?.schema) {
|
|
45
|
+
const validator = $props.schema["~standard"];
|
|
46
|
+
let result = validator.validate($props.data);
|
|
47
|
+
if (result instanceof Promise) {
|
|
48
|
+
result = await result;
|
|
49
|
+
}
|
|
50
|
+
let issues: ValidationIssue[] = [];
|
|
51
|
+
if (result.issues) {
|
|
52
|
+
for (let issue of result.issues) {
|
|
53
|
+
if (issue.path) {
|
|
54
|
+
// TODO: arrays with `[0]`
|
|
55
|
+
issues.push({ path: issue.path.join("."), message: issue.message });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
$state.issues = issues;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function handleSubmit(e: SubmitEvent) {
|
|
64
|
+
// Validate, and proceed with submission if there are no errors
|
|
65
|
+
await validate();
|
|
66
|
+
let proceed = $state.valid;
|
|
67
|
+
|
|
68
|
+
// Changing inputs from now on will cause revalidation
|
|
69
|
+
submitted = true;
|
|
70
|
+
|
|
71
|
+
// If validation failed, don't continue
|
|
72
|
+
if (!proceed) {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Dispatch the submit event for the user to handle
|
|
78
|
+
if ($props?.onsubmit) {
|
|
79
|
+
let userSubmit = $props.onsubmit(e);
|
|
80
|
+
if (userSubmit instanceof Promise) {
|
|
81
|
+
userSubmit = await userSubmit;
|
|
82
|
+
}
|
|
83
|
+
proceed = userSubmit !== false && !e.defaultPrevented;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// If the user handled the submit, don't continue
|
|
87
|
+
if (!proceed) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If there's a @torpor/build context, call its submit event
|
|
93
|
+
let buildContext = $context["TorporBuildContext"];
|
|
94
|
+
if (buildContext?.onformsubmit) {
|
|
95
|
+
let buildSubmit = buildContext.onformsubmit(e);
|
|
96
|
+
if (buildSubmit instanceof Promise) {
|
|
97
|
+
buildSubmit = await buildSubmit;
|
|
98
|
+
}
|
|
99
|
+
proceed = buildSubmit !== false && !e.defaultPrevented;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// If @torpor/build handled the submit, don't continue
|
|
103
|
+
if (!proceed) {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Otherwise just let normal form submission go ahead...
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function handleValidate() {
|
|
112
|
+
if (submitted) {
|
|
113
|
+
await validate();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@render {
|
|
118
|
+
<form
|
|
119
|
+
{$props?.id}
|
|
120
|
+
class={`torp-form ${$props?.class ?? ""}`.trim()}
|
|
121
|
+
method="POST"
|
|
122
|
+
onsubmit={handleSubmit}
|
|
123
|
+
@// Set novalidate only if JS is enabled (to override HTML validation)
|
|
124
|
+
onmount={(e) => e.novalidate = true }
|
|
125
|
+
>
|
|
126
|
+
<slot />
|
|
127
|
+
</form>
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type StandardSchemaV1 } from "./StandardSchemaV1";
|
|
2
|
+
|
|
3
|
+
export const FormContextName: unique symbol = Symbol("Form");
|
|
4
|
+
export const FieldContextName: unique symbol = Symbol("Field");
|
|
5
|
+
|
|
6
|
+
export interface ValidationIssue {
|
|
7
|
+
path: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FormContext {
|
|
12
|
+
schema?: StandardSchemaV1;
|
|
13
|
+
state: {
|
|
14
|
+
issues: ValidationIssue[];
|
|
15
|
+
};
|
|
16
|
+
validate: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FieldContext {
|
|
20
|
+
name: string;
|
|
21
|
+
schema?: StandardSchemaV1;
|
|
22
|
+
state: {
|
|
23
|
+
inputId: string;
|
|
24
|
+
value?: any;
|
|
25
|
+
issues: ValidationIssue[];
|
|
26
|
+
valid: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
validate: () => void;
|
|
30
|
+
}
|