@torpor/ui 0.0.16 → 0.1.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 +38 -0
- package/dist/Accordion/Accordion.torp +53 -54
- package/dist/Accordion/AccordionContent.torp +5 -5
- package/dist/Accordion/AccordionHeader.torp +5 -4
- package/dist/Accordion/AccordionItem.torp +13 -8
- package/dist/Accordion/AccordionTrigger.torp +5 -5
- package/dist/Accordion/AccordionTypes.ts +1 -0
- package/dist/Dialog/Dialog.torp +156 -0
- package/dist/Dialog/DialogBody.torp +34 -0
- package/dist/Dialog/DialogButton.torp +48 -0
- package/dist/Dialog/DialogFooter.torp +13 -0
- package/dist/Dialog/DialogHeader.torp +34 -0
- package/dist/Dialog/DialogTypes.ts +16 -0
- package/dist/Dialog/index.cjs +53 -0
- package/dist/Dialog/index.cjs.map +1 -0
- package/dist/Dialog/index.js +22 -0
- package/dist/Dialog/index.js.map +1 -0
- package/dist/NavMenu/NavMenu.torp +178 -0
- package/dist/NavMenu/NavMenuButton.torp +46 -0
- package/dist/NavMenu/NavMenuElement.torp +43 -0
- package/dist/NavMenu/NavMenuGroup.torp +30 -0
- package/dist/NavMenu/NavMenuGroupHeader.torp +30 -0
- package/dist/NavMenu/NavMenuLink.torp +52 -0
- package/dist/NavMenu/NavMenuPopout.torp +69 -0
- package/dist/NavMenu/NavMenuPopoutButton.torp +120 -0
- package/dist/NavMenu/NavMenuPopoutContent.torp +79 -0
- package/dist/NavMenu/NavMenuSeparator.torp +14 -0
- package/dist/NavMenu/NavMenuTypes.ts +32 -0
- package/dist/NavMenu/index.cjs +78 -0
- package/dist/NavMenu/index.cjs.map +1 -0
- package/dist/NavMenu/index.js +42 -0
- package/dist/NavMenu/index.js.map +1 -0
- package/dist/Popover/Popover.torp +105 -0
- package/dist/Popover/PopoverContentAnchored.torp +107 -0
- package/dist/Popover/PopoverContentContextual.torp +99 -0
- package/dist/Popover/PopoverContentStatic.torp +78 -0
- package/dist/Popover/PopoverOverlay.torp +32 -0
- package/dist/Popover/PopoverTrigger.torp +29 -0
- package/dist/Popover/PopoverTriggerContextual.torp +37 -0
- package/dist/Popover/PopoverTriggerHover.torp +30 -0
- package/dist/Popover/PopoverTypes.ts +30 -0
- package/dist/Popover/index.cjs +222 -0
- package/dist/Popover/index.cjs.map +1 -0
- package/dist/Popover/index.js +183 -0
- package/dist/Popover/index.js.map +1 -0
- package/dist/Popover/mountClickPopover.ts +50 -0
- package/dist/Popover/mountContextPopover.ts +50 -0
- package/dist/Popover/mountHoverPopover.ts +57 -0
- package/dist/Popover/showPopover.ts +24 -0
- package/dist/Popover/showPopoverPrompt.ts +23 -0
- package/dist/TabGroup/TabContent.torp +36 -0
- package/dist/TabGroup/TabGroup.torp +225 -0
- package/dist/TabGroup/TabGroupTypes.ts +39 -0
- package/dist/TabGroup/TabHeader.torp +62 -0
- package/dist/TabGroup/TabList.torp +50 -0
- package/dist/TabGroup/TabTrigger.torp +65 -0
- package/dist/TabGroup/index.cjs +53 -0
- package/dist/TabGroup/index.cjs.map +1 -0
- package/dist/TabGroup/index.js +22 -0
- package/dist/TabGroup/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/mount/disableScroll.ts +28 -0
- package/dist/mount/hoverTrigger.ts +59 -0
- package/dist/mount/trapFocus.ts +58 -0
- package/dist/utils/documentEvents.ts +17 -0
- package/dist/utils/getScrollParent.ts +22 -0
- package/dist/utils/setContextPosition.ts +31 -0
- package/dist/utils/setPopoverPosition.ts +138 -0
- package/package.json +26 -6
- package/scripts/postbuild.ts +28 -10
- package/src/Accordion/Accordion.torp +53 -54
- package/src/Accordion/AccordionContent.torp +5 -5
- package/src/Accordion/AccordionHeader.torp +5 -4
- package/src/Accordion/AccordionItem.torp +13 -8
- package/src/Accordion/AccordionTrigger.torp +5 -5
- package/src/Accordion/AccordionTypes.ts +1 -0
- package/src/Dialog/Dialog.torp +156 -0
- package/src/Dialog/DialogBody.torp +34 -0
- package/src/Dialog/DialogButton.torp +48 -0
- package/src/Dialog/DialogFooter.torp +13 -0
- package/src/Dialog/DialogHeader.torp +34 -0
- package/src/Dialog/DialogTypes.ts +16 -0
- package/src/Dialog/index.ts +7 -0
- package/src/Dialog.ts +7 -0
- package/src/NavMenu/NavMenu.torp +178 -0
- package/src/NavMenu/NavMenuButton.torp +46 -0
- package/src/NavMenu/NavMenuElement.torp +43 -0
- package/src/NavMenu/NavMenuGroup.torp +30 -0
- package/src/NavMenu/NavMenuGroupHeader.torp +30 -0
- package/src/NavMenu/NavMenuLink.torp +52 -0
- package/src/NavMenu/NavMenuPopout.torp +69 -0
- package/src/NavMenu/NavMenuPopoutButton.torp +120 -0
- package/src/NavMenu/NavMenuPopoutContent.torp +79 -0
- package/src/NavMenu/NavMenuSeparator.torp +14 -0
- package/src/NavMenu/NavMenuTypes.ts +32 -0
- package/src/NavMenu/index.ts +23 -0
- package/src/NavMenu.ts +23 -0
- package/src/Popover/Popover.torp +105 -0
- package/src/Popover/PopoverContentAnchored.torp +107 -0
- package/src/Popover/PopoverContentContextual.torp +99 -0
- package/src/Popover/PopoverContentStatic.torp +78 -0
- package/src/Popover/PopoverOverlay.torp +32 -0
- package/src/Popover/PopoverTrigger.torp +29 -0
- package/src/Popover/PopoverTriggerContextual.torp +37 -0
- package/src/Popover/PopoverTriggerHover.torp +30 -0
- package/src/Popover/PopoverTypes.ts +30 -0
- package/src/Popover/index.ts +29 -0
- package/src/Popover/mountClickPopover.ts +50 -0
- package/src/Popover/mountContextPopover.ts +50 -0
- package/src/Popover/mountHoverPopover.ts +57 -0
- package/src/Popover/showPopover.ts +24 -0
- package/src/Popover/showPopoverPrompt.ts +23 -0
- package/src/Popover.ts +29 -0
- package/src/TabGroup/TabContent.torp +36 -0
- package/src/TabGroup/TabGroup.torp +225 -0
- package/src/TabGroup/TabGroupTypes.ts +39 -0
- package/src/TabGroup/TabHeader.torp +62 -0
- package/src/TabGroup/TabList.torp +50 -0
- package/src/TabGroup/TabTrigger.torp +65 -0
- package/src/TabGroup/index.ts +7 -0
- package/src/TabGroup.ts +7 -0
- package/src/mount/disableScroll.ts +28 -0
- package/src/mount/hoverTrigger.ts +59 -0
- package/src/mount/trapFocus.ts +58 -0
- package/src/utils/documentEvents.ts +17 -0
- package/src/utils/getScrollParent.ts +22 -0
- package/src/utils/setContextPosition.ts +31 -0
- package/src/utils/setPopoverPosition.ts +138 -0
- package/test/Accordion/accessibility.test.ts +0 -3
- package/test/Accordion/for-items.test.ts +2 -4
- package/test/Accordion/keyboard.test.ts +0 -3
- package/test/Accordion/multiple-item-toggle.test.ts +3 -5
- package/test/Accordion/single-item-toggle.test.ts +3 -5
- package/test/Dialog/accessibility-alert.test.ts +40 -0
- package/test/Dialog/accessibility-modal.test.ts +52 -0
- package/test/Dialog/components/CustomDialog.torp +20 -0
- package/test/Dialog/components/DialogAccessibility.torp +31 -0
- package/test/Dialog/components/DialogFunction.torp +22 -0
- package/test/Dialog/components/DialogVariable.torp +35 -0
- package/test/Dialog/function.test.ts +26 -0
- package/test/Dialog/variable.test.ts +27 -0
- package/test/NavMenu/accessibility.test.ts +97 -0
- package/test/NavMenu/basic.test.ts +22 -0
- package/test/NavMenu/components/NavMenu.torp +28 -0
- package/test/NavMenu/components/NavMenuAccessibility.torp +35 -0
- package/test/TabGroup/accessibility.test.ts +67 -0
- package/test/TabGroup/components/TabGroupAccessibility.torp +28 -0
- package/test/TabGroup/components/TabGroupKeyboard.torp +28 -0
- package/test/TabGroup/components/TabGroupList.torp +26 -0
- package/test/TabGroup/components/TabGroupTrigger.torp +28 -0
- package/test/TabGroup/for-items.test.ts +52 -0
- package/test/TabGroup/item-trigger.test.ts +32 -0
- package/test/TabGroup/item-value.test.ts +17 -0
- package/test/TabGroup/keyboard.test.ts +72 -0
- package/tsup.config.ts +8 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
import {
|
|
3
|
+
NavMenuContextName,
|
|
4
|
+
NavMenuPopoutContextName,
|
|
5
|
+
type NavMenuContext,
|
|
6
|
+
type NavMenuPopoutContext,
|
|
7
|
+
} from "./NavMenuTypes";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A button that is placed in a NavMenu and signals a response by a user.
|
|
11
|
+
*
|
|
12
|
+
* @prop {number} hoverDelay -- The delay before showing the popout, in milliseconds
|
|
13
|
+
* @prop {boolean} disabled -- Whether this button is disabled
|
|
14
|
+
*/
|
|
15
|
+
export default function NavMenuPopoutButton($props: any) {
|
|
16
|
+
$props.hoverDelay ??= 500;
|
|
17
|
+
|
|
18
|
+
/** Set when the item is disabled */
|
|
19
|
+
let dataDisabled = $props.disabled || undefined;
|
|
20
|
+
|
|
21
|
+
let button: HTMLButtonElement;
|
|
22
|
+
let interval: NodeJS.Timeout | undefined;
|
|
23
|
+
|
|
24
|
+
// Get stuff out of the NavMenuContext
|
|
25
|
+
const menuContext = $context[NavMenuContextName] as NavMenuContext;
|
|
26
|
+
if (!menuContext) {
|
|
27
|
+
throw new Error("NavMenuPopoutButton must be contained within a NavMenu");
|
|
28
|
+
}
|
|
29
|
+
const { registerItem, handleItemKey, handleItemFocus } = menuContext;
|
|
30
|
+
|
|
31
|
+
// Get stuff out of the NavMenuPopoutContext
|
|
32
|
+
const popoutContext = $context[NavMenuPopoutContextName] as NavMenuPopoutContext;
|
|
33
|
+
if (!popoutContext) {
|
|
34
|
+
throw new Error("NavMenuPopoutButton must be contained within a NavMenuPopout");
|
|
35
|
+
}
|
|
36
|
+
let $state = popoutContext.state;
|
|
37
|
+
|
|
38
|
+
// Register this item with the parent NavMenu and store the index
|
|
39
|
+
const { index } = registerItem(setFocused);
|
|
40
|
+
|
|
41
|
+
// TODO: $mount should be run AFTER the button has been set
|
|
42
|
+
//$mount(() => {
|
|
43
|
+
// // Set the anchor element to the button when it exists
|
|
44
|
+
// popoutContext.anchorElement = button;
|
|
45
|
+
//});
|
|
46
|
+
|
|
47
|
+
function setFocused() {
|
|
48
|
+
button.focus();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleClick() {
|
|
52
|
+
$state.visible = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handlePopoutKey(e: KeyboardEvent) {
|
|
56
|
+
switch (e.key) {
|
|
57
|
+
case "Right":
|
|
58
|
+
case "ArrowRight":
|
|
59
|
+
// TODO: This should be based on what side the menu shows on
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
$state.visible = true;
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
handleItemKey(e);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function handleMouseEnter() {
|
|
69
|
+
if ($props.disabled) return;
|
|
70
|
+
|
|
71
|
+
// Handle the hover after a delay
|
|
72
|
+
if (!interval) {
|
|
73
|
+
interval = setTimeout(() => $state.visible = true, $props.hoverDelay);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function handleMouseLeave() {
|
|
78
|
+
if ($props.disabled) return;
|
|
79
|
+
|
|
80
|
+
// Prevent short hovers or touches from firing the event
|
|
81
|
+
if (interval) {
|
|
82
|
+
clearTimeout(interval);
|
|
83
|
+
interval = undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function handleMouseMove() {
|
|
88
|
+
if ($props.disabled) return;
|
|
89
|
+
|
|
90
|
+
// Reset the interval on mouse move
|
|
91
|
+
if (interval) {
|
|
92
|
+
clearTimeout(interval);
|
|
93
|
+
interval = setTimeout(() => $state.visible = true, $props.hoverDelay);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@render {
|
|
98
|
+
<button
|
|
99
|
+
id={$props.id}
|
|
100
|
+
class={`torp-nav-menu-popout-button ${$props.class ?? ""}`.trim()}
|
|
101
|
+
type="button"
|
|
102
|
+
disabled={$props.disabled}
|
|
103
|
+
role="menuitem"
|
|
104
|
+
aria-haspopup="true"
|
|
105
|
+
aria-expanded={$state.visible.toString()}
|
|
106
|
+
aria-disabled={$props.disabled ? "true" : undefined}
|
|
107
|
+
data-disabled={dataDisabled}
|
|
108
|
+
onkeydown={handlePopoutKey}
|
|
109
|
+
onclick={handleClick}
|
|
110
|
+
onfocus={() => handleItemFocus(index)}
|
|
111
|
+
onmouseenter={handleMouseEnter}
|
|
112
|
+
onmouseleave={handleMouseLeave}
|
|
113
|
+
onmousemove={handleMouseMove}
|
|
114
|
+
&ref={button}
|
|
115
|
+
:onmount={(el) => popoutContext.anchorElement = el }
|
|
116
|
+
>
|
|
117
|
+
<:slot />
|
|
118
|
+
</button>
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
import getScrollParent from "../utils/getScrollParent";
|
|
3
|
+
import setPopoverPosition from "../utils/setPopoverPosition";
|
|
4
|
+
import { NavMenuPopoutContextName, type NavMenuPopoutContext } from "./NavMenuTypes";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The content that is displayed for a NavMenuPopout when it is open.
|
|
8
|
+
*
|
|
9
|
+
* @prop {"top" | "right" | "bottom" | "left"} side -- Where the popout will be shown, relative to the popout button
|
|
10
|
+
* @prop {"start" | "center" | "end"} alignment -- How the popout will be aligned, relative to the popout button
|
|
11
|
+
*/
|
|
12
|
+
export default function NavMenuPopoutContent($props: any) {
|
|
13
|
+
$props.side ??= "bottom";
|
|
14
|
+
$props.alignment ??= "start";
|
|
15
|
+
|
|
16
|
+
let div: HTMLDivElement;
|
|
17
|
+
|
|
18
|
+
// Get stuff out of the NavMenuPopoutContext
|
|
19
|
+
const context = $context[NavMenuPopoutContextName] as NavMenuPopoutContext;
|
|
20
|
+
if (!context) {
|
|
21
|
+
throw new Error("NavMenuPopoutContent must be contained within a NavMenuPopout");
|
|
22
|
+
}
|
|
23
|
+
let $state = context.state;
|
|
24
|
+
|
|
25
|
+
let first = true;
|
|
26
|
+
$run(() => {
|
|
27
|
+
let visible = $state.visible;
|
|
28
|
+
|
|
29
|
+
if (first) {
|
|
30
|
+
first = false;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (visible) {
|
|
35
|
+
// Wait for the content div to be shown, set its position, and listen for window resize to re-position
|
|
36
|
+
//await tick();
|
|
37
|
+
setPosition();
|
|
38
|
+
if (context.focusFirstElement) {
|
|
39
|
+
context.focusFirstElement();
|
|
40
|
+
}
|
|
41
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
42
|
+
scrollParent.addEventListener("resize", setPosition);
|
|
43
|
+
scrollParent.addEventListener("scroll", setPosition);
|
|
44
|
+
} else {
|
|
45
|
+
// Stop listening for window resize and focus the anchor element per WAI guidelines
|
|
46
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
47
|
+
scrollParent.removeEventListener("resize", setPosition);
|
|
48
|
+
scrollParent.removeEventListener("scroll", setPosition);
|
|
49
|
+
if (context.anchorElement?.focus) {
|
|
50
|
+
context.anchorElement.focus();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
function setPosition() {
|
|
56
|
+
if (!context.anchorElement) {
|
|
57
|
+
throw new Error("NavMenuPopoutContent must have an anchor element");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (div) {
|
|
61
|
+
setPopoverPosition(context.anchorElement, div, $props.side, $props.alignment);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@render {
|
|
66
|
+
<div style="display: contents">
|
|
67
|
+
@if ($state.visible) {
|
|
68
|
+
<div
|
|
69
|
+
id={$props.id}
|
|
70
|
+
class={`torp-nav-menu-popout-content ${$props.class ?? ""}`.trim()}
|
|
71
|
+
&ref={div}
|
|
72
|
+
:onmount={(el) => setPopoverPosition(context.anchorElement, el, $props.side, $props.alignment)}
|
|
73
|
+
>
|
|
74
|
+
<:slot />
|
|
75
|
+
</div>
|
|
76
|
+
}
|
|
77
|
+
</div>
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* An element in a menu that separates logical sets of menu items.
|
|
4
|
+
*/
|
|
5
|
+
export default function NavMenuSeparator($props: any) {
|
|
6
|
+
@render {
|
|
7
|
+
<div
|
|
8
|
+
id={$props.id}
|
|
9
|
+
class={`torp-nav-menu-separator ${$props.class ?? ""}`.trim()} role="separator"
|
|
10
|
+
>
|
|
11
|
+
<:slot />
|
|
12
|
+
</div>
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const NavMenuContextName = "NavMenu";
|
|
2
|
+
export const NavMenuPopoutContextName = "NavMenuPopout";
|
|
3
|
+
export const NavMenuGroupContextName = "NavMenuGroup";
|
|
4
|
+
|
|
5
|
+
export interface NavMenuContext {
|
|
6
|
+
/** Called from a NavMenuLink or NavMenuButton when its button is focused */
|
|
7
|
+
handleItemFocus: (index: number) => void;
|
|
8
|
+
|
|
9
|
+
/** Called from a NavMenuLink or NavMenuButton when a key is pressed */
|
|
10
|
+
handleItemKey: (e: KeyboardEvent) => void;
|
|
11
|
+
|
|
12
|
+
/** Called from each NavMenuLink or NavMenuButton to register itself with this NavMenu */
|
|
13
|
+
registerItem: (setFocused: () => void) => { index: number };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NavMenuPopoutContext {
|
|
17
|
+
state: {
|
|
18
|
+
visible: boolean;
|
|
19
|
+
};
|
|
20
|
+
anchorElement?: HTMLElement;
|
|
21
|
+
focusFirstElement?: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NavMenuGroupContext {
|
|
25
|
+
state: {
|
|
26
|
+
headerId: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ItemState {
|
|
31
|
+
setFocused: () => void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/NavMenu/index.ts
|
|
21
|
+
var NavMenu_exports = {};
|
|
22
|
+
__export(NavMenu_exports, {
|
|
23
|
+
NavMenu: () => NavMenu_default,
|
|
24
|
+
NavMenuButton: () => NavMenuButton_default,
|
|
25
|
+
NavMenuElement: () => NavMenuElement_default,
|
|
26
|
+
NavMenuGroup: () => NavMenuGroup_default,
|
|
27
|
+
NavMenuGroupHeader: () => NavMenuGroupHeader_default,
|
|
28
|
+
NavMenuLink: () => NavMenuLink_default,
|
|
29
|
+
NavMenuPopout: () => NavMenuPopout_default,
|
|
30
|
+
NavMenuPopoutButton: () => NavMenuPopoutButton_default,
|
|
31
|
+
NavMenuPopoutContent: () => NavMenuPopoutContent_default,
|
|
32
|
+
NavMenuSeparator: () => NavMenuSeparator_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(NavMenu_exports);
|
|
35
|
+
|
|
36
|
+
// src/NavMenu/NavMenu.torp
|
|
37
|
+
import NavMenu_default from "./NavMenu.torp";
|
|
38
|
+
|
|
39
|
+
// src/NavMenu/NavMenuButton.torp
|
|
40
|
+
import NavMenuButton_default from "./NavMenuButton.torp";
|
|
41
|
+
|
|
42
|
+
// src/NavMenu/NavMenuElement.torp
|
|
43
|
+
import NavMenuElement_default from "./NavMenuElement.torp";
|
|
44
|
+
|
|
45
|
+
// src/NavMenu/NavMenuGroup.torp
|
|
46
|
+
import NavMenuGroup_default from "./NavMenuGroup.torp";
|
|
47
|
+
|
|
48
|
+
// src/NavMenu/NavMenuGroupHeader.torp
|
|
49
|
+
import NavMenuGroupHeader_default from "./NavMenuGroupHeader.torp";
|
|
50
|
+
|
|
51
|
+
// src/NavMenu/NavMenuLink.torp
|
|
52
|
+
import NavMenuLink_default from "./NavMenuLink.torp";
|
|
53
|
+
|
|
54
|
+
// src/NavMenu/NavMenuPopout.torp
|
|
55
|
+
import NavMenuPopout_default from "./NavMenuPopout.torp";
|
|
56
|
+
|
|
57
|
+
// src/NavMenu/NavMenuPopoutButton.torp
|
|
58
|
+
import NavMenuPopoutButton_default from "./NavMenuPopoutButton.torp";
|
|
59
|
+
|
|
60
|
+
// src/NavMenu/NavMenuPopoutContent.torp
|
|
61
|
+
import NavMenuPopoutContent_default from "./NavMenuPopoutContent.torp";
|
|
62
|
+
|
|
63
|
+
// src/NavMenu/NavMenuSeparator.torp
|
|
64
|
+
import NavMenuSeparator_default from "./NavMenuSeparator.torp";
|
|
65
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
66
|
+
0 && (module.exports = {
|
|
67
|
+
NavMenu,
|
|
68
|
+
NavMenuButton,
|
|
69
|
+
NavMenuElement,
|
|
70
|
+
NavMenuGroup,
|
|
71
|
+
NavMenuGroupHeader,
|
|
72
|
+
NavMenuLink,
|
|
73
|
+
NavMenuPopout,
|
|
74
|
+
NavMenuPopoutButton,
|
|
75
|
+
NavMenuPopoutContent,
|
|
76
|
+
NavMenuSeparator
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/NavMenu/index.ts"],"sourcesContent":["import NavMenu from \"./NavMenu.torp\";\nimport NavMenuButton from \"./NavMenuButton.torp\";\nimport NavMenuElement from \"./NavMenuElement.torp\";\nimport NavMenuGroup from \"./NavMenuGroup.torp\";\nimport NavMenuGroupHeader from \"./NavMenuGroupHeader.torp\";\nimport NavMenuLink from \"./NavMenuLink.torp\";\nimport NavMenuPopout from \"./NavMenuPopout.torp\";\nimport NavMenuPopoutButton from \"./NavMenuPopoutButton.torp\";\nimport NavMenuPopoutContent from \"./NavMenuPopoutContent.torp\";\nimport NavMenuSeparator from \"./NavMenuSeparator.torp\";\n\nexport {\n\tNavMenu,\n\tNavMenuLink,\n\tNavMenuButton,\n\tNavMenuElement,\n\tNavMenuPopout,\n\tNavMenuPopoutButton,\n\tNavMenuPopoutContent,\n\tNavMenuGroup,\n\tNavMenuGroupHeader,\n\tNavMenuSeparator,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A","names":[]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/NavMenu/NavMenu.torp
|
|
2
|
+
import NavMenu_default from "./NavMenu.torp";
|
|
3
|
+
|
|
4
|
+
// src/NavMenu/NavMenuButton.torp
|
|
5
|
+
import NavMenuButton_default from "./NavMenuButton.torp";
|
|
6
|
+
|
|
7
|
+
// src/NavMenu/NavMenuElement.torp
|
|
8
|
+
import NavMenuElement_default from "./NavMenuElement.torp";
|
|
9
|
+
|
|
10
|
+
// src/NavMenu/NavMenuGroup.torp
|
|
11
|
+
import NavMenuGroup_default from "./NavMenuGroup.torp";
|
|
12
|
+
|
|
13
|
+
// src/NavMenu/NavMenuGroupHeader.torp
|
|
14
|
+
import NavMenuGroupHeader_default from "./NavMenuGroupHeader.torp";
|
|
15
|
+
|
|
16
|
+
// src/NavMenu/NavMenuLink.torp
|
|
17
|
+
import NavMenuLink_default from "./NavMenuLink.torp";
|
|
18
|
+
|
|
19
|
+
// src/NavMenu/NavMenuPopout.torp
|
|
20
|
+
import NavMenuPopout_default from "./NavMenuPopout.torp";
|
|
21
|
+
|
|
22
|
+
// src/NavMenu/NavMenuPopoutButton.torp
|
|
23
|
+
import NavMenuPopoutButton_default from "./NavMenuPopoutButton.torp";
|
|
24
|
+
|
|
25
|
+
// src/NavMenu/NavMenuPopoutContent.torp
|
|
26
|
+
import NavMenuPopoutContent_default from "./NavMenuPopoutContent.torp";
|
|
27
|
+
|
|
28
|
+
// src/NavMenu/NavMenuSeparator.torp
|
|
29
|
+
import NavMenuSeparator_default from "./NavMenuSeparator.torp";
|
|
30
|
+
export {
|
|
31
|
+
NavMenu_default as NavMenu,
|
|
32
|
+
NavMenuButton_default as NavMenuButton,
|
|
33
|
+
NavMenuElement_default as NavMenuElement,
|
|
34
|
+
NavMenuGroup_default as NavMenuGroup,
|
|
35
|
+
NavMenuGroupHeader_default as NavMenuGroupHeader,
|
|
36
|
+
NavMenuLink_default as NavMenuLink,
|
|
37
|
+
NavMenuPopout_default as NavMenuPopout,
|
|
38
|
+
NavMenuPopoutButton_default as NavMenuPopoutButton,
|
|
39
|
+
NavMenuPopoutContent_default as NavMenuPopoutContent,
|
|
40
|
+
NavMenuSeparator_default as NavMenuSeparator
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { PopoverContextName, type Point, type PopoverContext, type PopoverState } from "./PopoverTypes";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A popover that can be used to show rich content or obtain information from a user.
|
|
5
|
+
*
|
|
6
|
+
* See [the WAI ARIA guidelines for Dialogs](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/).
|
|
7
|
+
*
|
|
8
|
+
* The structure of a Popover is:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* <Popover>
|
|
12
|
+
* <PopoverOverlay /> (optional, if modal)
|
|
13
|
+
* <PopoverTrigger/PopoverTriggerContextual/PopoverTriggerHover /> (optional)
|
|
14
|
+
* <PopoverContentStatic/PopoverContentAnchored/PopoverContentContextual />
|
|
15
|
+
* </Popover>
|
|
16
|
+
* ```
|
|
17
|
+
* @prop {boolean} modal -- Whether the popover is modal. If true, an overlay should be supplied to obscure outside content, and only the popover content will be visible to screen readers
|
|
18
|
+
* @prop {boolean} visible -- Whether the popover is visible
|
|
19
|
+
* @prop {HTMLElement | undefined} anchor -- The element to anchor this popover to, as an alternative to supplying a PopoverTrigger
|
|
20
|
+
* @prop {Point | undefined} position -- The position to display this popover at if it's contextual, as an alternative to supplying a PopoverTriggerContextual
|
|
21
|
+
* @prop {((value: any) => void) | undefined} [callback] -- A function to be called when the user selects a response
|
|
22
|
+
*/
|
|
23
|
+
export default function Popover($props: any) {
|
|
24
|
+
let $state: PopoverState = $watch({
|
|
25
|
+
visible: false,
|
|
26
|
+
position: { x: 0, y: 0 }
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
let result: any;
|
|
30
|
+
|
|
31
|
+
// Set the context to pass down to items
|
|
32
|
+
let context: PopoverContext = {
|
|
33
|
+
handleButton,
|
|
34
|
+
handleClickOutside,
|
|
35
|
+
state: $state,
|
|
36
|
+
modal: $props.modal,
|
|
37
|
+
anchorElement: $unwrap($props.anchor),
|
|
38
|
+
};
|
|
39
|
+
$context[PopoverContextName] = context;
|
|
40
|
+
|
|
41
|
+
// Copy props into state whenever they change
|
|
42
|
+
$run(() => {
|
|
43
|
+
$state.visible = $props.visible ?? false;
|
|
44
|
+
$state.position = $props.position ?? { x: 0, y: 0 };
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Dispatch the toggle event when the popout is opened or closed
|
|
48
|
+
let firstToggle = true;
|
|
49
|
+
$run(() => {
|
|
50
|
+
const ontoggle = $props.ontoggle;
|
|
51
|
+
const onopen = $props.onopen;
|
|
52
|
+
const onclose = $props.onclose;
|
|
53
|
+
const visible = $state.visible;
|
|
54
|
+
if (firstToggle) {
|
|
55
|
+
firstToggle = false;
|
|
56
|
+
} else {
|
|
57
|
+
if (ontoggle) ontoggle(visible);
|
|
58
|
+
if (visible) {
|
|
59
|
+
if (onopen) onopen()
|
|
60
|
+
} else {
|
|
61
|
+
if (onclose) onclose(result);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function handleButton(type: "confirm" | "cancel" | undefined, value?: any) {
|
|
67
|
+
// Get the button's result
|
|
68
|
+
switch (type) {
|
|
69
|
+
case "confirm": {
|
|
70
|
+
result = value || true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "cancel": {
|
|
74
|
+
result = value || false;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
default: {
|
|
78
|
+
result = value;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Hide the popover
|
|
84
|
+
$state.visible = false;
|
|
85
|
+
|
|
86
|
+
// Call the callback
|
|
87
|
+
if ($props.callback) {
|
|
88
|
+
$props.callback(result);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function handleClickOutside(e: MouseEvent) {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
$state.visible = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@render {
|
|
98
|
+
<div
|
|
99
|
+
id={$props.id}
|
|
100
|
+
class={`torp-popover ${$props.class ?? ""}`.trim()}
|
|
101
|
+
>
|
|
102
|
+
<:slot />
|
|
103
|
+
</div>
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
|
|
2
|
+
import getScrollParent from "../utils/getScrollParent";
|
|
3
|
+
import setPopoverPosition from "../utils/setPopoverPosition";
|
|
4
|
+
import { PopoverContextName, type PopoverContext } from "./PopoverTypes";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The content that is displayed for a Popover when it is open, displayed
|
|
8
|
+
* alongside an element.
|
|
9
|
+
*
|
|
10
|
+
* @prop {"top" | "right" | "bottom" | "left"} site -- Where the popover will be
|
|
11
|
+
* shown, relative to the popover button
|
|
12
|
+
* @prop {"start" | "center" | "end"} alignment -- How the popover will be
|
|
13
|
+
* aligned, relative to the popover button
|
|
14
|
+
*/
|
|
15
|
+
export default function PopoverContentAnchored($props: any) {
|
|
16
|
+
$props.side ??= "bottom";
|
|
17
|
+
$props.alignment ??= "center";
|
|
18
|
+
|
|
19
|
+
let div: HTMLDivElement;
|
|
20
|
+
|
|
21
|
+
let closeOnClick = false;
|
|
22
|
+
|
|
23
|
+
// Get stuff out of the PopoverContext
|
|
24
|
+
const context = $context[PopoverContextName] as PopoverContext;
|
|
25
|
+
if (!context) {
|
|
26
|
+
throw new Error("PopoverContentAnchored must be contained within a Popover");
|
|
27
|
+
}
|
|
28
|
+
let $state = context.state;
|
|
29
|
+
|
|
30
|
+
let first = true;
|
|
31
|
+
$run(() => {
|
|
32
|
+
let visible = $state.visible;
|
|
33
|
+
|
|
34
|
+
if (first) {
|
|
35
|
+
first = false;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (visible) {
|
|
40
|
+
// HACK: Because this Popover could be shown with a click, we need to wait for another mousedown
|
|
41
|
+
// before we close on click. Otherwise the click immediately bubbles to the document
|
|
42
|
+
closeOnClick = false;
|
|
43
|
+
addDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
44
|
+
addDocumentEvent("click", handleDocumentClick);
|
|
45
|
+
// Wait for the content div to be shown, set its position, and listen for window resize to re-position
|
|
46
|
+
//await tick();
|
|
47
|
+
setPosition();
|
|
48
|
+
if (context.focusFirstElement) {
|
|
49
|
+
context.focusFirstElement();
|
|
50
|
+
}
|
|
51
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
52
|
+
scrollParent.addEventListener("resize", setPosition);
|
|
53
|
+
scrollParent.addEventListener("scroll", setPosition);
|
|
54
|
+
} else {
|
|
55
|
+
removeDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
56
|
+
removeDocumentEvent("click", handleDocumentClick);
|
|
57
|
+
// Stop listening for window resize and focus the anchor element per WAI guidelines
|
|
58
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
59
|
+
scrollParent.removeEventListener("resize", setPosition);
|
|
60
|
+
scrollParent.removeEventListener("scroll", setPosition);
|
|
61
|
+
if (context.anchorElement?.focus) {
|
|
62
|
+
context.anchorElement.focus();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function handleDocumentMouseDown() {
|
|
68
|
+
closeOnClick = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function handleDocumentClick(e: MouseEvent) {
|
|
72
|
+
if (
|
|
73
|
+
closeOnClick &&
|
|
74
|
+
div &&
|
|
75
|
+
!div.contains(e.target as HTMLElement) &&
|
|
76
|
+
(!context.anchorElement || !context.anchorElement.contains(e.target as HTMLElement))
|
|
77
|
+
) {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
context.handleClickOutside!(e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function setPosition() {
|
|
84
|
+
if (!context.anchorElement) {
|
|
85
|
+
throw new Error("PopoverContentAnchored must have an anchor element");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (div) {
|
|
89
|
+
setPopoverPosition(context.anchorElement, div, $props.side, $props.alignment);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@render {
|
|
94
|
+
<div style="display: content;">
|
|
95
|
+
@if ($state.visible) {
|
|
96
|
+
<div
|
|
97
|
+
id={$props.id}
|
|
98
|
+
class={`torp-popover-content-anchored ${$props.class ?? ""}`.trim()}
|
|
99
|
+
&ref={div}
|
|
100
|
+
:onmount={(el) => setPopoverPosition(context.anchorElement, el, $props.side, $props.alignment)}
|
|
101
|
+
>
|
|
102
|
+
<:slot />
|
|
103
|
+
</div>
|
|
104
|
+
}
|
|
105
|
+
</div>
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
import { addDocumentEvent, removeDocumentEvent } from "../utils/documentEvents";
|
|
3
|
+
import getScrollParent from "../utils/getScrollParent";
|
|
4
|
+
import setContextPosition from "../utils/setContextPosition";
|
|
5
|
+
import { PopoverContextName, type PopoverContext } from "./PopoverTypes";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The content that is displayed for a Popover when it is open, displayed at the
|
|
9
|
+
* position that the user right-clicked.
|
|
10
|
+
*/
|
|
11
|
+
export default function PopoverContentContextual($props: any) {
|
|
12
|
+
let div: HTMLDivElement;
|
|
13
|
+
|
|
14
|
+
let closeOnClick = false;
|
|
15
|
+
|
|
16
|
+
// Get stuff out of the PopoverContext
|
|
17
|
+
const context = $context[PopoverContextName] as PopoverContext;
|
|
18
|
+
if (!context) {
|
|
19
|
+
throw new Error("ContextualContent must be contained within a Popover");
|
|
20
|
+
}
|
|
21
|
+
let $state = context.state;
|
|
22
|
+
|
|
23
|
+
let first = true;
|
|
24
|
+
$run(() => {
|
|
25
|
+
let visible = $state.visible;
|
|
26
|
+
|
|
27
|
+
if (first) {
|
|
28
|
+
first = false;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (visible) {
|
|
33
|
+
// HACK: Because this Contextual could be shown with a click, we need to wait for another mousedown
|
|
34
|
+
// before we close on click. Otherwise the click immediately bubbles to the document
|
|
35
|
+
closeOnClick = false;
|
|
36
|
+
addDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
37
|
+
addDocumentEvent("click", handleDocumentClick);
|
|
38
|
+
// Wait for the content div to be shown, set its position, and listen for window resize to re-position
|
|
39
|
+
//await tick();
|
|
40
|
+
setPosition();
|
|
41
|
+
if (context.focusFirstElement) {
|
|
42
|
+
context.focusFirstElement();
|
|
43
|
+
}
|
|
44
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
45
|
+
scrollParent.addEventListener("resize", setPosition);
|
|
46
|
+
scrollParent.addEventListener("scroll", setPosition);
|
|
47
|
+
} else {
|
|
48
|
+
removeDocumentEvent("mousedown", handleDocumentMouseDown);
|
|
49
|
+
removeDocumentEvent("click", handleDocumentClick);
|
|
50
|
+
// Stop listening for window resize and focus the anchor element per WAI guidelines
|
|
51
|
+
const scrollParent = getScrollParent(context.anchorElement)
|
|
52
|
+
scrollParent.removeEventListener("resize", setPosition);
|
|
53
|
+
scrollParent.removeEventListener("scroll", setPosition);
|
|
54
|
+
if (context.anchorElement?.focus) {
|
|
55
|
+
context.anchorElement.focus();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function handleDocumentMouseDown() {
|
|
61
|
+
closeOnClick = true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function handleDocumentClick(e: MouseEvent) {
|
|
65
|
+
if (
|
|
66
|
+
closeOnClick &&
|
|
67
|
+
div &&
|
|
68
|
+
!div.contains(e.target as HTMLElement) &&
|
|
69
|
+
(!context.anchorElement || !context.anchorElement.contains(e.target as HTMLElement))
|
|
70
|
+
) {
|
|
71
|
+
context.handleClickOutside!(e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function setPosition() {
|
|
76
|
+
if (!context.anchorElement) {
|
|
77
|
+
throw new Error("ContextualContent must have an anchor element");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (div && $state.position) {
|
|
81
|
+
setContextPosition(context.anchorElement, div, $state.position);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@render {
|
|
86
|
+
<div style="display: content;">
|
|
87
|
+
@if ($state.visible) {
|
|
88
|
+
<div
|
|
89
|
+
id={$props.id}
|
|
90
|
+
class={`torp-popover-content-contextual ${$props.class ?? ""}`.trim()}
|
|
91
|
+
&ref={div}
|
|
92
|
+
:onmount={(el) => setContextPosition(context.anchorElement, el, $state.position)}
|
|
93
|
+
>
|
|
94
|
+
<:slot />
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
</div>
|
|
98
|
+
}
|
|
99
|
+
}
|