@zyncat/ui 0.9.1 → 0.10.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/dist/{chunk-EQ32F2QJ.js → chunk-62P3KD57.js} +5 -5
- package/dist/chunk-62P3KD57.js.map +1 -0
- package/dist/{chunk-W5YM2FV4.js → chunk-FTOAUJL5.js} +9 -6
- package/dist/chunk-FTOAUJL5.js.map +1 -0
- package/dist/{chunk-FLO4A6BI.js → chunk-K5X2GCXU.js} +54 -52
- package/dist/chunk-K5X2GCXU.js.map +1 -0
- package/dist/{chunk-45XJ75GZ.js → chunk-OMHQ4U4J.js} +4 -4
- package/dist/{chunk-45XJ75GZ.js.map → chunk-OMHQ4U4J.js.map} +1 -1
- package/dist/{chunk-GY7JOPUQ.js → chunk-RCONNA7I.js} +5 -5
- package/dist/chunk-RCONNA7I.js.map +1 -0
- package/dist/{chunk-GLT5IKQY.js → chunk-UXONUUPI.js} +9 -5
- package/dist/chunk-UXONUUPI.js.map +1 -0
- package/dist/{chunk-LDC3RIYA.js → chunk-ZCOUPPTI.js} +3 -3
- package/dist/{chunk-LDC3RIYA.js.map → chunk-ZCOUPPTI.js.map} +1 -1
- package/dist/date-field.js +4 -4
- package/dist/date-range-field.js +6 -6
- package/dist/datetime-field.js +4 -4
- package/dist/dialog.css +3 -0
- package/dist/dialog.d.ts +4 -1
- package/dist/dialog.js +7 -5
- package/dist/dialog.js.map +1 -1
- package/dist/modal.css +9 -0
- package/dist/modal.d.ts +35 -0
- package/dist/modal.js +63 -0
- package/dist/modal.js.map +1 -0
- package/dist/multi-select.js +3 -3
- package/dist/overlay.css +3 -0
- package/dist/popover.d.ts +10 -3
- package/dist/popover.js +4 -4
- package/dist/select.js +3 -3
- package/dist/sheet.d.ts +4 -1
- package/dist/sheet.js +4 -4
- package/llms.txt +28 -5
- package/package.json +7 -2
- package/src/components/composites/dialog/Dialog.tsx +30 -23
- package/src/components/composites/dialog/dialog.css +3 -0
- package/src/components/composites/modal/Modal.tsx +95 -0
- package/src/components/composites/modal/modal.css +9 -0
- package/src/components/composites/popover/Popover.tsx +12 -3
- package/src/components/composites/sheet/Sheet.tsx +10 -1
- package/src/components/internal/overlay/layer.tsx +3 -3
- package/src/components/internal/overlay/modal.tsx +45 -36
- package/src/components/internal/overlay/overlay.css +3 -0
- package/src/components/internal/overlay/position.ts +8 -3
- package/dist/chunk-EQ32F2QJ.js.map +0 -1
- package/dist/chunk-FLO4A6BI.js.map +0 -1
- package/dist/chunk-GLT5IKQY.js.map +0 -1
- package/dist/chunk-GY7JOPUQ.js.map +0 -1
- package/dist/chunk-W5YM2FV4.js.map +0 -1
|
@@ -40,6 +40,9 @@ export interface DialogProps {
|
|
|
40
40
|
icon?: ReactNode;
|
|
41
41
|
/** Close button + backdrop/Esc dismissal. Default true. */
|
|
42
42
|
dismissible?: boolean;
|
|
43
|
+
/** Center inside this element instead of the viewport - it must be positioned (`position: relative` or similar).
|
|
44
|
+
* Scrim, scroll lock and inert scope to it; the rest of the page stays interactive. */
|
|
45
|
+
container?: HTMLElement | null;
|
|
43
46
|
/** Action row - a node, or a render fn `(close) => node` so uncontrolled dialogs can dismiss. */
|
|
44
47
|
footer?: ReactNode | ((close: () => void) => ReactNode);
|
|
45
48
|
/** Dialog body - scrolls when tall; its scroll edges flag the header lift + footer divider. */
|
|
@@ -100,30 +103,32 @@ function DialogSurface({
|
|
|
100
103
|
aria-labelledby={title ? titleId : undefined}
|
|
101
104
|
aria-describedby={description ? descId : undefined}
|
|
102
105
|
>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<div className="dialog__heading">
|
|
110
|
-
{title && (
|
|
111
|
-
<h2 className="dialog__title" id={titleId}>
|
|
112
|
-
{title}
|
|
113
|
-
</h2>
|
|
106
|
+
{(icon || title || description || dismissible) && (
|
|
107
|
+
<header className="dialog__header">
|
|
108
|
+
{icon && (
|
|
109
|
+
<span className="dialog__icon">
|
|
110
|
+
<IconSlot size="md">{icon}</IconSlot>
|
|
111
|
+
</span>
|
|
114
112
|
)}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
{
|
|
118
|
-
|
|
113
|
+
<div className="dialog__heading">
|
|
114
|
+
{title && (
|
|
115
|
+
<h2 className="dialog__title" id={titleId}>
|
|
116
|
+
{title}
|
|
117
|
+
</h2>
|
|
118
|
+
)}
|
|
119
|
+
{description && (
|
|
120
|
+
<p className="dialog__desc" id={descId}>
|
|
121
|
+
{description}
|
|
122
|
+
</p>
|
|
123
|
+
)}
|
|
124
|
+
</div>
|
|
125
|
+
{dismissible && (
|
|
126
|
+
<button type="button" className="dialog__close" aria-label="Close dialog" onClick={requestClose}>
|
|
127
|
+
<Icon name="x" size="sm" weight="bold" />
|
|
128
|
+
</button>
|
|
119
129
|
)}
|
|
120
|
-
</
|
|
121
|
-
|
|
122
|
-
<button type="button" className="dialog__close" aria-label="Close dialog" onClick={requestClose}>
|
|
123
|
-
<Icon name="x" size="sm" weight="bold" />
|
|
124
|
-
</button>
|
|
125
|
-
)}
|
|
126
|
-
</header>
|
|
130
|
+
</header>
|
|
131
|
+
)}
|
|
127
132
|
|
|
128
133
|
<div className="dialog__body" ref={bodyRef}>
|
|
129
134
|
{children}
|
|
@@ -145,6 +150,7 @@ export function Dialog({
|
|
|
145
150
|
tone = 'default',
|
|
146
151
|
icon = null,
|
|
147
152
|
dismissible = true,
|
|
153
|
+
container = null,
|
|
148
154
|
footer = null,
|
|
149
155
|
children,
|
|
150
156
|
id,
|
|
@@ -162,7 +168,7 @@ export function Dialog({
|
|
|
162
168
|
return (
|
|
163
169
|
<Fragment>
|
|
164
170
|
{ovCloneTrigger(trigger, { open, onPress: () => setOpen(true), panelId, haspopup: 'dialog', triggerRef })}
|
|
165
|
-
<OverlayPortal>
|
|
171
|
+
<OverlayPortal container={container}>
|
|
166
172
|
<Presence>
|
|
167
173
|
{open && (
|
|
168
174
|
<ModalShell
|
|
@@ -173,6 +179,7 @@ export function Dialog({
|
|
|
173
179
|
panelClass="overlay-panel--dialog"
|
|
174
180
|
panelId={panelId}
|
|
175
181
|
dismissible={dismissible}
|
|
182
|
+
container={container}
|
|
176
183
|
requestClose={close}
|
|
177
184
|
>
|
|
178
185
|
<DialogSurface
|
|
@@ -136,6 +136,9 @@
|
|
|
136
136
|
color: var(--text-body);
|
|
137
137
|
font: var(--type-body);
|
|
138
138
|
}
|
|
139
|
+
.dialog__body:first-child {
|
|
140
|
+
padding-top: var(--space-5);
|
|
141
|
+
}
|
|
139
142
|
.dialog:has(.dialog__icon) .dialog__body {
|
|
140
143
|
padding-left: calc(var(--space-5) + 2.25rem + var(--space-3));
|
|
141
144
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import './modal.css';
|
|
4
|
+
import { Fragment, useId, useRef, type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
|
|
5
|
+
import { Presence } from '../../../motion/presence';
|
|
6
|
+
import { resolveMotionTiming } from '../../../motion/motion-timing';
|
|
7
|
+
import type { DisableableAnimation } from '../../../motion/timing';
|
|
8
|
+
import { useControllable } from '../../internal/hooks/use-controllable';
|
|
9
|
+
import { ovCloneTrigger, OverlayPortal } from '../../internal/overlay/layer';
|
|
10
|
+
import { ModalShell } from '../../internal/overlay/modal';
|
|
11
|
+
import type { DataAttributes } from '../../../dom-props';
|
|
12
|
+
|
|
13
|
+
const MODAL_TIMING = {
|
|
14
|
+
open: { duration: 'slow', ease: 'entrance' },
|
|
15
|
+
close: { duration: 'base', ease: 'standard' },
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export interface ModalProps {
|
|
19
|
+
/** Controlled open state. Omit to stay uncontrolled. */
|
|
20
|
+
open?: boolean;
|
|
21
|
+
/** Initial state when uncontrolled. Default false. */
|
|
22
|
+
defaultOpen?: boolean;
|
|
23
|
+
/** Fires whenever the open state changes. Pair with `open` for controlled use. */
|
|
24
|
+
onOpenChange?: (open: boolean) => void;
|
|
25
|
+
|
|
26
|
+
/** Cloned to open the modal on click. */
|
|
27
|
+
trigger?: ReactElement | null;
|
|
28
|
+
|
|
29
|
+
/** Scrim/Esc dismissal. Default true. */
|
|
30
|
+
dismissible?: boolean;
|
|
31
|
+
|
|
32
|
+
/** Mount inside this element instead of the viewport - it must be positioned (`position: relative` or similar).
|
|
33
|
+
* Scrim, scroll lock and inert scope to it; the rest of the page stays interactive. */
|
|
34
|
+
container?: HTMLElement | null;
|
|
35
|
+
|
|
36
|
+
/** Base id for the panel; drives the trigger's `aria-controls`. Auto-generated when omitted. */
|
|
37
|
+
id?: string;
|
|
38
|
+
/** Standard attributes (className, style, data-*, ...) forwarded to the panel - this is where your paint goes.
|
|
39
|
+
* Centered by default; `width: 100%; height: 100%` makes it full-bleed. */
|
|
40
|
+
htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
41
|
+
/** Standard attributes forwarded to the layer behind the panel - set `--bg-overlay` here to retint the scrim. */
|
|
42
|
+
layerProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
43
|
+
/** Open/close timing - motion tokens only, or `null` to disable. @default open 'slow'/'entrance', close 'base'/'standard' */
|
|
44
|
+
animation?: DisableableAnimation;
|
|
45
|
+
/** The ENTIRE surface - paint AND semantics (`role="dialog"`, a label). Nothing is painted for you. */
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function Modal({
|
|
50
|
+
open: controlledOpen,
|
|
51
|
+
defaultOpen = false,
|
|
52
|
+
onOpenChange,
|
|
53
|
+
trigger = null,
|
|
54
|
+
dismissible = true,
|
|
55
|
+
container = null,
|
|
56
|
+
id,
|
|
57
|
+
htmlProps,
|
|
58
|
+
layerProps,
|
|
59
|
+
animation,
|
|
60
|
+
children,
|
|
61
|
+
}: ModalProps) {
|
|
62
|
+
const [open, setOpen] = useControllable(controlledOpen, defaultOpen, onOpenChange);
|
|
63
|
+
const triggerRef = useRef<HTMLElement>(null);
|
|
64
|
+
const autoId = useId();
|
|
65
|
+
const panelId = id || 'modal-' + autoId;
|
|
66
|
+
const close = () => setOpen(false);
|
|
67
|
+
const timings = resolveMotionTiming(animation, MODAL_TIMING);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Fragment>
|
|
71
|
+
{ovCloneTrigger(trigger, { open, onPress: () => setOpen(true), panelId, haspopup: 'dialog', triggerRef })}
|
|
72
|
+
<OverlayPortal container={container}>
|
|
73
|
+
<Presence>
|
|
74
|
+
{open && (
|
|
75
|
+
<ModalShell
|
|
76
|
+
key="modal"
|
|
77
|
+
animate={{ opacity: [0, 1], scale: [0.98, 1], timing: timings.open }}
|
|
78
|
+
exit={{ opacity: [0], scale: [0.98], timing: timings.close }}
|
|
79
|
+
layerClass="overlay-layer--modal"
|
|
80
|
+
panelClass="overlay-panel--modal"
|
|
81
|
+
panelId={panelId}
|
|
82
|
+
dismissible={dismissible}
|
|
83
|
+
container={container}
|
|
84
|
+
requestClose={close}
|
|
85
|
+
panelProps={htmlProps}
|
|
86
|
+
layerProps={layerProps}
|
|
87
|
+
>
|
|
88
|
+
{children}
|
|
89
|
+
</ModalShell>
|
|
90
|
+
)}
|
|
91
|
+
</Presence>
|
|
92
|
+
</OverlayPortal>
|
|
93
|
+
</Fragment>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* modal.css - the unpainted modal surface: the shell layer centers the panel and
|
|
2
|
+
constrains it to the layer box. Everything else is the consumer's paint. */
|
|
3
|
+
.overlay-layer--modal {
|
|
4
|
+
place-items: center;
|
|
5
|
+
}
|
|
6
|
+
.overlay-panel--modal {
|
|
7
|
+
max-width: 100%;
|
|
8
|
+
max-height: 100%;
|
|
9
|
+
}
|
|
@@ -9,10 +9,12 @@ import type { DisableableAnimation } from '../../../motion/timing';
|
|
|
9
9
|
import { useControllable } from '../../internal/hooks/use-controllable';
|
|
10
10
|
import { ovCloneTrigger, useOverlayEntry, useOutsidePress, OverlayPortal } from '../../internal/overlay/layer';
|
|
11
11
|
import { useReturnFocus } from '../../internal/overlay/focus';
|
|
12
|
-
import { useAnchorPosition } from '../../internal/overlay/position';
|
|
12
|
+
import { useAnchorPosition, type VirtualAnchor } from '../../internal/overlay/position';
|
|
13
13
|
import type { DataAttributes } from '../../../dom-props';
|
|
14
14
|
import { cx } from '../../internal/utils/cx';
|
|
15
15
|
|
|
16
|
+
export type { VirtualAnchor };
|
|
17
|
+
|
|
16
18
|
const POPOVER_TIMING = {
|
|
17
19
|
open: { duration: 'base', ease: 'entrance' },
|
|
18
20
|
close: { duration: 'fast', ease: 'exit' },
|
|
@@ -26,8 +28,11 @@ export interface PopoverProps {
|
|
|
26
28
|
/** Fires whenever the open state changes. Pair with `open` for controlled use. */
|
|
27
29
|
onOpenChange?: (open: boolean) => void;
|
|
28
30
|
|
|
29
|
-
/** Cloned to toggle the panel
|
|
31
|
+
/** Cloned to toggle the panel, and used as the anchor unless `anchor` is set. */
|
|
30
32
|
trigger?: ReactElement | null;
|
|
33
|
+
/** Anchor to an arbitrary rect instead of the trigger - any `{ getBoundingClientRect() }`, which an element also satisfies.
|
|
34
|
+
* Drive `open` yourself; pass a new object to re-place a moving anchor. */
|
|
35
|
+
anchor?: VirtualAnchor | null;
|
|
31
36
|
|
|
32
37
|
/** Preferred side; flips to the opposite side when cramped. Default 'bottom'. */
|
|
33
38
|
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
@@ -56,6 +61,7 @@ function PopoverPanel({
|
|
|
56
61
|
arrow,
|
|
57
62
|
dismissible,
|
|
58
63
|
requestClose,
|
|
64
|
+
anchor,
|
|
59
65
|
triggerRef,
|
|
60
66
|
htmlProps,
|
|
61
67
|
animate,
|
|
@@ -68,6 +74,7 @@ function PopoverPanel({
|
|
|
68
74
|
arrow: boolean;
|
|
69
75
|
dismissible: boolean;
|
|
70
76
|
requestClose: () => void;
|
|
77
|
+
anchor?: VirtualAnchor | null;
|
|
71
78
|
triggerRef: RefObject<HTMLElement>;
|
|
72
79
|
htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
73
80
|
children: ReactNode;
|
|
@@ -76,7 +83,7 @@ function PopoverPanel({
|
|
|
76
83
|
const entry = useOverlayEntry({ nodeRef: panelRef, dismissible, requestClose });
|
|
77
84
|
useMotion(panelRef, { animate, exit });
|
|
78
85
|
useReturnFocus(panelRef);
|
|
79
|
-
useAnchorPosition({ side, align, arrow, triggerRef, panelRef });
|
|
86
|
+
useAnchorPosition({ side, align, arrow, anchor, triggerRef, panelRef });
|
|
80
87
|
useOutsidePress({ entry, refs: [triggerRef, panelRef], enabled: dismissible, onPress: requestClose });
|
|
81
88
|
return (
|
|
82
89
|
<div
|
|
@@ -96,6 +103,7 @@ export function Popover({
|
|
|
96
103
|
defaultOpen = false,
|
|
97
104
|
onOpenChange,
|
|
98
105
|
trigger = null,
|
|
106
|
+
anchor = null,
|
|
99
107
|
side = 'bottom',
|
|
100
108
|
align = 'start',
|
|
101
109
|
arrow = false,
|
|
@@ -128,6 +136,7 @@ export function Popover({
|
|
|
128
136
|
arrow={arrow}
|
|
129
137
|
dismissible={dismissible}
|
|
130
138
|
requestClose={close}
|
|
139
|
+
anchor={anchor}
|
|
131
140
|
triggerRef={triggerRef}
|
|
132
141
|
htmlProps={htmlProps}
|
|
133
142
|
>
|
|
@@ -35,6 +35,10 @@ export interface SheetProps {
|
|
|
35
35
|
/** Scrim/Esc dismissal + drag-to-edge. Default true. */
|
|
36
36
|
dismissible?: boolean;
|
|
37
37
|
|
|
38
|
+
/** Dock inside this element instead of the viewport - it must be positioned (`position: relative` or similar).
|
|
39
|
+
* Scrim, scroll lock and inert scope to it; the rest of the page stays interactive. */
|
|
40
|
+
container?: HTMLElement | null;
|
|
41
|
+
|
|
38
42
|
/** Base id for the panel; drives the trigger's `aria-controls`. Auto-generated when omitted. */
|
|
39
43
|
id?: string;
|
|
40
44
|
/** Standard attributes (className, style, data-*, ...) forwarded to the sheet panel. */
|
|
@@ -54,6 +58,7 @@ function SheetShell({
|
|
|
54
58
|
side,
|
|
55
59
|
panelId,
|
|
56
60
|
dismissible,
|
|
61
|
+
container,
|
|
57
62
|
requestClose,
|
|
58
63
|
htmlProps,
|
|
59
64
|
animate,
|
|
@@ -63,6 +68,7 @@ function SheetShell({
|
|
|
63
68
|
side: 'right' | 'bottom';
|
|
64
69
|
panelId: string;
|
|
65
70
|
dismissible: boolean;
|
|
71
|
+
container?: HTMLElement | null;
|
|
66
72
|
requestClose: () => void;
|
|
67
73
|
htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;
|
|
68
74
|
children: ReactNode;
|
|
@@ -77,6 +83,7 @@ function SheetShell({
|
|
|
77
83
|
panelClass={'overlay-panel--sheet-' + side}
|
|
78
84
|
panelId={panelId}
|
|
79
85
|
dismissible={dismissible}
|
|
86
|
+
container={container}
|
|
80
87
|
requestClose={requestClose}
|
|
81
88
|
panelRef={panelRef}
|
|
82
89
|
panelProps={{ ...htmlProps, ...dragProps }}
|
|
@@ -93,6 +100,7 @@ export function Sheet({
|
|
|
93
100
|
trigger = null,
|
|
94
101
|
side = 'right',
|
|
95
102
|
dismissible = true,
|
|
103
|
+
container = null,
|
|
96
104
|
id,
|
|
97
105
|
htmlProps,
|
|
98
106
|
animation,
|
|
@@ -108,7 +116,7 @@ export function Sheet({
|
|
|
108
116
|
return (
|
|
109
117
|
<Fragment>
|
|
110
118
|
{ovCloneTrigger(trigger, { open, onPress: () => setOpen(true), panelId, haspopup: 'dialog', triggerRef })}
|
|
111
|
-
<OverlayPortal>
|
|
119
|
+
<OverlayPortal container={container}>
|
|
112
120
|
<Presence>
|
|
113
121
|
{open && (
|
|
114
122
|
<SheetShell
|
|
@@ -122,6 +130,7 @@ export function Sheet({
|
|
|
122
130
|
side={side}
|
|
123
131
|
panelId={panelId}
|
|
124
132
|
dismissible={dismissible}
|
|
133
|
+
container={container}
|
|
125
134
|
requestClose={close}
|
|
126
135
|
htmlProps={htmlProps}
|
|
127
136
|
>
|
|
@@ -129,7 +129,7 @@ function ovCloneTrigger(
|
|
|
129
129
|
} as Partial<typeof trigger.props>);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
function OverlayPortal({ children }: { children: ReactNode }) {
|
|
132
|
+
function OverlayPortal({ container, children }: { container?: HTMLElement | null; children: ReactNode }) {
|
|
133
133
|
const hostRef = useRef<HTMLDivElement | null>(null);
|
|
134
134
|
if (typeof document !== 'undefined' && !hostRef.current) {
|
|
135
135
|
hostRef.current = document.createElement('div');
|
|
@@ -138,9 +138,9 @@ function OverlayPortal({ children }: { children: ReactNode }) {
|
|
|
138
138
|
useLayoutEffect(() => {
|
|
139
139
|
const el = hostRef.current;
|
|
140
140
|
if (!el) return;
|
|
141
|
-
document.body.appendChild(el);
|
|
141
|
+
(container ?? document.body).appendChild(el);
|
|
142
142
|
return () => el.remove();
|
|
143
|
-
}, []);
|
|
143
|
+
}, [container]);
|
|
144
144
|
if (!hostRef.current) return null;
|
|
145
145
|
return createPortal(children, hostRef.current);
|
|
146
146
|
}
|
|
@@ -11,49 +11,51 @@ import { cx } from '../utils/cx';
|
|
|
11
11
|
|
|
12
12
|
const SM = UIMotion;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
return () => {
|
|
28
|
-
if (--ovLocks === 0) {
|
|
29
|
-
document.body.style.overflow = ovSavedOverflow;
|
|
30
|
-
document.body.style.paddingRight = ovSavedPad;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}, []);
|
|
14
|
+
interface OvScopeLock {
|
|
15
|
+
depth: number;
|
|
16
|
+
overflow: string;
|
|
17
|
+
paddingRight: string;
|
|
18
|
+
inerted: HTMLElement[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ovScopeLocks = new Map<HTMLElement, OvScopeLock>();
|
|
22
|
+
|
|
23
|
+
function ovGutter(scope: HTMLElement, cs: CSSStyleDeclaration): number {
|
|
24
|
+
if (scope === document.body) return window.innerWidth - document.documentElement.clientWidth;
|
|
25
|
+
return scope.offsetWidth - scope.clientWidth - parseFloat(cs.borderLeftWidth) - parseFloat(cs.borderRightWidth);
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
const ovInerted = new Set<HTMLElement>();
|
|
38
|
-
function useInertOutside() {
|
|
28
|
+
function useScopeLock(container?: HTMLElement | null) {
|
|
39
29
|
useEffect(() => {
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
const scope = container ?? document.body;
|
|
31
|
+
const lock: OvScopeLock = ovScopeLocks.get(scope) ?? {
|
|
32
|
+
depth: 0,
|
|
33
|
+
overflow: scope.style.overflow,
|
|
34
|
+
paddingRight: scope.style.paddingRight,
|
|
35
|
+
inerted: [],
|
|
36
|
+
};
|
|
37
|
+
ovScopeLocks.set(scope, lock);
|
|
38
|
+
|
|
39
|
+
if (++lock.depth === 1) {
|
|
40
|
+
const cs = getComputedStyle(scope);
|
|
41
|
+
const gutter = ovGutter(scope, cs);
|
|
42
|
+
scope.style.overflow = 'hidden';
|
|
43
|
+
if (gutter > 0) scope.style.paddingRight = (parseFloat(cs.paddingRight) || 0) + gutter + 'px';
|
|
44
|
+
for (const el of Array.from(scope.children) as HTMLElement[]) {
|
|
42
45
|
if (el.hasAttribute('data-overlay-root') || el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.inert)
|
|
43
46
|
continue;
|
|
44
47
|
el.inert = true;
|
|
45
|
-
|
|
48
|
+
lock.inerted.push(el);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
return () => {
|
|
49
|
-
if (--
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
52
|
+
if (--lock.depth > 0) return;
|
|
53
|
+
scope.style.overflow = lock.overflow;
|
|
54
|
+
scope.style.paddingRight = lock.paddingRight;
|
|
55
|
+
for (const el of lock.inerted) el.inert = false;
|
|
56
|
+
ovScopeLocks.delete(scope);
|
|
55
57
|
};
|
|
56
|
-
}, []);
|
|
58
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
const ovScrimTiming = {
|
|
@@ -92,8 +94,10 @@ export function ModalShell({
|
|
|
92
94
|
panelId,
|
|
93
95
|
dismissible,
|
|
94
96
|
requestClose,
|
|
97
|
+
container,
|
|
95
98
|
panelRef: externalPanelRef = null,
|
|
96
99
|
panelProps = {},
|
|
100
|
+
layerProps = {},
|
|
97
101
|
animate: animateSpec,
|
|
98
102
|
exit,
|
|
99
103
|
children,
|
|
@@ -103,8 +107,10 @@ export function ModalShell({
|
|
|
103
107
|
panelId: string;
|
|
104
108
|
dismissible: boolean;
|
|
105
109
|
requestClose: () => void;
|
|
110
|
+
container?: HTMLElement | null;
|
|
106
111
|
panelRef?: RefObject<HTMLElement> | null;
|
|
107
112
|
panelProps?: Record<string, any>;
|
|
113
|
+
layerProps?: Record<string, any>;
|
|
108
114
|
children: ReactNode;
|
|
109
115
|
} & MotionSpecs) {
|
|
110
116
|
const layerRef = useRef<HTMLDivElement>(null);
|
|
@@ -120,12 +126,15 @@ export function ModalShell({
|
|
|
120
126
|
};
|
|
121
127
|
|
|
122
128
|
useMotion(layerRef, { animate: withScrim('open', animateSpec), exit: withScrim('close', exit) });
|
|
123
|
-
|
|
124
|
-
useInertOutside();
|
|
129
|
+
useScopeLock(container);
|
|
125
130
|
useReturnFocus(layerRef);
|
|
126
131
|
useFocusTrap({ panelRef, entry });
|
|
127
132
|
return (
|
|
128
|
-
<div
|
|
133
|
+
<div
|
|
134
|
+
{...layerProps}
|
|
135
|
+
ref={layerRef}
|
|
136
|
+
className={cx('overlay-layer', container && 'overlay-layer--scoped', layerClass, layerProps.className)}
|
|
137
|
+
>
|
|
129
138
|
<OverlayScrim dismissible={dismissible} onPress={requestClose} scrimRef={scrimRef} />
|
|
130
139
|
<div
|
|
131
140
|
id={panelId}
|
|
@@ -3,18 +3,23 @@
|
|
|
3
3
|
import { useLayoutEffect, type RefObject } from 'react';
|
|
4
4
|
import { tokenPx } from '../utils/token-px';
|
|
5
5
|
|
|
6
|
+
export interface VirtualAnchor {
|
|
7
|
+
getBoundingClientRect(): DOMRect;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
interface UseAnchorPositionProps {
|
|
7
11
|
side: 'top' | 'bottom' | 'left' | 'right';
|
|
8
12
|
align: 'start' | 'center' | 'end';
|
|
9
13
|
arrow: boolean;
|
|
14
|
+
anchor?: VirtualAnchor | null;
|
|
10
15
|
triggerRef: RefObject<HTMLElement>;
|
|
11
16
|
panelRef: RefObject<HTMLElement>;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
export function useAnchorPosition({ side, align, arrow, triggerRef, panelRef }: UseAnchorPositionProps) {
|
|
19
|
+
export function useAnchorPosition({ side, align, arrow, anchor, triggerRef, panelRef }: UseAnchorPositionProps) {
|
|
15
20
|
useLayoutEffect(() => {
|
|
16
21
|
const place = () => {
|
|
17
|
-
const t = triggerRef.current;
|
|
22
|
+
const t = anchor ?? triggerRef.current;
|
|
18
23
|
const p = panelRef.current;
|
|
19
24
|
if (!t || !p) return;
|
|
20
25
|
|
|
@@ -99,5 +104,5 @@ export function useAnchorPosition({ side, align, arrow, triggerRef, panelRef }:
|
|
|
99
104
|
window.removeEventListener('resize', place);
|
|
100
105
|
ro.disconnect();
|
|
101
106
|
};
|
|
102
|
-
}, [side, align, arrow]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
107
|
+
}, [side, align, arrow, anchor]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
103
108
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/internal/overlay/position.ts"],"names":[],"mappings":"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","file":"chunk-EQ32F2QJ.js","sourcesContent":["'use client';\n\nimport { useLayoutEffect, type RefObject } from 'react';\nimport { tokenPx } from '../utils/token-px';\n\ninterface UseAnchorPositionProps {\n side: 'top' | 'bottom' | 'left' | 'right';\n align: 'start' | 'center' | 'end';\n arrow: boolean;\n triggerRef: RefObject<HTMLElement>;\n panelRef: RefObject<HTMLElement>;\n}\n\nexport function useAnchorPosition({ side, align, arrow, triggerRef, panelRef }: UseAnchorPositionProps) {\n useLayoutEffect(() => {\n const place = () => {\n const t = triggerRef.current;\n const p = panelRef.current;\n if (!t || !p) return;\n\n const r = t.getBoundingClientRect();\n const pw = p.offsetWidth;\n const ph = p.offsetHeight;\n\n const edge = tokenPx('--space-2');\n\n const gap = arrow ? tokenPx('--space-2') + 3 : 0;\n\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n\n const room: Record<string, number> = { top: r.top, bottom: vh - r.bottom, left: r.left, right: vw - r.right };\n\n const opposite: Record<string, 'top' | 'bottom' | 'left' | 'right'> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n };\n\n const vertical = side === 'top' || side === 'bottom';\n\n let s = side;\n if (room[s] < (vertical ? ph : pw) + gap && room[opposite[s]] > room[s]) {\n s = opposite[s];\n }\n\n let x: number;\n let y: number;\n\n if (vertical) {\n y = s === 'top' ? r.top - ph - gap : r.bottom + gap;\n\n x = align === 'start' ? r.left : align === 'end' ? r.right - pw : r.left + (r.width - pw) / 2;\n\n x = Math.min(Math.max(x, edge), vw - pw - edge);\n\n y = Math.max(Math.min(y, vh - ph - edge), edge);\n } else {\n x = s === 'left' ? r.left - pw - gap : r.right + gap;\n\n y = align === 'start' ? r.top : align === 'end' ? r.bottom - ph : r.top + (r.height - ph) / 2;\n\n y = Math.min(Math.max(y, edge), vh - ph - edge);\n x = Math.max(Math.min(x, vw - pw - edge), edge);\n }\n\n p.style.left = Math.round(x) + 'px';\n p.style.top = Math.round(y) + 'px';\n\n p.setAttribute('data-side', s);\n p.setAttribute('data-align', align);\n\n if (arrow) {\n if (vertical) {\n p.style.setProperty(\n '--overlay-arrow-x',\n Math.round(Math.min(Math.max(r.left + r.width / 2 - x, 12), pw - 12)) + 'px',\n );\n } else {\n p.style.setProperty(\n '--overlay-arrow-y',\n Math.round(Math.min(Math.max(r.top + r.height / 2 - y, 12), ph - 12)) + 'px',\n );\n }\n }\n };\n\n place();\n\n window.addEventListener('scroll', place, true);\n window.addEventListener('resize', place);\n\n const ro = new ResizeObserver(place);\n ro.observe(panelRef.current!);\n\n return () => {\n window.removeEventListener('scroll', place, true);\n window.removeEventListener('resize', place);\n ro.disconnect();\n };\n }, [side, align, arrow]); // eslint-disable-line react-hooks/exhaustive-deps\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/internal/overlay/modal.tsx"],"names":[],"mappings":"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","file":"chunk-FLO4A6BI.js","sourcesContent":["'use client';\n\nimport './overlay.css';\nimport { useEffect, useRef, type ReactNode, type RefObject } from 'react';\nimport { animate, type Layer, type Playback } from '../../../engine';\nimport { useMotion, run, type MotionSpec, type MotionSpecs } from '../../../motion/use-motion';\nimport { UIMotion } from '../../../tokens/motion-tokens';\nimport { useOverlayEntry } from './layer';\nimport { useReturnFocus, useFocusTrap } from './focus';\nimport { cx } from '../utils/cx';\n\nconst SM = UIMotion;\n\nlet ovLocks = 0;\nlet ovSavedOverflow = '';\nlet ovSavedPad = '';\nfunction useScrollLock() {\n useEffect(() => {\n if (++ovLocks === 1) {\n const body = document.body;\n const gutter = window.innerWidth - document.documentElement.clientWidth;\n ovSavedOverflow = body.style.overflow;\n ovSavedPad = body.style.paddingRight;\n body.style.overflow = 'hidden';\n if (gutter > 0) body.style.paddingRight = (parseFloat(getComputedStyle(body).paddingRight) || 0) + gutter + 'px';\n }\n return () => {\n if (--ovLocks === 0) {\n document.body.style.overflow = ovSavedOverflow;\n document.body.style.paddingRight = ovSavedPad;\n }\n };\n }, []);\n}\n\nlet ovInertCount = 0;\nconst ovInerted = new Set<HTMLElement>();\nfunction useInertOutside() {\n useEffect(() => {\n if (++ovInertCount === 1) {\n for (const el of Array.from(document.body.children) as HTMLElement[]) {\n if (el.hasAttribute('data-overlay-root') || el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.inert)\n continue;\n el.inert = true;\n ovInerted.add(el);\n }\n }\n return () => {\n if (--ovInertCount === 0) {\n ovInerted.forEach((el) => {\n el.inert = false;\n });\n ovInerted.clear();\n }\n };\n }, []);\n}\n\nconst ovScrimTiming = {\n open: { duration: SM.dur.slow, ease: SM.ease.entrance },\n close: { duration: SM.dur.base, ease: SM.ease.standard },\n};\n\nfunction OverlayScrim({\n dismissible,\n onPress,\n scrimRef,\n}: {\n dismissible: boolean;\n onPress: () => void;\n scrimRef: RefObject<HTMLDivElement | null>;\n}) {\n const down = useRef(false);\n return (\n <div\n ref={scrimRef}\n className=\"overlay-scrim\"\n aria-hidden=\"true\"\n onPointerDown={(e) => {\n down.current = e.target === e.currentTarget;\n }}\n onClick={(e) => {\n if (dismissible && down.current && e.target === e.currentTarget) onPress();\n }}\n ></div>\n );\n}\n\nexport function ModalShell({\n layerClass,\n panelClass,\n panelId,\n dismissible,\n requestClose,\n panelRef: externalPanelRef = null,\n panelProps = {},\n animate: animateSpec,\n exit,\n children,\n}: {\n layerClass: string;\n panelClass: string;\n panelId: string;\n dismissible: boolean;\n requestClose: () => void;\n panelRef?: RefObject<HTMLElement> | null;\n panelProps?: Record<string, any>;\n children: ReactNode;\n} & MotionSpecs) {\n const layerRef = useRef<HTMLDivElement>(null);\n const scrimRef = useRef<HTMLDivElement>(null);\n const internalPanelRef = useRef<HTMLElement>(null);\n const panelRef = externalPanelRef || internalPanelRef;\n const entry = useOverlayEntry({ nodeRef: layerRef, dismissible, requestClose });\n\n const withScrim = (dir: 'open' | 'close', panelSpec: MotionSpec | undefined) => (): Playback[] => {\n const fade: Layer = { opacity: dir === 'open' ? [0, 1] : [0], timing: ovScrimTiming[dir] };\n const plays = scrimRef.current ? [animate(scrimRef.current, fade)] : [];\n return panelRef.current ? plays.concat(run(panelSpec, panelRef.current)) : plays;\n };\n\n useMotion(layerRef, { animate: withScrim('open', animateSpec), exit: withScrim('close', exit) });\n useScrollLock();\n useInertOutside();\n useReturnFocus(layerRef);\n useFocusTrap({ panelRef, entry });\n return (\n <div ref={layerRef} className={'overlay-layer ' + layerClass}>\n <OverlayScrim dismissible={dismissible} onPress={requestClose} scrimRef={scrimRef} />\n <div\n id={panelId}\n tabIndex={-1}\n {...panelProps}\n ref={panelRef as RefObject<HTMLDivElement>}\n className={cx('overlay-panel', panelClass, panelProps.className)}\n >\n {children}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/composites/sheet/use-sheet-drag.ts","../src/components/composites/sheet/Sheet.tsx"],"names":["animate","useRef"],"mappings":"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","file":"chunk-GLT5IKQY.js","sourcesContent":["'use client';\n\nimport { useEffect, useRef, type PointerEvent as ReactPointerEvent, type RefObject } from 'react';\nimport { animate, startDrag, type PanInfo } from '../../../engine';\nimport { UIMotion } from '../../../tokens/motion-tokens';\n\nconst SM = UIMotion;\n\nconst DISMISS_RATIO = 0.4;\nconst DISMISS_VELOCITY = 500;\nconst INTENT_PX = 4;\nconst STRETCH_MAX = 0.06;\n\nfunction findScrollable(node: EventTarget | null, stop: HTMLElement | null): HTMLElement | null {\n let el = node instanceof Element ? node : null;\n while (el && el !== stop) {\n const s = getComputedStyle(el);\n if (\n /(auto|scroll)/.test(s.overflowY + s.overflowX) &&\n (el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth)\n )\n return el as HTMLElement;\n el = el.parentElement;\n }\n return null;\n}\n\nfunction sheetScrim(panel: HTMLElement | null): HTMLElement | null {\n return panel?.parentElement?.querySelector<HTMLElement>('.overlay-scrim') ?? null;\n}\n\nexport function sheetSpan(panel: HTMLElement, side: 'right' | 'bottom'): number {\n return side === 'bottom' ? panel.offsetHeight : panel.offsetWidth;\n}\n\nexport function useSheetDrag({\n side,\n panelRef,\n enabled,\n requestClose,\n}: {\n side: 'right' | 'bottom';\n panelRef: RefObject<HTMLElement>;\n enabled: boolean;\n requestClose: () => void;\n}) {\n const axis = side === 'bottom' ? 'y' : 'x';\n const savedUserSelect = useRef<string | null>(null);\n\n function suspendSelection() {\n const sel = window.getSelection();\n if (sel) sel.removeAllRanges();\n savedUserSelect.current = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n }\n function restoreSelection() {\n if (savedUserSelect.current === null) return;\n document.body.style.userSelect = savedUserSelect.current;\n savedUserSelect.current = null;\n }\n useEffect(() => restoreSelection, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n function paint(el: HTMLElement, travel: number, stretch: number) {\n const span = sheetSpan(el, side);\n el.style.translate = axis === 'y' ? `0px ${travel}px` : `${travel}px 0px`;\n el.style.scale = axis === 'y' ? `1 ${stretch}` : `${stretch} 1`;\n const scrim = sheetScrim(el);\n if (scrim) scrim.style.opacity = String(1 - Math.min(Math.max(travel / span, 0), 1));\n }\n\n function onPointerDown(e: ReactPointerEvent) {\n if (e.button !== 0) return;\n const startX = e.clientX,\n startY = e.clientY;\n const scrollable = findScrollable(e.target, panelRef.current);\n\n const onMove = (ev: PointerEvent) => {\n const dx = ev.clientX - startX,\n dy = ev.clientY - startY;\n if (Math.max(Math.abs(dx), Math.abs(dy)) < INTENT_PX) return;\n cleanup();\n const along = axis === 'y' ? dy : dx;\n const cross = axis === 'y' ? dx : dy;\n if (Math.abs(along) <= Math.abs(cross)) return;\n if (scrollable) {\n if (along < 0) return;\n if (axis === 'y' ? scrollable.scrollTop > 0 : scrollable.scrollLeft > 0) return;\n }\n suspendSelection();\n startDrag(ev, { onMove: onDrag, onEnd: onDragEnd });\n };\n\n const cleanup = () => {\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', cleanup);\n window.removeEventListener('pointercancel', cleanup);\n };\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', cleanup);\n window.addEventListener('pointercancel', cleanup);\n }\n\n function onDrag(info: PanInfo) {\n const el = panelRef.current;\n if (!el) return;\n const o = info.offset[axis];\n const span = sheetSpan(el, side);\n paint(el, Math.max(o, 0), o < 0 ? 1 + Math.min(-o / span, 1) * STRETCH_MAX : 1);\n }\n\n function onDragEnd(info: PanInfo) {\n restoreSelection();\n const el = panelRef.current;\n if (!el) return;\n const span = sheetSpan(el, side);\n if (info.offset[axis] > span * DISMISS_RATIO || info.velocity[axis] > DISMISS_VELOCITY) {\n requestClose();\n return;\n }\n animate(el, { x: [0], y: [0], scale: [[1, 1]], timing: SM.t.settle });\n const scrim = sheetScrim(el);\n if (scrim) animate(scrim, { opacity: [1], timing: SM.t.settle });\n }\n\n return enabled ? { onPointerDown } : {};\n}\n","'use client';\n\nimport './sheet.css';\nimport { Fragment, useId, useRef, type HTMLAttributes, type ReactElement, type ReactNode } from 'react';\nimport { animate, type Layer } from '../../../engine';\nimport { Presence } from '../../../motion/presence';\nimport type { MotionSpecs } from '../../../motion/use-motion';\nimport { resolveMotionTiming } from '../../../motion/motion-timing';\nimport type { DisableableAnimation } from '../../../motion/timing';\nimport { useControllable } from '../../internal/hooks/use-controllable';\nimport { ovCloneTrigger, OverlayPortal } from '../../internal/overlay/layer';\nimport { ModalShell } from '../../internal/overlay/modal';\nimport { useSheetDrag, sheetSpan } from './use-sheet-drag';\nimport type { DataAttributes } from '../../../dom-props';\n\nconst SHEET_TIMING = {\n open: { duration: 'slow', ease: 'entrance' },\n close: { duration: 'base', ease: 'exit' },\n} as const;\n\nexport interface SheetProps {\n /** Controlled open state. Omit to stay uncontrolled. */\n open?: boolean;\n /** Initial state when uncontrolled. Default false. */\n defaultOpen?: boolean;\n /** Fires whenever the open state changes. Pair with `open` for controlled use. */\n onOpenChange?: (open: boolean) => void;\n\n /** Cloned to open the sheet on click. */\n trigger?: ReactElement | null;\n\n /** Edge the sheet slides in from. Default 'right'. */\n side?: 'right' | 'bottom';\n\n /** Scrim/Esc dismissal + drag-to-edge. Default true. */\n dismissible?: boolean;\n\n /** Base id for the panel; drives the trigger's `aria-controls`. Auto-generated when omitted. */\n id?: string;\n /** Standard attributes (className, style, data-*, ...) forwarded to the sheet panel. */\n htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\n /** Open/close timing - motion tokens only, or `null` to disable. @default open 'slow'/'entrance', close 'base'/'exit' */\n animation?: DisableableAnimation;\n /** The ENTIRE surface - paint AND semantics. Drive dismissal with `open`/`onOpenChange`. */\n children: ReactNode;\n}\n\nfunction sheetSlide(slot: HTMLElement, side: 'right' | 'bottom', at: number[]): Layer {\n const frames = at.map((ratio) => ratio * sheetSpan(slot, side));\n return side === 'bottom' ? { y: frames } : { x: frames };\n}\n\nfunction SheetShell({\n side,\n panelId,\n dismissible,\n requestClose,\n htmlProps,\n animate,\n exit,\n children,\n}: {\n side: 'right' | 'bottom';\n panelId: string;\n dismissible: boolean;\n requestClose: () => void;\n htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\n children: ReactNode;\n} & MotionSpecs) {\n const panelRef = useRef<HTMLElement>(null);\n const dragProps = useSheetDrag({ side, panelRef, enabled: dismissible, requestClose });\n return (\n <ModalShell\n animate={animate}\n exit={exit}\n layerClass={'overlay-layer--sheet-' + side}\n panelClass={'overlay-panel--sheet-' + side}\n panelId={panelId}\n dismissible={dismissible}\n requestClose={requestClose}\n panelRef={panelRef}\n panelProps={{ ...htmlProps, ...dragProps }}\n >\n {children}\n </ModalShell>\n );\n}\n\nexport function Sheet({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n trigger = null,\n side = 'right',\n dismissible = true,\n id,\n htmlProps,\n animation,\n children,\n}: SheetProps) {\n const [open, setOpen] = useControllable(controlledOpen, defaultOpen, onOpenChange);\n const triggerRef = useRef<HTMLElement>(null);\n const autoId = useId();\n const panelId = id || 'sheet-' + autoId;\n const close = () => setOpen(false);\n const timings = resolveMotionTiming(animation, SHEET_TIMING);\n\n return (\n <Fragment>\n {ovCloneTrigger(trigger, { open, onPress: () => setOpen(true), panelId, haspopup: 'dialog', triggerRef })}\n <OverlayPortal>\n <Presence>\n {open && (\n <SheetShell\n key=\"sheet\"\n animate={(slot) => {\n const play = animate(slot, { ...sheetSlide(slot, side, [1, 0]), timing: timings.open });\n play.finished.then(() => play.stop());\n return play;\n }}\n exit={(slot) => animate(slot, { ...sheetSlide(slot, side, [1]), timing: timings.close })}\n side={side}\n panelId={panelId}\n dismissible={dismissible}\n requestClose={close}\n htmlProps={htmlProps}\n >\n {children}\n </SheetShell>\n )}\n </Presence>\n </OverlayPortal>\n </Fragment>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/internal/overlay/layer.tsx"],"names":[],"mappings":"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","file":"chunk-GY7JOPUQ.js","sourcesContent":["'use client';\n\nimport {\n cloneElement,\n useEffect,\n useLayoutEffect,\n useRef,\n type ReactElement,\n type ReactNode,\n type Ref,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\ninterface OverlayEntry {\n contains: (t: EventTarget | null) => boolean;\n isDismissible: () => boolean;\n requestClose: () => void;\n _dismissible?: boolean;\n _close?: () => void;\n}\n\nconst ovStack: OverlayEntry[] = [];\n\nfunction ovOnDocKeyDown(e: KeyboardEvent) {\n if (e.key !== 'Escape' || e.defaultPrevented || ovStack.length === 0) return;\n const top = ovStack[ovStack.length - 1];\n if (!top.isDismissible()) return;\n e.preventDefault();\n top.requestClose();\n}\n\nfunction ovIsTop(entry: OverlayEntry) {\n return ovStack[ovStack.length - 1] === entry;\n}\n\nfunction ovInOverlayAbove(entry: OverlayEntry, target: EventTarget | null) {\n const i = ovStack.indexOf(entry);\n return i >= 0 && ovStack.slice(i + 1).some((e) => e.contains(target));\n}\n\nfunction useOverlayEntry({\n nodeRef,\n dismissible,\n requestClose,\n}: {\n nodeRef: RefObject<HTMLElement>;\n dismissible: boolean;\n requestClose: () => void;\n}): OverlayEntry {\n const ref = useRef<OverlayEntry>(null);\n if (!ref.current) {\n ref.current = {\n contains: (t) => Boolean(nodeRef.current && nodeRef.current.contains(t as Node)),\n isDismissible: () => ref.current._dismissible,\n requestClose: () => ref.current._close(),\n };\n }\n ref.current._dismissible = dismissible;\n ref.current._close = requestClose;\n\n useLayoutEffect(() => {\n const entry = ref.current;\n if (ovStack.length === 0) document.addEventListener('keydown', ovOnDocKeyDown);\n ovStack.push(entry);\n if (nodeRef.current) nodeRef.current.style.zIndex = 'calc(var(--layer-overlay) + ' + (ovStack.length - 1) + ')';\n return () => {\n const i = ovStack.indexOf(entry);\n if (i >= 0) ovStack.splice(i, 1);\n if (ovStack.length === 0) document.removeEventListener('keydown', ovOnDocKeyDown);\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n return ref.current;\n}\n\nfunction useOutsidePress({\n entry,\n refs,\n enabled,\n onPress,\n}: {\n entry: OverlayEntry;\n refs: RefObject<HTMLElement>[];\n enabled: boolean;\n onPress: () => void;\n}) {\n const latest = useRef<{ enabled: boolean; onPress: () => void }>(null);\n latest.current = { enabled, onPress };\n useEffect(() => {\n const onDown = (e: PointerEvent) => {\n if (!latest.current.enabled) return;\n const t = e.target;\n if (refs.some((r) => r.current && r.current.contains(t as Node))) return;\n if (ovInOverlayAbove(entry, t)) return;\n latest.current.onPress();\n };\n document.addEventListener('pointerdown', onDown, true);\n return () => document.removeEventListener('pointerdown', onDown, true);\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n}\n\nfunction ovCloneTrigger(\n trigger: ReactElement | null,\n {\n open,\n onPress,\n panelId,\n haspopup,\n triggerRef,\n }: { open: boolean; onPress: () => void; panelId: string; haspopup: string; triggerRef: RefObject<HTMLElement> },\n): ReactElement | null {\n if (!trigger) return null;\n const own = trigger.props as { onClick?: (...args: unknown[]) => void };\n const ownRef = (trigger as ReactElement & { ref?: Ref<HTMLElement> }).ref;\n return cloneElement(trigger, {\n 'aria-haspopup': haspopup,\n 'aria-expanded': open,\n 'aria-controls': open ? panelId : undefined,\n onClick: (...args: unknown[]) => {\n own.onClick?.(...args);\n onPress();\n },\n ref: (node: HTMLElement | null) => {\n triggerRef.current = node;\n if (typeof ownRef === 'function') ownRef(node);\n else if (ownRef) ownRef.current = node;\n },\n } as Partial<typeof trigger.props>);\n}\n\nfunction OverlayPortal({ children }: { children: ReactNode }) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n if (typeof document !== 'undefined' && !hostRef.current) {\n hostRef.current = document.createElement('div');\n hostRef.current.setAttribute('data-overlay-root', '');\n }\n useLayoutEffect(() => {\n const el = hostRef.current;\n if (!el) return;\n document.body.appendChild(el);\n return () => el.remove();\n }, []);\n if (!hostRef.current) return null;\n return createPortal(children, hostRef.current);\n}\n\nexport { ovIsTop, ovInOverlayAbove, useOverlayEntry, useOutsidePress, ovCloneTrigger, OverlayPortal };\nexport type { OverlayEntry };\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/composites/popover/Popover.tsx"],"names":[],"mappings":"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","file":"chunk-W5YM2FV4.js","sourcesContent":["'use client';\n\nimport './popover.css';\nimport { Fragment, useId, useRef, type HTMLAttributes, type ReactElement, type ReactNode, type RefObject } from 'react';\nimport { Presence } from '../../../motion/presence';\nimport { useMotion, type MotionSpecs } from '../../../motion/use-motion';\nimport { resolveMotionTiming } from '../../../motion/motion-timing';\nimport type { DisableableAnimation } from '../../../motion/timing';\nimport { useControllable } from '../../internal/hooks/use-controllable';\nimport { ovCloneTrigger, useOverlayEntry, useOutsidePress, OverlayPortal } from '../../internal/overlay/layer';\nimport { useReturnFocus } from '../../internal/overlay/focus';\nimport { useAnchorPosition } from '../../internal/overlay/position';\nimport type { DataAttributes } from '../../../dom-props';\nimport { cx } from '../../internal/utils/cx';\n\nconst POPOVER_TIMING = {\n open: { duration: 'base', ease: 'entrance' },\n close: { duration: 'fast', ease: 'exit' },\n} as const;\n\nexport interface PopoverProps {\n /** Controlled open state. Omit to stay uncontrolled. */\n open?: boolean;\n /** Initial state when uncontrolled. Default false. */\n defaultOpen?: boolean;\n /** Fires whenever the open state changes. Pair with `open` for controlled use. */\n onOpenChange?: (open: boolean) => void;\n\n /** Cloned to toggle the panel; required as the anchor. */\n trigger?: ReactElement | null;\n\n /** Preferred side; flips to the opposite side when cramped. Default 'bottom'. */\n side?: 'top' | 'bottom' | 'left' | 'right';\n /** Cross-axis alignment against the trigger. Default 'start'. */\n align?: 'start' | 'center' | 'end';\n /** Caret tracking the trigger center. Default false. */\n arrow?: boolean;\n\n /** Esc + outside-press dismissal. Default true. */\n dismissible?: boolean;\n\n /** Base id for the panel; drives the trigger's `aria-controls`. Auto-generated when omitted. */\n id?: string;\n /** Standard attributes (className, style, data-*, ...) forwarded to the popover panel. */\n htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\n /** Open/close timing - motion tokens only, or `null` to disable. @default open 'base'/'entrance', close 'fast'/'exit' */\n animation?: DisableableAnimation;\n /** The ENTIRE surface - paint AND semantics. Drive dismissal with `open`/`onOpenChange`. */\n children: ReactNode;\n}\n\nfunction PopoverPanel({\n panelId,\n side,\n align,\n arrow,\n dismissible,\n requestClose,\n triggerRef,\n htmlProps,\n animate,\n exit,\n children,\n}: {\n panelId: string;\n side: 'top' | 'bottom' | 'left' | 'right';\n align: 'start' | 'center' | 'end';\n arrow: boolean;\n dismissible: boolean;\n requestClose: () => void;\n triggerRef: RefObject<HTMLElement>;\n htmlProps?: HTMLAttributes<HTMLDivElement> & DataAttributes;\n children: ReactNode;\n} & MotionSpecs) {\n const panelRef = useRef<HTMLElement>(null);\n const entry = useOverlayEntry({ nodeRef: panelRef, dismissible, requestClose });\n useMotion(panelRef, { animate, exit });\n useReturnFocus(panelRef);\n useAnchorPosition({ side, align, arrow, triggerRef, panelRef });\n useOutsidePress({ entry, refs: [triggerRef, panelRef], enabled: dismissible, onPress: requestClose });\n return (\n <div\n {...htmlProps}\n id={panelId}\n ref={panelRef as RefObject<HTMLDivElement>}\n className={cx('overlay-popover', htmlProps?.className)}\n >\n {arrow && <span className=\"overlay-popover__arrow\" aria-hidden=\"true\"></span>}\n {children}\n </div>\n );\n}\n\nexport function Popover({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n trigger = null,\n side = 'bottom',\n align = 'start',\n arrow = false,\n dismissible = true,\n id,\n htmlProps,\n animation,\n children,\n}: PopoverProps) {\n const [open, setOpen] = useControllable(controlledOpen, defaultOpen, onOpenChange);\n const triggerRef = useRef<HTMLElement>(null);\n const autoId = useId();\n const panelId = id || 'popover-' + autoId;\n const close = () => setOpen(false);\n const timings = resolveMotionTiming(animation, POPOVER_TIMING);\n\n return (\n <Fragment>\n {ovCloneTrigger(trigger, { open, onPress: () => setOpen(!open), panelId, haspopup: 'true', triggerRef })}\n <OverlayPortal>\n <Presence>\n {open && (\n <PopoverPanel\n key=\"panel\"\n animate={{ opacity: [0, 1], scale: [0.96, 1], timing: timings.open }}\n exit={{ opacity: [0], scale: [0.96], timing: timings.close }}\n panelId={panelId}\n side={side}\n align={align}\n arrow={arrow}\n dismissible={dismissible}\n requestClose={close}\n triggerRef={triggerRef}\n htmlProps={htmlProps}\n >\n {children}\n </PopoverPanel>\n )}\n </Presence>\n </OverlayPortal>\n </Fragment>\n );\n}\n"]}
|