@hjmds/react 1.1.1 → 1.3.4
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/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +12 -6
- package/dist/actions.js.map +1 -1
- package/dist/advanced-display.d.ts +21 -0
- package/dist/advanced-display.d.ts.map +1 -1
- package/dist/advanced-display.js +11 -0
- package/dist/advanced-display.js.map +1 -1
- package/dist/advanced-navigation.d.ts +2 -23
- package/dist/advanced-navigation.d.ts.map +1 -1
- package/dist/advanced-navigation.js +3 -24
- package/dist/advanced-navigation.js.map +1 -1
- package/dist/agreement.d.ts +20 -0
- package/dist/agreement.d.ts.map +1 -0
- package/dist/agreement.js +29 -0
- package/dist/agreement.js.map +1 -0
- package/dist/anchor.d.ts +24 -0
- package/dist/anchor.d.ts.map +1 -0
- package/dist/anchor.js +110 -0
- package/dist/anchor.js.map +1 -0
- package/dist/asset.d.ts +40 -0
- package/dist/asset.d.ts.map +1 -0
- package/dist/asset.js +26 -0
- package/dist/asset.js.map +1 -0
- package/dist/auth-screen.d.ts +29 -0
- package/dist/auth-screen.d.ts.map +1 -0
- package/dist/auth-screen.js +26 -0
- package/dist/auth-screen.js.map +1 -0
- package/dist/bottom-cta.d.ts +32 -0
- package/dist/bottom-cta.d.ts.map +1 -0
- package/dist/bottom-cta.js +30 -0
- package/dist/bottom-cta.js.map +1 -0
- package/dist/bottom-info.d.ts +16 -0
- package/dist/bottom-info.d.ts.map +1 -0
- package/dist/bottom-info.js +12 -0
- package/dist/bottom-info.js.map +1 -0
- package/dist/breadcrumb.d.ts +9 -0
- package/dist/breadcrumb.d.ts.map +1 -0
- package/dist/breadcrumb.js +12 -0
- package/dist/breadcrumb.js.map +1 -0
- package/dist/calendar.d.ts +30 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +69 -0
- package/dist/calendar.js.map +1 -0
- package/dist/carousel.d.ts +20 -0
- package/dist/carousel.d.ts.map +1 -0
- package/dist/carousel.js +70 -0
- package/dist/carousel.js.map +1 -0
- package/dist/clipboard.d.ts +33 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +40 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/collapsible.d.ts +11 -0
- package/dist/collapsible.d.ts.map +1 -0
- package/dist/collapsible.js +18 -0
- package/dist/collapsible.js.map +1 -0
- package/dist/command-palette.d.ts +25 -0
- package/dist/command-palette.d.ts.map +1 -0
- package/dist/command-palette.js +82 -0
- package/dist/command-palette.js.map +1 -0
- package/dist/context-menu.d.ts +12 -0
- package/dist/context-menu.d.ts.map +1 -0
- package/dist/context-menu.js +111 -0
- package/dist/context-menu.js.map +1 -0
- package/dist/data-table.d.ts +30 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/data-table.js +57 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-picker.d.ts +3 -1
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +16 -31
- package/dist/date-picker.js.map +1 -1
- package/dist/date-range.d.ts +24 -0
- package/dist/date-range.d.ts.map +1 -0
- package/dist/date-range.js +29 -0
- package/dist/date-range.js.map +1 -0
- package/dist/display.d.ts +16 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +13 -1
- package/dist/display.js.map +1 -1
- package/dist/evidence.d.ts +496 -1
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +48 -2
- package/dist/evidence.js.map +1 -1
- package/dist/feedback.d.ts +18 -2
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +10 -2
- package/dist/feedback.js.map +1 -1
- package/dist/floating-action-button.d.ts +32 -0
- package/dist/floating-action-button.d.ts.map +1 -0
- package/dist/floating-action-button.js +58 -0
- package/dist/floating-action-button.js.map +1 -0
- package/dist/forms.d.ts +3 -3
- package/dist/forms.d.ts.map +1 -1
- package/dist/forms.js +18 -7
- package/dist/forms.js.map +1 -1
- package/dist/heading.d.ts +12 -0
- package/dist/heading.d.ts.map +1 -0
- package/dist/heading.js +25 -0
- package/dist/heading.js.map +1 -0
- package/dist/index.d.ts +36 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -2
- package/dist/index.js.map +1 -1
- package/dist/layout.d.ts +1 -1
- package/dist/mentions.d.ts +28 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +84 -0
- package/dist/mentions.js.map +1 -0
- package/dist/menubar.d.ts +11 -0
- package/dist/menubar.d.ts.map +1 -0
- package/dist/menubar.js +137 -0
- package/dist/menubar.js.map +1 -0
- package/dist/modal.d.ts +67 -0
- package/dist/modal.d.ts.map +1 -0
- package/dist/modal.js +307 -0
- package/dist/modal.js.map +1 -0
- package/dist/overlay-stack.d.ts +28 -0
- package/dist/overlay-stack.d.ts.map +1 -0
- package/dist/overlay-stack.js +47 -0
- package/dist/overlay-stack.js.map +1 -0
- package/dist/overlays.d.ts +26 -43
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +62 -302
- package/dist/overlays.js.map +1 -1
- package/dist/pagination.d.ts +17 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +21 -0
- package/dist/pagination.js.map +1 -0
- package/dist/popover.d.ts +25 -0
- package/dist/popover.d.ts.map +1 -0
- package/dist/popover.js +226 -0
- package/dist/popover.js.map +1 -0
- package/dist/portal.d.ts +5 -1
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +24 -1
- package/dist/portal.js.map +1 -1
- package/dist/provider-button.d.ts +21 -0
- package/dist/provider-button.d.ts.map +1 -0
- package/dist/provider-button.js +26 -0
- package/dist/provider-button.js.map +1 -0
- package/dist/provider.d.ts +13 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +12 -2
- package/dist/provider.js.map +1 -1
- package/dist/selection.d.ts +5 -1
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +2 -2
- package/dist/selection.js.map +1 -1
- package/dist/side-panel.d.ts +34 -0
- package/dist/side-panel.d.ts.map +1 -0
- package/dist/side-panel.js +95 -0
- package/dist/side-panel.js.map +1 -0
- package/dist/sidebar.d.ts +22 -0
- package/dist/sidebar.d.ts.map +1 -0
- package/dist/sidebar.js +29 -0
- package/dist/sidebar.js.map +1 -0
- package/dist/skip-nav.d.ts +12 -0
- package/dist/skip-nav.d.ts.map +1 -0
- package/dist/skip-nav.js +29 -0
- package/dist/skip-nav.js.map +1 -0
- package/dist/splitter.d.ts +45 -0
- package/dist/splitter.d.ts.map +1 -0
- package/dist/splitter.js +116 -0
- package/dist/splitter.js.map +1 -0
- package/dist/styles.css +1105 -26
- package/dist/supplemental-display.d.ts +12 -0
- package/dist/supplemental-display.d.ts.map +1 -1
- package/dist/supplemental-display.js +6 -2
- package/dist/supplemental-display.js.map +1 -1
- package/dist/tags-input.d.ts +60 -0
- package/dist/tags-input.d.ts.map +1 -0
- package/dist/tags-input.js +90 -0
- package/dist/tags-input.js.map +1 -0
- package/dist/text-formats.d.ts +16 -0
- package/dist/text-formats.d.ts.map +1 -0
- package/dist/text-formats.js +24 -0
- package/dist/text-formats.js.map +1 -0
- package/dist/toast.d.ts +1 -1
- package/dist/toggle-group.d.ts +13 -0
- package/dist/toggle-group.d.ts.map +1 -0
- package/dist/toggle-group.js +21 -0
- package/dist/toggle-group.js.map +1 -0
- package/dist/top-bar.d.ts +27 -0
- package/dist/top-bar.d.ts.map +1 -0
- package/dist/top-bar.js +30 -0
- package/dist/top-bar.js.map +1 -0
- package/dist/top.d.ts +15 -0
- package/dist/top.d.ts.map +1 -0
- package/dist/top.js +18 -0
- package/dist/top.js.map +1 -0
- package/dist/tour.d.ts +21 -0
- package/dist/tour.d.ts.map +1 -0
- package/dist/tour.js +122 -0
- package/dist/tour.js.map +1 -0
- package/dist/transfer-list.d.ts +24 -0
- package/dist/transfer-list.d.ts.map +1 -0
- package/dist/transfer-list.js +109 -0
- package/dist/transfer-list.js.map +1 -0
- package/dist/tree.d.ts +29 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +120 -0
- package/dist/tree.js.map +1 -0
- package/package.json +178 -3
package/dist/overlays.js
CHANGED
|
@@ -1,309 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { isLargeTextScale } from "@hjmds/design-contracts/components/design-system-provider";
|
|
3
2
|
import { createAlertDialogSession, getAlertDialogInitialFocus, validateAlertDialogRequest, } from "@hjmds/design-contracts/components/alert-dialog";
|
|
4
|
-
import { createSheetLifecycle, sheetBehaviorDefaults, } from "@hjmds/design-contracts/components/sheet";
|
|
3
|
+
import { createSheetLifecycle, resolveNextSheetDetent, sheetBehaviorDefaults, } from "@hjmds/design-contracts/components/sheet";
|
|
5
4
|
import { resolveTooltipDescriptor, tooltipBehaviorDefaults, } from "@hjmds/design-contracts/components/tooltip";
|
|
6
5
|
import { dialogRecipe, menuRecipe, sheetRecipe, } from "@hjmds/design-contracts/recipes";
|
|
7
6
|
import { cloneElement, forwardRef, useCallback, useEffect, useId, useRef, useState, useSyncExternalStore, } from "react";
|
|
8
|
-
import { createPortal } from "react-dom";
|
|
9
7
|
import { Button } from "./actions.js";
|
|
10
8
|
import { classNames, composeRefs, useControllableState } from "./internal.js";
|
|
11
|
-
import { AnchoredPortal, useAnchoredPopup
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
function
|
|
15
|
-
return target !== null &&
|
|
16
|
-
"nodeType" in target &&
|
|
17
|
-
container?.contains(target) === true;
|
|
18
|
-
}
|
|
19
|
-
function HjmPortal({ children, container }) {
|
|
20
|
-
const [mounted, setMounted] = useState(false);
|
|
21
|
-
const theme = useOptionalHjmTheme();
|
|
22
|
-
useEffect(() => setMounted(true), []);
|
|
23
|
-
if (!mounted)
|
|
24
|
-
return null;
|
|
25
|
-
return createPortal(theme ? (_jsx("div", { className: "hjm-root hjm-portal", "data-hjm-portal": "", "data-motion": theme.environment.reducedMotion ? "reduced" : "full", "data-theme": theme.environment.theme, "data-text-scale": theme.environment.textScale, "data-large-text": isLargeTextScale(theme.environment.textScale) ? "true" : undefined, dir: theme.environment.direction, style: createHjmThemeStyle(theme), children: children })) : children, container ?? document.body);
|
|
26
|
-
}
|
|
27
|
-
function useOpenState({ open, defaultOpen = false, onOpenChange, }) {
|
|
28
|
-
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
29
|
-
const controlled = open !== undefined;
|
|
30
|
-
const currentOpen = controlled ? open : internalOpen;
|
|
31
|
-
const pendingRequestRef = useRef(undefined);
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (pendingRequestRef.current === currentOpen) {
|
|
34
|
-
pendingRequestRef.current = undefined;
|
|
35
|
-
}
|
|
36
|
-
}, [currentOpen]);
|
|
37
|
-
const changeOpen = useCallback((nextOpen, detail) => {
|
|
38
|
-
if (nextOpen === currentOpen ||
|
|
39
|
-
pendingRequestRef.current === nextOpen)
|
|
40
|
-
return;
|
|
41
|
-
pendingRequestRef.current = nextOpen;
|
|
42
|
-
if (!controlled)
|
|
43
|
-
setInternalOpen(nextOpen);
|
|
44
|
-
onOpenChange?.(nextOpen, detail);
|
|
45
|
-
if (controlled && nextOpen) {
|
|
46
|
-
queueMicrotask(() => {
|
|
47
|
-
if (pendingRequestRef.current === true) {
|
|
48
|
-
pendingRequestRef.current = undefined;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}, [controlled, currentOpen, onOpenChange]);
|
|
53
|
-
return [currentOpen, changeOpen];
|
|
54
|
-
}
|
|
55
|
-
const focusableSelector = [
|
|
56
|
-
"a[href]",
|
|
57
|
-
"button:not([disabled])",
|
|
58
|
-
"input:not([disabled])",
|
|
59
|
-
"select:not([disabled])",
|
|
60
|
-
"textarea:not([disabled])",
|
|
61
|
-
"[tabindex]:not([tabindex='-1'])",
|
|
62
|
-
].join(",");
|
|
63
|
-
function getFocusable(container) {
|
|
64
|
-
return [...container.querySelectorAll(focusableSelector)].filter((element) => !element.hidden && element.getAttribute("aria-hidden") !== "true");
|
|
65
|
-
}
|
|
66
|
-
let bodyLockCount = 0;
|
|
67
|
-
let previousBodyOverflow = "";
|
|
68
|
-
const activeModalStack = [];
|
|
69
|
-
let activeModalOrder = 0;
|
|
70
|
-
const isolatedModalBackground = new Map();
|
|
71
|
-
let modalIsolationObserver = null;
|
|
72
|
-
function getOwnedPopupHosts(modal) {
|
|
73
|
-
const ownerId = modal.element.id;
|
|
74
|
-
if (ownerId.length === 0)
|
|
75
|
-
return [];
|
|
76
|
-
return [...document.querySelectorAll("[data-hjm-popup-owner]")]
|
|
77
|
-
.filter((host) => host.getAttribute("data-hjm-popup-owner") === ownerId);
|
|
78
|
-
}
|
|
79
|
-
function modalContainsNode(modal, node) {
|
|
80
|
-
return modal.element.contains(node) ||
|
|
81
|
-
getOwnedPopupHosts(modal).some((host) => host.contains(node));
|
|
82
|
-
}
|
|
83
|
-
function getModalFocusable(modal) {
|
|
84
|
-
return [
|
|
85
|
-
...getFocusable(modal.element),
|
|
86
|
-
...getOwnedPopupHosts(modal).flatMap((host) => getFocusable(host)),
|
|
87
|
-
];
|
|
88
|
-
}
|
|
89
|
-
function getModalLayer(priority) {
|
|
90
|
-
if (!Number.isSafeInteger(priority)) {
|
|
91
|
-
throw new TypeError("modalPriority must be a safe integer");
|
|
92
|
-
}
|
|
93
|
-
return 1000 + priority;
|
|
94
|
-
}
|
|
95
|
-
function getTopModal() {
|
|
96
|
-
let top;
|
|
97
|
-
for (const modal of activeModalStack) {
|
|
98
|
-
if (!modal.element.isConnected)
|
|
99
|
-
continue;
|
|
100
|
-
if (top === undefined ||
|
|
101
|
-
modal.priority > top.priority ||
|
|
102
|
-
(modal.priority === top.priority && modal.order > top.order))
|
|
103
|
-
top = modal;
|
|
104
|
-
}
|
|
105
|
-
return top;
|
|
106
|
-
}
|
|
107
|
-
function modalRanksAbove(candidate, reference) {
|
|
108
|
-
return candidate.priority > reference.priority ||
|
|
109
|
-
(candidate.priority === reference.priority && candidate.order > reference.order);
|
|
110
|
-
}
|
|
111
|
-
function restoreModalBackground() {
|
|
112
|
-
for (const [element, previous] of isolatedModalBackground) {
|
|
113
|
-
element.inert = previous.inert;
|
|
114
|
-
if (previous.ariaHidden === null)
|
|
115
|
-
element.removeAttribute("aria-hidden");
|
|
116
|
-
else
|
|
117
|
-
element.setAttribute("aria-hidden", previous.ariaHidden);
|
|
118
|
-
}
|
|
119
|
-
isolatedModalBackground.clear();
|
|
120
|
-
}
|
|
121
|
-
function isolateModalBackgroundElement(element) {
|
|
122
|
-
if (isolatedModalBackground.has(element))
|
|
123
|
-
return;
|
|
124
|
-
isolatedModalBackground.set(element, {
|
|
125
|
-
ariaHidden: element.getAttribute("aria-hidden"),
|
|
126
|
-
inert: element.inert,
|
|
127
|
-
});
|
|
128
|
-
element.inert = true;
|
|
129
|
-
element.setAttribute("aria-hidden", "true");
|
|
130
|
-
}
|
|
131
|
-
/** Keeps only the top modal's ancestor path interactive, including late portals. */
|
|
132
|
-
function synchronizeModalBackgroundIsolation() {
|
|
133
|
-
restoreModalBackground();
|
|
134
|
-
for (let index = activeModalStack.length - 1; index >= 0; index -= 1) {
|
|
135
|
-
if (!activeModalStack[index]?.element.isConnected)
|
|
136
|
-
activeModalStack.splice(index, 1);
|
|
137
|
-
}
|
|
138
|
-
const top = getTopModal();
|
|
139
|
-
if (!top) {
|
|
140
|
-
modalIsolationObserver?.disconnect();
|
|
141
|
-
modalIsolationObserver = null;
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const interactivePath = new Set();
|
|
145
|
-
for (const root of [top.element, ...getOwnedPopupHosts(top)]) {
|
|
146
|
-
let pathNode = root;
|
|
147
|
-
while (pathNode) {
|
|
148
|
-
if (pathNode === document.body)
|
|
149
|
-
break;
|
|
150
|
-
interactivePath.add(pathNode);
|
|
151
|
-
pathNode = pathNode.parentElement;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const inspectedParents = new Set();
|
|
155
|
-
for (const pathNode of interactivePath) {
|
|
156
|
-
const parent = pathNode.parentElement;
|
|
157
|
-
if (!parent || inspectedParents.has(parent))
|
|
158
|
-
continue;
|
|
159
|
-
inspectedParents.add(parent);
|
|
160
|
-
for (const sibling of parent.children) {
|
|
161
|
-
if (sibling instanceof HTMLElement && !interactivePath.has(sibling)) {
|
|
162
|
-
isolateModalBackgroundElement(sibling);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (modalIsolationObserver === null) {
|
|
167
|
-
modalIsolationObserver = new MutationObserver(() => {
|
|
168
|
-
synchronizeModalBackgroundIsolation();
|
|
169
|
-
});
|
|
170
|
-
modalIsolationObserver.observe(document.body, {
|
|
171
|
-
attributes: true,
|
|
172
|
-
attributeFilter: ["data-hjm-popup-owner"],
|
|
173
|
-
childList: true,
|
|
174
|
-
subtree: true,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
function lockBodyScroll() {
|
|
179
|
-
if (bodyLockCount === 0) {
|
|
180
|
-
previousBodyOverflow = document.body.style.overflow;
|
|
181
|
-
document.body.style.overflow = "hidden";
|
|
182
|
-
}
|
|
183
|
-
bodyLockCount += 1;
|
|
184
|
-
return () => {
|
|
185
|
-
bodyLockCount = Math.max(0, bodyLockCount - 1);
|
|
186
|
-
if (bodyLockCount === 0)
|
|
187
|
-
document.body.style.overflow = previousBodyOverflow;
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
function useModalFocus({ active, priority = 0, contentRef, initialFocusRef, returnFocusRef, fallbackReturnRef, onEscape, }) {
|
|
191
|
-
const escapeRef = useRef(onEscape);
|
|
192
|
-
escapeRef.current = onEscape;
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
if (!active)
|
|
195
|
-
return;
|
|
196
|
-
let retryTimer;
|
|
197
|
-
let release;
|
|
198
|
-
let cancelled = false;
|
|
199
|
-
const activate = () => {
|
|
200
|
-
const content = contentRef.current;
|
|
201
|
-
if (!content) {
|
|
202
|
-
retryTimer = setTimeout(activate, 0);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const previouslyFocused = document.activeElement instanceof HTMLElement
|
|
206
|
-
? document.activeElement
|
|
207
|
-
: null;
|
|
208
|
-
const releaseScroll = lockBodyScroll();
|
|
209
|
-
const modal = {
|
|
210
|
-
element: content,
|
|
211
|
-
order: activeModalOrder += 1,
|
|
212
|
-
priority,
|
|
213
|
-
};
|
|
214
|
-
activeModalStack.push(modal);
|
|
215
|
-
synchronizeModalBackgroundIsolation();
|
|
216
|
-
const initial = initialFocusRef?.current ?? getFocusable(content)[0] ?? content;
|
|
217
|
-
if (getTopModal() === modal)
|
|
218
|
-
initial.focus();
|
|
219
|
-
const handleKeyDown = (event) => {
|
|
220
|
-
if (getTopModal() !== modal)
|
|
221
|
-
return;
|
|
222
|
-
if (event.key === "Escape") {
|
|
223
|
-
event.preventDefault();
|
|
224
|
-
escapeRef.current();
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (event.key !== "Tab")
|
|
228
|
-
return;
|
|
229
|
-
const focusable = getModalFocusable(modal);
|
|
230
|
-
if (focusable.length === 0) {
|
|
231
|
-
event.preventDefault();
|
|
232
|
-
content.focus();
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
const first = focusable[0];
|
|
236
|
-
const last = focusable.at(-1);
|
|
237
|
-
const current = document.activeElement;
|
|
238
|
-
if (event.shiftKey && (current === first || !(current instanceof Node) || !modalContainsNode(modal, current))) {
|
|
239
|
-
event.preventDefault();
|
|
240
|
-
last.focus();
|
|
241
|
-
}
|
|
242
|
-
else if (!event.shiftKey && (current === last || !(current instanceof Node) || !modalContainsNode(modal, current))) {
|
|
243
|
-
event.preventDefault();
|
|
244
|
-
first.focus();
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
const handleFocusIn = (event) => {
|
|
248
|
-
if (getTopModal() !== modal)
|
|
249
|
-
return;
|
|
250
|
-
if (event.target instanceof Node && !modalContainsNode(modal, event.target)) {
|
|
251
|
-
(initialFocusRef?.current ?? getFocusable(content)[0] ?? content).focus();
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
document.addEventListener("keydown", handleKeyDown, true);
|
|
255
|
-
document.addEventListener("focusin", handleFocusIn, true);
|
|
256
|
-
release = () => {
|
|
257
|
-
const wasTop = !activeModalStack.some((candidate) => candidate !== modal && candidate.element.isConnected && modalRanksAbove(candidate, modal));
|
|
258
|
-
document.removeEventListener("keydown", handleKeyDown, true);
|
|
259
|
-
document.removeEventListener("focusin", handleFocusIn, true);
|
|
260
|
-
const stackIndex = activeModalStack.lastIndexOf(modal);
|
|
261
|
-
if (stackIndex >= 0)
|
|
262
|
-
activeModalStack.splice(stackIndex, 1);
|
|
263
|
-
synchronizeModalBackgroundIsolation();
|
|
264
|
-
releaseScroll();
|
|
265
|
-
const returnTarget = returnFocusRef?.current ?? fallbackReturnRef?.current ?? previouslyFocused;
|
|
266
|
-
if (wasTop) {
|
|
267
|
-
queueMicrotask(() => {
|
|
268
|
-
const nextTop = getTopModal();
|
|
269
|
-
if (!nextTop) {
|
|
270
|
-
returnTarget?.focus();
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
if (returnTarget && modalContainsNode(nextTop, returnTarget))
|
|
274
|
-
returnTarget.focus();
|
|
275
|
-
else
|
|
276
|
-
(getModalFocusable(nextTop)[0] ?? nextTop.element).focus();
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
if (cancelled)
|
|
281
|
-
release();
|
|
282
|
-
};
|
|
283
|
-
activate();
|
|
284
|
-
return () => {
|
|
285
|
-
cancelled = true;
|
|
286
|
-
if (retryTimer !== undefined)
|
|
287
|
-
clearTimeout(retryTimer);
|
|
288
|
-
release?.();
|
|
289
|
-
};
|
|
290
|
-
}, [active, contentRef, fallbackReturnRef, initialFocusRef, priority, returnFocusRef]);
|
|
291
|
-
}
|
|
292
|
-
function renderTrigger(trigger, triggerRef, open, contentId, popup, onOpen) {
|
|
293
|
-
const props = trigger.props;
|
|
294
|
-
return cloneElement(trigger, {
|
|
295
|
-
ref: composeRefs(props.ref, triggerRef),
|
|
296
|
-
"aria-controls": contentId,
|
|
297
|
-
"aria-expanded": open,
|
|
298
|
-
"aria-haspopup": popup,
|
|
299
|
-
onClick: (event) => {
|
|
300
|
-
props.onClick?.(event);
|
|
301
|
-
if (!event.defaultPrevented && !props.disabled)
|
|
302
|
-
onOpen();
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
export const Dialog = forwardRef(function Dialog({ trigger, title, description, children, footer, size = dialogRecipe.defaults.size, dismissible = dialogRecipe.defaults.dismissible, busy = false, closeLabel, initialFocusRef, returnFocusRef, modalPriority = 0, portalContainer, open: openProp, defaultOpen, onOpenChange, className, }, forwardedRef) {
|
|
9
|
+
import { AnchoredPortal, useAnchoredPopup } from "./portal.js";
|
|
10
|
+
import { useHjmDensityDefault, useTooltipCoordinator } from "./provider.js";
|
|
11
|
+
import { containsEventTarget, getModalLayer, HjmPortal, renderTrigger, useModalFocus, useOpenState, } from "./modal.js";
|
|
12
|
+
export const Dialog = forwardRef(function Dialog({ trigger, title, description, children, footer, size = dialogRecipe.defaults.size, dismissible = dialogRecipe.defaults.dismissible, busy = false, closeLabel, initialFocusRef, returnFocusRef, onDismissComplete, modalPriority = 0, portalContainer, open: openProp, defaultOpen, onOpenChange, className, }, forwardedRef) {
|
|
307
13
|
const modalLayer = getModalLayer(modalPriority);
|
|
308
14
|
const [open, changeOpen] = useOpenState({
|
|
309
15
|
...(openProp === undefined ? {} : { open: openProp }),
|
|
@@ -317,11 +23,52 @@ export const Dialog = forwardRef(function Dialog({ trigger, title, description,
|
|
|
317
23
|
const contentId = `${id}-dialog`;
|
|
318
24
|
const titleId = `${id}-title`;
|
|
319
25
|
const descriptionId = `${id}-description`;
|
|
26
|
+
const dismissReasonRef = useRef(undefined);
|
|
27
|
+
const wasOpenRef = useRef(open);
|
|
28
|
+
const completeRef = useRef(onDismissComplete);
|
|
29
|
+
completeRef.current = onDismissComplete;
|
|
30
|
+
const openRef = useRef(open);
|
|
31
|
+
openRef.current = open;
|
|
320
32
|
const requestClose = (reason) => {
|
|
321
33
|
if (!dismissible || busy)
|
|
322
34
|
return;
|
|
35
|
+
dismissReasonRef.current = reason;
|
|
323
36
|
changeOpen(false, { reason });
|
|
324
37
|
};
|
|
38
|
+
const settle = () => {
|
|
39
|
+
const reason = dismissReasonRef.current ?? "programmatic";
|
|
40
|
+
dismissReasonRef.current = undefined;
|
|
41
|
+
// After the portal is unmounted and `useModalFocus` has restored focus in
|
|
42
|
+
// its own microtask, so the next overlay can open without a 0ms guess.
|
|
43
|
+
queueMicrotask(() => queueMicrotask(() => completeRef.current?.({ reason })));
|
|
44
|
+
};
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (open) {
|
|
47
|
+
wasOpenRef.current = true;
|
|
48
|
+
dismissReasonRef.current = undefined;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!wasOpenRef.current)
|
|
52
|
+
return;
|
|
53
|
+
wasOpenRef.current = false;
|
|
54
|
+
settle();
|
|
55
|
+
}, [open]);
|
|
56
|
+
const epochRef = useRef(0);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const epoch = epochRef.current + 1;
|
|
59
|
+
epochRef.current = epoch;
|
|
60
|
+
return () => {
|
|
61
|
+
queueMicrotask(() => {
|
|
62
|
+
// StrictMode re-runs setup right after its probe cleanup; only a real
|
|
63
|
+
// unmount has no later epoch. An open Dialog torn down that way still
|
|
64
|
+
// settles exactly once.
|
|
65
|
+
if (epochRef.current !== epoch || !openRef.current || !wasOpenRef.current)
|
|
66
|
+
return;
|
|
67
|
+
wasOpenRef.current = false;
|
|
68
|
+
settle();
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
325
72
|
useModalFocus({
|
|
326
73
|
active: open,
|
|
327
74
|
priority: modalPriority,
|
|
@@ -420,7 +167,7 @@ export const AlertDialog = forwardRef(function AlertDialog({ trigger, request, i
|
|
|
420
167
|
changeOpen(true, { reason: "trigger" });
|
|
421
168
|
}), open ? (_jsx(HjmPortal, { ...(portalContainer === undefined ? {} : { container: portalContainer }), children: _jsx("div", { className: "hjm-overlay", "data-kind": "alert-dialog", "data-modal-priority": modalPriority, "data-state": "open", style: { zIndex: modalLayer }, children: _jsxs("div", { ref: composeRefs(contentRef, forwardedRef), id: contentId, role: "alertdialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": `${descriptionId}${error ? ` ${errorId}` : ""}`, "aria-busy": busy || undefined, tabIndex: -1, className: classNames("hjm-alert-dialog", className), "data-hjm-modal-content": "", "data-size": size, "data-tone": tone, "data-state": busy ? "busy" : error ? "error" : "idle", children: [icon ? _jsx("div", { className: "hjm-alert-dialog__icon", "aria-hidden": "true", children: icon }) : null, _jsx("h2", { id: titleId, className: "hjm-alert-dialog__title", children: request.title }), _jsx("p", { id: descriptionId, className: "hjm-alert-dialog__description", children: request.description }), error ? _jsx("p", { id: errorId, className: "hjm-alert-dialog__error", role: "alert", children: error }) : null, _jsxs("div", { className: "hjm-alert-dialog__actions", children: [request.mode === "confirm" ? (_jsx(Button, { ref: cancelRef, tone: "secondary", disabled: busy || closing, onClick: () => cancel("cancel-action"), children: request.cancelLabel })) : null, _jsx(Button, { ref: confirmRef, tone: tone === "danger" ? "danger" : "primary", loading: busy, disabled: closing, onClick: () => void confirm(), children: request.confirmLabel })] })] }) }) })) : null] }));
|
|
422
169
|
});
|
|
423
|
-
export const Sheet = forwardRef(function Sheet({ trigger, title, description, children, footer, placement = sheetRecipe.defaults.placement, busy = false, dismissPolicy, closeLabel, initialFocusRef, returnFocusRef, onDismissComplete, modalPriority = 0, portalContainer, open: openProp, defaultOpen, onOpenChange, className, }, forwardedRef) {
|
|
170
|
+
export const Sheet = forwardRef(function Sheet({ trigger, title, description, children, footer, placement = sheetRecipe.defaults.placement, detents, activeDetent, onDetentChange, detentLabels, busy = false, dismissPolicy, closeLabel, initialFocusRef, returnFocusRef, onDismissComplete, modalPriority = 0, portalContainer, open: openProp, defaultOpen, onOpenChange, className, }, forwardedRef) {
|
|
424
171
|
const modalLayer = getModalLayer(modalPriority);
|
|
425
172
|
const [open, changeOpen] = useOpenState({
|
|
426
173
|
...(openProp === undefined ? {} : { open: openProp }),
|
|
@@ -494,7 +241,14 @@ export const Sheet = forwardRef(function Sheet({ trigger, title, description, ch
|
|
|
494
241
|
return (_jsxs(_Fragment, { children: [trigger === undefined ? null : renderTrigger(trigger, triggerRef, open, contentId, "dialog", () => changeOpen(true, { reason: "trigger" })), open ? (_jsx(HjmPortal, { ...(portalContainer === undefined ? {} : { container: portalContainer }), children: _jsx("div", { className: "hjm-overlay hjm-sheet-positioner", "data-kind": "sheet", "data-modal-priority": modalPriority, "data-placement": placement, "data-state": "open", style: { zIndex: modalLayer }, onMouseDown: (event) => {
|
|
495
242
|
if (event.target === event.currentTarget)
|
|
496
243
|
requestClose("outside");
|
|
497
|
-
}, children: _jsxs("div", { ref: composeRefs(contentRef, forwardedRef), id: contentId, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : undefined, "aria-busy": busy || undefined, tabIndex: -1, className: classNames("hjm-sheet", className), "data-hjm-modal-content": "", "data-placement": placement, "data-has-footer": footer ? true : undefined, "data-state": busy ? "busy" : "idle", children: [
|
|
244
|
+
}, children: _jsxs("div", { ref: composeRefs(contentRef, forwardedRef), id: contentId, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : undefined, "aria-busy": busy || undefined, tabIndex: -1, className: classNames("hjm-sheet", className), "data-hjm-modal-content": "", "data-placement": placement, "data-detent": activeDetent, "data-has-footer": footer ? true : undefined, "data-state": busy ? "busy" : "idle", children: [detents && detentLabels && activeDetent ? (_jsx("div", { className: "hjm-sheet__handle-row", children: _jsx("button", { type: "button", className: "hjm-sheet__handle", "aria-label": resolveNextSheetDetent(detents, activeDetent, "expand") === null
|
|
245
|
+
? detentLabels.collapse
|
|
246
|
+
: detentLabels.expand, onClick: () => {
|
|
247
|
+
const expand = resolveNextSheetDetent(detents, activeDetent, "expand");
|
|
248
|
+
const next = expand ?? resolveNextSheetDetent(detents, activeDetent, "collapse");
|
|
249
|
+
if (next !== null)
|
|
250
|
+
onDetentChange?.(next);
|
|
251
|
+
}, children: _jsx("span", { "aria-hidden": "true", className: "hjm-sheet__handle-bar" }) }) })) : null, _jsxs("header", { className: "hjm-sheet__header", children: [_jsxs("div", { children: [_jsx("h2", { id: titleId, className: "hjm-sheet__title", children: title }), description ? _jsx("p", { id: descriptionId, className: "hjm-sheet__description", children: description }) : null] }), policy.dismissible ? (_jsx("button", { type: "button", className: "hjm-dialog__close", "aria-label": closeLabel, disabled: busy && !policy.dismissWhileBusy, onClick: () => requestClose("close-action"), children: "\u00D7" })) : null] }), children ? _jsx("div", { className: "hjm-sheet__body", children: children }) : null, footer ? _jsx("footer", { className: "hjm-sheet__footer", children: footer }) : null] }) }) })) : null] }));
|
|
498
252
|
});
|
|
499
253
|
export const Tooltip = forwardRef(function Tooltip({ trigger, content, placement, align, pointerOpenDelayMs = tooltipBehaviorDefaults.pointerOpenDelayMs, focusOpenDelayMs = tooltipBehaviorDefaults.focusOpenDelayMs, portalContainer, open: openProp, defaultOpen, onOpenChange, className, }, ref) {
|
|
500
254
|
const descriptor = resolveTooltipDescriptor({
|
|
@@ -683,7 +437,13 @@ function validateMenuItems(items, sections, asyncState) {
|
|
|
683
437
|
}
|
|
684
438
|
}
|
|
685
439
|
export const Menu = forwardRef(function Menu(props, ref) {
|
|
686
|
-
|
|
440
|
+
// Unconditional: `??` on the hook call would break the rules of hooks.
|
|
441
|
+
const densityDefault = useHjmDensityDefault({
|
|
442
|
+
comfortable: menuRecipe.defaults.density,
|
|
443
|
+
compact: "compact",
|
|
444
|
+
});
|
|
445
|
+
const { trigger, label, density: densityProp, align = "start", disabled = false, asyncState = { status: "idle" }, onAction, onActionAfterDismiss, portalContainer, open: openProp, defaultOpen, onOpenChange, className, } = props;
|
|
446
|
+
const density = densityProp ?? densityDefault;
|
|
687
447
|
const sections = props.sections;
|
|
688
448
|
const items = sections === undefined ? props.items : sections.flatMap((section) => section.items);
|
|
689
449
|
if (label.trim().length === 0)
|