@passui/react 0.1.45 → 0.1.46
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/README.md +57 -46
- package/dist/types/src/components/accordion/accordion.d.ts +1 -1
- package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
- package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
- package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
- package/dist/types/src/components/popover/index.d.ts +41 -0
- package/dist/types/src/components/popover/popover-content.d.ts +32 -0
- package/dist/types/src/components/popover/popover-root.d.ts +22 -0
- package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
- package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
- package/dist/types/src/components/table/table.d.ts +1 -1
- package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/machines/popover.machine.d.ts +30 -0
- package/package.json +307 -307
- package/src/appearance/index.ts +2 -2
- package/src/appearance/platform.tsx +76 -60
- package/src/appearance/theme.tsx +409 -377
- package/src/components/accordion/accordion.tsx +58 -49
- package/src/components/ambient/ambient.tsx +25 -25
- package/src/components/ambient/index.ts +1 -1
- package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
- package/src/components/breadcrumb/breadcrumb.tsx +63 -57
- package/src/components/button/button-styles.ts +39 -32
- package/src/components/button/button.tsx +91 -84
- package/src/components/button/icon-button.tsx +27 -25
- package/src/components/button/pending-spinner.tsx +8 -8
- package/src/components/button/use-pending-indicator.ts +11 -11
- package/src/components/chip/chip.tsx +35 -30
- package/src/components/code-block/code-block.tsx +40 -39
- package/src/components/combo-box/combo-box.tsx +5 -5
- package/src/components/command/command.tsx +124 -94
- package/src/components/compact-date-picker/calendar-month.ts +75 -53
- package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
- package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
- package/src/components/compact-time-picker/time-value.ts +32 -29
- package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
- package/src/components/content-view/content-view.tsx +168 -159
- package/src/components/content-view/index.ts +1 -1
- package/src/components/context-menu/conceal-menu-source.ts +25 -25
- package/src/components/context-menu/context-menu-host.tsx +439 -402
- package/src/components/context-menu/context-menu-store.ts +125 -123
- package/src/components/context-menu/menu-navigation.ts +25 -18
- package/src/components/context-menu/menu-position.ts +33 -29
- package/src/components/context-menu/menu-reveal.ts +32 -32
- package/src/components/context-menu/use-context-menu.ts +63 -56
- package/src/components/conversation/conversation.tsx +265 -263
- package/src/components/conversation/index.ts +1 -1
- package/src/components/date-picker/date-picker.tsx +95 -94
- package/src/components/date-picker/date-value.ts +20 -10
- package/src/components/dialog/dialog-host.tsx +105 -103
- package/src/components/dialog/dialog-store.ts +37 -36
- package/src/components/dialog/dialog.tsx +192 -179
- package/src/components/dialog/index.ts +1 -1
- package/src/components/disclosure-group/disclosure-group.tsx +93 -86
- package/src/components/divider/divider.tsx +24 -22
- package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
- package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
- package/src/components/labeled-content/labeled-content.tsx +68 -68
- package/src/components/locale-switcher/index.ts +1 -1
- package/src/components/locale-switcher/locale-switcher.tsx +279 -251
- package/src/components/menu/menu.tsx +75 -58
- package/src/components/morph/index.ts +2 -2
- package/src/components/morph/morph.tsx +227 -201
- package/src/components/morph/use-morph-disclosure.ts +19 -19
- package/src/components/one-time-code/one-time-code.tsx +216 -200
- package/src/components/picker/picker-trigger.ts +16 -13
- package/src/components/picker/picker.tsx +371 -345
- package/src/components/popover/anchored-surface.tsx +253 -229
- package/src/components/popover/index.ts +50 -0
- package/src/components/popover/popover-content.tsx +334 -0
- package/src/components/popover/popover-fit.ts +58 -53
- package/src/components/popover/popover-root.tsx +97 -0
- package/src/components/popover/popover-trigger.tsx +71 -0
- package/src/components/profile-image/profile-image.tsx +80 -64
- package/src/components/progress-view/progress-view.tsx +61 -59
- package/src/components/progress-view/use-liquid-progress.ts +38 -38
- package/src/components/radio-group/radio-group.tsx +71 -68
- package/src/components/search-field/expanding-search-field.tsx +191 -177
- package/src/components/search-field/search-field.tsx +59 -50
- package/src/components/search-list/search-list.tsx +246 -236
- package/src/components/section/section.tsx +58 -58
- package/src/components/secure-field/secure-field.tsx +3 -3
- package/src/components/segmented-control/segmented-control.tsx +136 -136
- package/src/components/sheet/modal-environment.ts +71 -64
- package/src/components/sheet/sheet-portal.tsx +58 -53
- package/src/components/sheet/sheet.tsx +146 -141
- package/src/components/sheet/use-sheet-focus.ts +97 -82
- package/src/components/sidebar/sidebar.tsx +78 -78
- package/src/components/skeleton/skeleton.tsx +30 -21
- package/src/components/slider/slider.tsx +113 -104
- package/src/components/slosh-slider/liquid-step.ts +32 -31
- package/src/components/slosh-slider/slosh-slider.tsx +265 -228
- package/src/components/stat-tile/index.ts +1 -1
- package/src/components/stat-tile/stat-tile.tsx +79 -68
- package/src/components/status-indicator/status-indicator.tsx +40 -37
- package/src/components/stepper/rolling-digit.tsx +82 -68
- package/src/components/stepper/stepper.tsx +122 -113
- package/src/components/swatch-picker/swatch-picker.tsx +120 -120
- package/src/components/tab-bar/tab-bar.tsx +149 -148
- package/src/components/tab-view/tab-view.tsx +140 -137
- package/src/components/table/table.tsx +117 -98
- package/src/components/text-editor/text-editor.tsx +34 -29
- package/src/components/text-field/text-field.tsx +182 -180
- package/src/components/toast/toast-store.ts +127 -115
- package/src/components/toast/toaster.tsx +361 -347
- package/src/components/toggle/toggle.tsx +297 -282
- package/src/components/tooltip/tooltip-host.tsx +157 -150
- package/src/components/view-controller/index.ts +1 -1
- package/src/components/view-controller/view-controller.tsx +64 -53
- package/src/components/wake-slider/wake-slider.tsx +220 -205
- package/src/hooks/scroll/minimize-decision.ts +11 -7
- package/src/hooks/use-anchored-disclosure.ts +19 -19
- package/src/hooks/use-anchored-placement.ts +29 -25
- package/src/hooks/use-dismiss.ts +33 -29
- package/src/hooks/use-floating-dismiss.ts +24 -22
- package/src/hooks/use-media-query.ts +67 -60
- package/src/hooks/use-minimize-on-scroll.ts +69 -60
- package/src/hooks/use-visual-viewport.ts +63 -63
- package/src/hooks/viewport/types.ts +16 -16
- package/src/hooks/viewport/viewport-metrics.ts +16 -10
- package/src/icons/icons.tsx +440 -328
- package/src/index.ts +91 -91
- package/src/lib/anchored-placement.ts +70 -65
- package/src/lib/class-names.ts +14 -13
- package/src/lib/clipboard.ts +8 -7
- package/src/lib/date.ts +61 -60
- package/src/lib/floating-layers.ts +39 -24
- package/src/lib/haptics.ts +10 -10
- package/src/lib/language.ts +9 -9
- package/src/lib/range-value.ts +27 -21
- package/src/lib/screen-edges.ts +33 -30
- package/src/lib/shapes.ts +38 -34
- package/src/machines/popover.machine.ts +51 -0
- package/src/motion/transitions.ts +46 -43
- package/src/styles/code-block.css +69 -43
- package/src/styles/motion-controls.css +393 -191
- package/src/tokens.ts +1 -1
- package/dist/types/src/components/popover/popover.d.ts +0 -21
- package/src/components/popover/popover.tsx +0 -334
|
@@ -1,68 +1,85 @@
|
|
|
1
|
-
import { forwardRef, type ReactNode, useRef, useSyncExternalStore } from "react"
|
|
2
|
-
import { Button, type ButtonProps } from "../button/button"
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
forwardRef,
|
|
3
|
+
type ReactNode,
|
|
4
|
+
useRef,
|
|
5
|
+
useSyncExternalStore,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { Button, type ButtonProps } from "../button/button";
|
|
8
|
+
import {
|
|
9
|
+
closeContextMenu,
|
|
10
|
+
getContextMenu,
|
|
11
|
+
openMenu,
|
|
12
|
+
subscribeContextMenu,
|
|
13
|
+
type ContextMenuEdge,
|
|
14
|
+
type ContextMenuItem,
|
|
15
|
+
} from "../context-menu/context-menu-store";
|
|
11
16
|
|
|
12
17
|
export interface MenuProps extends Omit<ButtonProps, "onClick" | "children"> {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
/** Accessible name for the menu trigger. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Items are rendered by the app's existing ContextMenuHost. */
|
|
21
|
+
items: readonly ContextMenuItem[];
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
/** Logical edge used to align the menu, including RTL. @default "leading" */
|
|
24
|
+
edge?: ContextMenuEdge;
|
|
25
|
+
/** Expand upward from the trigger instead of downward. @default false */
|
|
26
|
+
above?: boolean;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
/** A declarative menu button using the same native host as context menus. */
|
|
25
30
|
export const Menu = forwardRef<HTMLButtonElement, MenuProps>(function Menu(
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
{
|
|
32
|
+
label,
|
|
33
|
+
items,
|
|
34
|
+
children,
|
|
35
|
+
edge = "leading",
|
|
36
|
+
above = false,
|
|
37
|
+
disabled,
|
|
38
|
+
...props
|
|
39
|
+
},
|
|
40
|
+
forwardedRef,
|
|
28
41
|
) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
const trigger = useRef<HTMLButtonElement>(null);
|
|
43
|
+
const current = useSyncExternalStore(
|
|
44
|
+
subscribeContextMenu,
|
|
45
|
+
getContextMenu,
|
|
46
|
+
() => null,
|
|
47
|
+
);
|
|
48
|
+
const expanded = Boolean(current && current.anchor === trigger.current);
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
const toggle = () => {
|
|
51
|
+
if (!trigger.current || disabled) return;
|
|
52
|
+
if (expanded) closeContextMenu(false);
|
|
53
|
+
else openMenu(trigger.current, [...items], { edge, above });
|
|
54
|
+
};
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
56
|
+
return (
|
|
57
|
+
<Button
|
|
58
|
+
{...props}
|
|
59
|
+
ref={(node) => {
|
|
60
|
+
trigger.current = node;
|
|
61
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
62
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
63
|
+
}}
|
|
64
|
+
type="button"
|
|
65
|
+
aria-label={label}
|
|
66
|
+
aria-haspopup="menu"
|
|
67
|
+
aria-expanded={expanded}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
onClick={toggle}
|
|
70
|
+
onKeyDown={(event) => {
|
|
71
|
+
props.onKeyDown?.(event);
|
|
72
|
+
if (event.defaultPrevented) return;
|
|
73
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
if (!expanded) toggle();
|
|
76
|
+
} else if (event.key === "Escape" && expanded) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
closeContextMenu();
|
|
79
|
+
}
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{children}
|
|
83
|
+
</Button>
|
|
84
|
+
);
|
|
85
|
+
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./morph"
|
|
2
|
-
export * from "./use-morph-disclosure"
|
|
1
|
+
export * from "./morph";
|
|
2
|
+
export * from "./use-morph-disclosure";
|
|
@@ -1,42 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
1
|
+
import {
|
|
2
|
+
AnimatePresence,
|
|
3
|
+
type HTMLMotionProps,
|
|
4
|
+
motion,
|
|
5
|
+
useReducedMotion,
|
|
6
|
+
} from "motion/react";
|
|
7
|
+
import {
|
|
8
|
+
type ReactNode,
|
|
9
|
+
type RefObject,
|
|
10
|
+
useLayoutEffect,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
} from "react";
|
|
14
|
+
import { concealMenuSource } from "../context-menu/conceal-menu-source";
|
|
15
|
+
import {
|
|
16
|
+
menuSurfaceExit,
|
|
17
|
+
menuSurfaceReveal,
|
|
18
|
+
type MenuRect,
|
|
19
|
+
} from "../context-menu/menu-reveal";
|
|
20
|
+
import { useSheetFocus } from "../sheet/use-sheet-focus";
|
|
21
|
+
import { SheetPortal } from "../sheet/sheet-portal";
|
|
22
|
+
import { cn } from "../../lib/class-names";
|
|
23
|
+
import {
|
|
24
|
+
contentCrossfade,
|
|
25
|
+
fluidCollapse,
|
|
26
|
+
fluidOvershoot,
|
|
27
|
+
} from "../../motion/transitions";
|
|
9
28
|
|
|
10
|
-
export type MorphSize = "sm" | "md" | "lg"
|
|
29
|
+
export type MorphSize = "sm" | "md" | "lg";
|
|
11
30
|
|
|
12
31
|
const outerPaddings: Record<MorphSize, string> = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
32
|
+
sm: "p-4",
|
|
33
|
+
md: "p-6",
|
|
34
|
+
lg: "p-8",
|
|
35
|
+
};
|
|
17
36
|
const panelMaxWidths: Record<MorphSize, string> = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
37
|
+
sm: "max-w-sm",
|
|
38
|
+
md: "max-w-[720px]",
|
|
39
|
+
lg: "max-w-3xl",
|
|
40
|
+
};
|
|
22
41
|
|
|
23
42
|
export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
/** Controls whether the overlay is presented. */
|
|
44
|
+
open: boolean;
|
|
45
|
+
/** Called on Escape or outside press. */
|
|
46
|
+
onClose: () => void;
|
|
47
|
+
/** The element whose rect the overlay grows from and collapses back to. */
|
|
48
|
+
sourceRef: RefObject<HTMLElement | null>;
|
|
49
|
+
/** Accessible name (used when there is no labelledBy). */
|
|
50
|
+
label: string;
|
|
51
|
+
/** ID of a visible heading inside the overlay. */
|
|
52
|
+
labelledBy?: string;
|
|
53
|
+
/** Allow tapping the backdrop to close. Escape still calls onClose. @default true */
|
|
54
|
+
dismissOnOutsidePress?: boolean;
|
|
55
|
+
/** Outer padding and panel max-width. @default "md" */
|
|
56
|
+
size?: MorphSize;
|
|
57
|
+
className?: string;
|
|
58
|
+
children?: ReactNode;
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
/**
|
|
@@ -50,183 +69,190 @@ export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
|
50
69
|
* primitive that powers anchored `Popover` and `ContextMenu`.
|
|
51
70
|
*/
|
|
52
71
|
export function Morph({ open, ...props }: MorphProps) {
|
|
53
|
-
|
|
72
|
+
return <AnimatePresence>{open && <OpenMorph {...props} />}</AnimatePresence>;
|
|
54
73
|
}
|
|
55
74
|
|
|
56
75
|
function OpenMorph({
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
onClose,
|
|
77
|
+
sourceRef,
|
|
78
|
+
label,
|
|
79
|
+
labelledBy,
|
|
80
|
+
dismissOnOutsidePress = true,
|
|
81
|
+
size = "md",
|
|
82
|
+
className,
|
|
83
|
+
children,
|
|
84
|
+
...props
|
|
66
85
|
}: Omit<MorphProps, "open">) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
const panel = useRef<HTMLDivElement>(null);
|
|
87
|
+
const reducedMotion = useReducedMotion();
|
|
88
|
+
const [settled, setSettled] = useState(false);
|
|
89
|
+
const [target, setTarget] = useState<{
|
|
90
|
+
width: number;
|
|
91
|
+
height: number;
|
|
92
|
+
radius: number;
|
|
93
|
+
} | null>(null);
|
|
94
|
+
const [reveal, setReveal] = useState<ReturnType<
|
|
95
|
+
typeof menuSurfaceReveal
|
|
96
|
+
> | null>(null);
|
|
97
|
+
useSheetFocus(panel, onClose);
|
|
75
98
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
99
|
+
// Measure the panel's resting rect and the source's rect, then compute
|
|
100
|
+
// the reveal (initial material state) from menuSurfaceReveal. Re-measures
|
|
101
|
+
// on resize/scroll so the morph stays aligned if either moves.
|
|
102
|
+
useLayoutEffect(() => {
|
|
103
|
+
const surface = panel.current;
|
|
104
|
+
const source = sourceRef.current;
|
|
105
|
+
if (!surface) return;
|
|
106
|
+
const measure = () => {
|
|
107
|
+
const panelRect = surface.getBoundingClientRect();
|
|
108
|
+
const radius =
|
|
109
|
+
Number.parseFloat(getComputedStyle(surface).borderTopLeftRadius) || 0;
|
|
110
|
+
setTarget((current) =>
|
|
111
|
+
current &&
|
|
112
|
+
current.width === panelRect.width &&
|
|
113
|
+
current.height === panelRect.height &&
|
|
114
|
+
current.radius === radius
|
|
115
|
+
? current
|
|
116
|
+
: { width: panelRect.width, height: panelRect.height, radius },
|
|
117
|
+
);
|
|
118
|
+
const sourceRect = source?.getBoundingClientRect();
|
|
119
|
+
const sourceArg: MenuRect | undefined =
|
|
120
|
+
sourceRect && sourceRect.width > 0 && sourceRect.height > 0
|
|
121
|
+
? {
|
|
122
|
+
left: sourceRect.left,
|
|
123
|
+
top: sourceRect.top,
|
|
124
|
+
width: sourceRect.width,
|
|
125
|
+
height: sourceRect.height,
|
|
126
|
+
}
|
|
127
|
+
: undefined;
|
|
128
|
+
setReveal(
|
|
129
|
+
menuSurfaceReveal(sourceArg, {
|
|
130
|
+
left: panelRect.left,
|
|
131
|
+
top: panelRect.top,
|
|
132
|
+
width: panelRect.width,
|
|
133
|
+
height: panelRect.height,
|
|
134
|
+
radius,
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
};
|
|
138
|
+
measure();
|
|
139
|
+
let frame = 0;
|
|
140
|
+
const schedule = () => {
|
|
141
|
+
cancelAnimationFrame(frame);
|
|
142
|
+
frame = requestAnimationFrame(measure);
|
|
143
|
+
};
|
|
144
|
+
const observer = new ResizeObserver(schedule);
|
|
145
|
+
observer.observe(surface);
|
|
146
|
+
if (source) observer.observe(source);
|
|
147
|
+
const viewport = window.visualViewport;
|
|
148
|
+
viewport?.addEventListener("resize", schedule);
|
|
149
|
+
viewport?.addEventListener("scroll", schedule);
|
|
150
|
+
window.addEventListener("resize", schedule);
|
|
151
|
+
window.addEventListener("scroll", schedule, true);
|
|
152
|
+
return () => {
|
|
153
|
+
observer.disconnect();
|
|
154
|
+
cancelAnimationFrame(frame);
|
|
155
|
+
viewport?.removeEventListener("resize", schedule);
|
|
156
|
+
viewport?.removeEventListener("scroll", schedule);
|
|
157
|
+
window.removeEventListener("resize", schedule);
|
|
158
|
+
window.removeEventListener("scroll", schedule, true);
|
|
159
|
+
};
|
|
160
|
+
}, [sourceRef]);
|
|
137
161
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
// Conceal the source while the morph is mounted; restore on unmount.
|
|
163
|
+
useLayoutEffect(() => {
|
|
164
|
+
const source = sourceRef.current;
|
|
165
|
+
if (!source) return;
|
|
166
|
+
const restore = concealMenuSource(source, false);
|
|
167
|
+
return () => {
|
|
168
|
+
restore();
|
|
169
|
+
};
|
|
170
|
+
}, [sourceRef]);
|
|
147
171
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
172
|
+
return (
|
|
173
|
+
<SheetPortal>
|
|
174
|
+
<div
|
|
175
|
+
className={cn(
|
|
176
|
+
"fixed inset-0 z-[75] flex items-center justify-center",
|
|
177
|
+
outerPaddings[size],
|
|
178
|
+
)}
|
|
179
|
+
>
|
|
180
|
+
<motion.div
|
|
181
|
+
aria-hidden="true"
|
|
182
|
+
className="absolute inset-0 bg-black/30 backdrop-blur-md"
|
|
183
|
+
initial={{ opacity: 0 }}
|
|
184
|
+
animate={{ opacity: 1 }}
|
|
185
|
+
exit={{ opacity: 0 }}
|
|
186
|
+
transition={reducedMotion ? { duration: 0 } : { duration: 0.18 }}
|
|
187
|
+
onClick={dismissOnOutsidePress ? onClose : undefined}
|
|
188
|
+
/>
|
|
189
|
+
<motion.div
|
|
190
|
+
ref={panel}
|
|
191
|
+
role="dialog"
|
|
192
|
+
aria-modal="true"
|
|
193
|
+
aria-label={labelledBy ? undefined : label}
|
|
194
|
+
aria-labelledby={labelledBy}
|
|
195
|
+
tabIndex={-1}
|
|
196
|
+
className={cn(
|
|
197
|
+
"rounded-5xl relative isolate flex w-full flex-col outline-none",
|
|
198
|
+
"max-h-[92%]",
|
|
199
|
+
panelMaxWidths[size],
|
|
200
|
+
settled && "overflow-hidden",
|
|
201
|
+
className,
|
|
202
|
+
)}
|
|
203
|
+
{...props}
|
|
204
|
+
>
|
|
205
|
+
{/* The morphing material — mirrors Popover's data-popover-surface
|
|
182
206
|
pattern. Starts at the source's rect (offset relative to the
|
|
183
207
|
panel) and grows to fill the panel, with the fluid overshoot
|
|
184
208
|
bounce. Border radius interpolates from the source's
|
|
185
209
|
"pillified" radius to the panel's authored radius. */}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
210
|
+
<motion.div
|
|
211
|
+
key={reveal ? "surface" : "measure"}
|
|
212
|
+
aria-hidden="true"
|
|
213
|
+
data-morph-surface=""
|
|
214
|
+
onAnimationComplete={() => {
|
|
215
|
+
if (reveal) setSettled(true);
|
|
216
|
+
}}
|
|
217
|
+
className="glass-menu rounded-5xl pointer-events-none absolute top-0 left-0 -z-10"
|
|
218
|
+
style={{ width: "100%", height: "100%" }}
|
|
219
|
+
initial={reducedMotion || !reveal ? { opacity: 0 } : reveal}
|
|
220
|
+
animate={
|
|
221
|
+
target
|
|
222
|
+
? {
|
|
223
|
+
x: 0,
|
|
224
|
+
y: 0,
|
|
225
|
+
width: target.width,
|
|
226
|
+
height: target.height,
|
|
227
|
+
opacity: 1,
|
|
228
|
+
borderRadius: target.radius,
|
|
229
|
+
}
|
|
230
|
+
: undefined
|
|
231
|
+
}
|
|
232
|
+
exit={
|
|
233
|
+
reducedMotion || !reveal
|
|
234
|
+
? { opacity: 0 }
|
|
235
|
+
: { ...menuSurfaceExit(reveal), transition: fluidCollapse }
|
|
236
|
+
}
|
|
237
|
+
transition={
|
|
238
|
+
reducedMotion
|
|
239
|
+
? { duration: 0 }
|
|
240
|
+
: { ...fluidOvershoot, opacity: { duration: 0.18 } }
|
|
241
|
+
}
|
|
242
|
+
/>
|
|
243
|
+
<motion.div
|
|
244
|
+
className="flex min-h-0 flex-1 flex-col overflow-y-auto"
|
|
245
|
+
initial={reducedMotion ? false : contentCrossfade.initial}
|
|
246
|
+
animate={reducedMotion ? undefined : contentCrossfade.animate}
|
|
247
|
+
exit={reducedMotion ? undefined : contentCrossfade.exit}
|
|
248
|
+
transition={
|
|
249
|
+
reducedMotion ? { duration: 0 } : contentCrossfade.transition
|
|
250
|
+
}
|
|
251
|
+
>
|
|
252
|
+
{children}
|
|
253
|
+
</motion.div>
|
|
254
|
+
</motion.div>
|
|
255
|
+
</div>
|
|
256
|
+
</SheetPortal>
|
|
257
|
+
);
|
|
232
258
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react"
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Disclosure state that keeps the overlay mounted through its exit animation,
|
|
@@ -6,22 +6,22 @@ import { useCallback, useEffect, useState } from "react"
|
|
|
6
6
|
* for the source.
|
|
7
7
|
*/
|
|
8
8
|
export function useMorphDisclosure(disabled = false) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const [mounted, setMounted] = useState(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (disabled) setOpen(false);
|
|
13
|
+
}, [disabled]);
|
|
14
|
+
const show = useCallback(() => {
|
|
15
|
+
setMounted(true);
|
|
16
|
+
setOpen(true);
|
|
17
|
+
}, []);
|
|
18
|
+
const toggle = useCallback(() => {
|
|
19
|
+
setMounted(true);
|
|
20
|
+
setOpen((value) => !value);
|
|
21
|
+
}, []);
|
|
22
|
+
const close = useCallback(() => setOpen(false), []);
|
|
23
|
+
const onExited = useCallback(() => {
|
|
24
|
+
if (!open || disabled) setMounted(false);
|
|
25
|
+
}, [open, disabled]);
|
|
26
|
+
return { open: open && !disabled, mounted, show, toggle, close, onExited };
|
|
27
27
|
}
|