@refraction-ui/react 0.23.2 → 0.24.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/index.cjs +1271 -1300
- package/dist/index.cjs.map +1 -1
- package/dist/internal/composer/index.d.ts +30 -1
- package/dist/internal/page-header/index.d.ts +2 -2
- package/dist/internal/react-composer/index.d.ts +11 -3
- package/dist/react-avatar.js +17 -3
- package/dist/react-avatar.js.map +2 -2
- package/dist/react-composer.js +99 -7
- package/dist/react-composer.js.map +3 -3
- package/dist/react-dialog.js +53 -31
- package/dist/react-dialog.js.map +2 -2
- package/dist/react-dropdown-menu.js +32 -102
- package/dist/react-dropdown-menu.js.map +2 -2
- package/dist/react-page-header.js +1 -1
- package/dist/react-page-header.js.map +1 -1
- package/dist/react-tabs.js +4 -96
- package/dist/react-tabs.js.map +2 -2
- package/package.json +1 -1
package/dist/react-dialog.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createDialog,
|
|
3
2
|
dialogContentVariants,
|
|
4
3
|
overlayStyles
|
|
5
4
|
} from "./chunk-4ACSHPCM.js";
|
|
@@ -43,11 +42,10 @@ function Dialog({
|
|
|
43
42
|
},
|
|
44
43
|
[isControlled, onOpenChange]
|
|
45
44
|
);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
const api = apiRef.current;
|
|
45
|
+
const id = React.useId();
|
|
46
|
+
const contentId = `rfr-dialog-content-${id}`;
|
|
47
|
+
const titleId = `rfr-dialog-title-${id}`;
|
|
48
|
+
const descriptionId = `rfr-dialog-desc-${id}`;
|
|
51
49
|
const [hasTitle, setHasTitle] = React.useState(false);
|
|
52
50
|
const [hasDescription, setHasDescription] = React.useState(false);
|
|
53
51
|
const ctx = React.useMemo(
|
|
@@ -55,38 +53,66 @@ function Dialog({
|
|
|
55
53
|
open,
|
|
56
54
|
onOpenChange: handleOpenChange,
|
|
57
55
|
modal,
|
|
58
|
-
contentId
|
|
59
|
-
titleId
|
|
60
|
-
descriptionId
|
|
56
|
+
contentId,
|
|
57
|
+
titleId,
|
|
58
|
+
descriptionId,
|
|
61
59
|
hasTitle,
|
|
62
60
|
hasDescription,
|
|
63
61
|
setHasTitle,
|
|
64
62
|
setHasDescription
|
|
65
63
|
}),
|
|
66
|
-
[open, handleOpenChange, modal,
|
|
64
|
+
[open, handleOpenChange, modal, contentId, titleId, descriptionId, hasTitle, hasDescription]
|
|
67
65
|
);
|
|
68
66
|
return React.createElement(DialogContext.Provider, { value: ctx }, children);
|
|
69
67
|
}
|
|
70
68
|
var DialogTrigger = React.forwardRef(
|
|
71
|
-
function DialogTrigger2({ onClick, children, ...props }, ref) {
|
|
69
|
+
function DialogTrigger2({ asChild = false, onClick, children, ...props }, ref) {
|
|
72
70
|
const { open, onOpenChange, contentId } = useDialogContext();
|
|
73
|
-
const handleClick = (
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
const handleClick = (event) => {
|
|
72
|
+
onClick?.(event);
|
|
73
|
+
if (!event.defaultPrevented) onOpenChange(!open);
|
|
76
74
|
};
|
|
77
|
-
|
|
78
|
-
"button",
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
const triggerProps = {
|
|
76
|
+
type: "button",
|
|
77
|
+
"aria-expanded": open,
|
|
78
|
+
"aria-controls": contentId,
|
|
79
|
+
"aria-haspopup": "dialog",
|
|
80
|
+
...props,
|
|
81
|
+
onClick: handleClick
|
|
82
|
+
};
|
|
83
|
+
if (asChild) {
|
|
84
|
+
if (!React.isValidElement(children)) {
|
|
85
|
+
devWarn("dialog-trigger-asChild-child", "DialogTrigger: `asChild` expects a single React element child; rendering nothing.");
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const child = children;
|
|
89
|
+
const childRef = React.version.startsWith("18.") ? child.ref : child.props.ref;
|
|
90
|
+
return React.cloneElement(child, {
|
|
91
|
+
...triggerProps,
|
|
92
|
+
className: cn(props.className, child.props.className),
|
|
93
|
+
onClick: (event) => {
|
|
94
|
+
child.props.onClick?.(event);
|
|
95
|
+
if (!event.defaultPrevented) handleClick(event);
|
|
96
|
+
},
|
|
97
|
+
ref: (node) => {
|
|
98
|
+
const outerCleanup = typeof ref === "function" ? ref(node) : void 0;
|
|
99
|
+
if (ref && typeof ref !== "function") ref.current = node;
|
|
100
|
+
const childCleanup = typeof childRef === "function" ? childRef(node) : void 0;
|
|
101
|
+
if (childRef && typeof childRef !== "function") childRef.current = node;
|
|
102
|
+
if (typeof outerCleanup === "function" || typeof childCleanup === "function") {
|
|
103
|
+
return () => {
|
|
104
|
+
if (typeof outerCleanup === "function") outerCleanup();
|
|
105
|
+
else if (typeof ref === "function") ref(null);
|
|
106
|
+
else if (ref) ref.current = null;
|
|
107
|
+
if (typeof childCleanup === "function") childCleanup();
|
|
108
|
+
else if (typeof childRef === "function") childRef(null);
|
|
109
|
+
else if (childRef) childRef.current = null;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return React.createElement("button", { ref, ...triggerProps }, children);
|
|
90
116
|
}
|
|
91
117
|
);
|
|
92
118
|
var DialogOverlay = React.forwardRef(
|
|
@@ -119,10 +145,6 @@ function containsElement(node, type) {
|
|
|
119
145
|
var DialogContent = React.forwardRef(
|
|
120
146
|
function DialogContent2({ className, children, onKeyDown, ...props }, ref) {
|
|
121
147
|
const { open, onOpenChange, modal, contentId, titleId, descriptionId, hasTitle, hasDescription } = useDialogContext();
|
|
122
|
-
React.useMemo(
|
|
123
|
-
() => createDialog({ open, modal }),
|
|
124
|
-
[open, modal]
|
|
125
|
-
);
|
|
126
148
|
const handleKeyDown = (e) => {
|
|
127
149
|
const handler = createKeyboardHandler({
|
|
128
150
|
Escape: (ev) => {
|
package/dist/react-dialog.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../react-dialog/src/dialog.tsx"],
|
|
4
|
-
"sourcesContent": ["import * as React from 'react'\nimport * as ReactDOM from 'react-dom'\nimport {\n createDialog,\n overlayStyles,\n dialogContentVariants,\n type DialogProps as CoreDialogProps,\n} from '@refraction-ui/dialog'\nimport { cn, createKeyboardHandler, devWarn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\ninterface DialogContextValue {\n open: boolean\n onOpenChange: (open: boolean) => void\n modal: boolean\n contentId: string\n titleId: string\n descriptionId: string\n /** Whether a DialogTitle / DialogDescription is mounted — only then is it referenced. */\n hasTitle: boolean\n hasDescription: boolean\n setHasTitle: (present: boolean) => void\n setHasDescription: (present: boolean) => void\n}\n\nconst DialogContext = React.createContext<DialogContextValue | null>(null)\n\nfunction useDialogContext(): DialogContextValue {\n const ctx = React.useContext(DialogContext)\n if (!ctx) {\n devWarn(\n 'react-dialog/context-outside-provider',\n 'Dialog compound components (DialogTrigger/DialogContent/DialogTitle/etc.) must be rendered inside a <Dialog>. The missing DialogContext makes this throw.',\n )\n throw new Error('Dialog compound components must be used within <Dialog>')\n }\n return ctx\n}\n\n// ---------------------------------------------------------------------------\n// Dialog (root provider)\n// ---------------------------------------------------------------------------\n\nexport interface DialogProps {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n modal?: boolean\n children?: React.ReactNode\n}\n\nexport function Dialog({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n modal = true,\n children,\n}: DialogProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n },\n [isControlled, onOpenChange],\n )\n\n // Use the headless core to get stable IDs\n const apiRef = React.useRef<ReturnType<typeof createDialog> | null>(null)\n if (apiRef.current === null) {\n apiRef.current = createDialog({ open, modal })\n }\n const api = apiRef.current\n const [hasTitle, setHasTitle] = React.useState(false)\n const [hasDescription, setHasDescription] = React.useState(false)\n\n const ctx = React.useMemo<DialogContextValue>(\n () => ({\n open,\n onOpenChange: handleOpenChange,\n modal,\n contentId: api.ids.content,\n titleId: api.ids.title,\n descriptionId: api.ids.description,\n hasTitle,\n hasDescription,\n setHasTitle,\n setHasDescription,\n }),\n [open, handleOpenChange, modal, api.ids.content, api.ids.title, api.ids.description, hasTitle, hasDescription],\n )\n\n return React.createElement(DialogContext.Provider, { value: ctx }, children)\n}\n\n// ---------------------------------------------------------------------------\n// DialogTrigger\n// ---------------------------------------------------------------------------\n\nexport interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n asChild?: boolean\n}\n\nexport const DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(\n function DialogTrigger({ onClick, children, ...props }, ref) {\n const { open, onOpenChange, contentId } = useDialogContext()\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onOpenChange(!open)\n onClick?.(e)\n }\n\n return React.createElement(\n 'button',\n {\n ref,\n type: 'button',\n 'aria-expanded': open,\n 'aria-controls': contentId,\n 'aria-haspopup': 'dialog',\n onClick: handleClick,\n ...props,\n },\n children,\n )\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogOverlay\n// ---------------------------------------------------------------------------\n\nexport interface DialogOverlayProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogOverlay = React.forwardRef<HTMLDivElement, DialogOverlayProps>(\n function DialogOverlay({ className, onClick, ...props }, ref) {\n const { open, onOpenChange } = useDialogContext()\n\n if (!open) return null\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n // Only close if clicking the overlay itself, not children\n if (e.target === e.currentTarget) {\n onOpenChange(false)\n }\n onClick?.(e)\n }\n\n return React.createElement('div', {\n ref,\n className: cn(overlayStyles, className),\n 'data-state': open ? 'open' : 'closed',\n onClick: handleClick,\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogContent\n// ---------------------------------------------------------------------------\n\nexport interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Whether `type` appears in the element tree — covers the first and server\n * render, before a mounted DialogTitle/DialogDescription has registered.\n * Parts nested inside custom components are found once they mount.\n */\nfunction containsElement(node: React.ReactNode, type: React.ElementType): boolean {\n let found = false\n React.Children.forEach(node, (child) => {\n if (found || !React.isValidElement<{ children?: React.ReactNode }>(child)) return\n found = child.type === type || containsElement(child.props.children, type)\n })\n return found\n}\n\nexport const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(\n function DialogContent({ className, children, onKeyDown, ...props }, ref) {\n const { open, onOpenChange, modal, contentId, titleId, descriptionId, hasTitle, hasDescription } =\n useDialogContext()\n\n const api = React.useMemo(\n () => createDialog({ open, modal }),\n [open, modal],\n )\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const handler = createKeyboardHandler({\n Escape: (ev) => {\n ev.preventDefault()\n onOpenChange(false)\n },\n })\n handler(e.nativeEvent)\n onKeyDown?.(e)\n }\n\n if (!open) return null\n\n const content = React.createElement(\n 'div',\n {\n ref,\n role: 'dialog',\n 'aria-modal': modal,\n // Reference only parts that exist: an id pointing at nothing leaves the\n // dialog unnamed while looking labelled.\n 'aria-labelledby': hasTitle || containsElement(children, DialogTitle) ? titleId : undefined,\n 'aria-describedby':\n hasDescription || containsElement(children, DialogDescription) ? descriptionId : undefined,\n id: contentId,\n className: cn(dialogContentVariants(), className),\n onKeyDown: handleKeyDown,\n tabIndex: -1,\n ...props,\n },\n children,\n )\n\n // Render via portal if document is available (client-side)\n if (typeof document !== 'undefined') {\n return ReactDOM.createPortal(content, document.body)\n }\n\n // SSR fallback: render inline\n return content\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogHeader\n// ---------------------------------------------------------------------------\n\nexport interface DialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(\n function DialogHeader({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn('flex flex-col space-y-1.5 text-center sm:text-left', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogFooter\n// ---------------------------------------------------------------------------\n\nexport interface DialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogFooter = React.forwardRef<HTMLDivElement, DialogFooterProps>(\n function DialogFooter({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn(\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\n className,\n ),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogTitle\n// ---------------------------------------------------------------------------\n\nexport interface DialogTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nexport const DialogTitle = React.forwardRef<HTMLHeadingElement, DialogTitleProps>(\n function DialogTitle({ className, ...props }, ref) {\n const { titleId, setHasTitle } = useDialogContext()\n React.useEffect(() => {\n setHasTitle(true)\n return () => setHasTitle(false)\n }, [setHasTitle])\n\n return React.createElement('h2', {\n ref,\n id: titleId,\n className: cn('text-lg font-semibold leading-none tracking-tight', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogDescription\n// ---------------------------------------------------------------------------\n\nexport interface DialogDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nexport const DialogDescription = React.forwardRef<\n HTMLParagraphElement,\n DialogDescriptionProps\n>(function DialogDescription({ className, ...props }, ref) {\n const { descriptionId, setHasDescription } = useDialogContext()\n React.useEffect(() => {\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return React.createElement('p', {\n ref,\n id: descriptionId,\n className: cn('text-sm text-muted-foreground', className),\n ...props,\n })\n})\n\n// ---------------------------------------------------------------------------\n// DialogClose\n// ---------------------------------------------------------------------------\n\nexport interface DialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\nexport const DialogClose = React.forwardRef<HTMLButtonElement, DialogCloseProps>(\n function DialogClose({ onClick, children, ...props }, ref) {\n const { onOpenChange } = useDialogContext()\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onOpenChange(false)\n onClick?.(e)\n }\n\n return React.createElement(\n 'button',\n {\n ref,\n type: 'button',\n onClick: handleClick,\n ...props,\n },\n children,\n )\n },\n)\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import * as React from 'react'\nimport * as ReactDOM from 'react-dom'\nimport {\n overlayStyles,\n dialogContentVariants,\n} from '@refraction-ui/dialog'\nimport { cn, createKeyboardHandler, devWarn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\ninterface DialogContextValue {\n open: boolean\n onOpenChange: (open: boolean) => void\n modal: boolean\n contentId: string\n titleId: string\n descriptionId: string\n /** Whether a DialogTitle / DialogDescription is mounted — only then is it referenced. */\n hasTitle: boolean\n hasDescription: boolean\n setHasTitle: (present: boolean) => void\n setHasDescription: (present: boolean) => void\n}\n\nconst DialogContext = React.createContext<DialogContextValue | null>(null)\n\nfunction useDialogContext(): DialogContextValue {\n const ctx = React.useContext(DialogContext)\n if (!ctx) {\n devWarn(\n 'react-dialog/context-outside-provider',\n 'Dialog compound components (DialogTrigger/DialogContent/DialogTitle/etc.) must be rendered inside a <Dialog>. The missing DialogContext makes this throw.',\n )\n throw new Error('Dialog compound components must be used within <Dialog>')\n }\n return ctx\n}\n\n// ---------------------------------------------------------------------------\n// Dialog (root provider)\n// ---------------------------------------------------------------------------\n\nexport interface DialogProps {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n modal?: boolean\n children?: React.ReactNode\n}\n\nexport function Dialog({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n modal = true,\n children,\n}: DialogProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n },\n [isControlled, onOpenChange],\n )\n\n // React's tree-derived IDs survive SSR requests and client hydration.\n const id = React.useId()\n const contentId = `rfr-dialog-content-${id}`\n const titleId = `rfr-dialog-title-${id}`\n const descriptionId = `rfr-dialog-desc-${id}`\n const [hasTitle, setHasTitle] = React.useState(false)\n const [hasDescription, setHasDescription] = React.useState(false)\n\n const ctx = React.useMemo<DialogContextValue>(\n () => ({\n open,\n onOpenChange: handleOpenChange,\n modal,\n contentId,\n titleId,\n descriptionId,\n hasTitle,\n hasDescription,\n setHasTitle,\n setHasDescription,\n }),\n [open, handleOpenChange, modal, contentId, titleId, descriptionId, hasTitle, hasDescription],\n )\n\n return React.createElement(DialogContext.Provider, { value: ctx }, children)\n}\n\n// ---------------------------------------------------------------------------\n// DialogTrigger\n// ---------------------------------------------------------------------------\n\nexport interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n asChild?: boolean\n}\n\nexport const DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(\n function DialogTrigger({ asChild = false, onClick, children, ...props }, ref) {\n const { open, onOpenChange, contentId } = useDialogContext()\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event)\n if (!event.defaultPrevented) onOpenChange(!open)\n }\n const triggerProps = {\n type: 'button' as const,\n 'aria-expanded': open,\n 'aria-controls': contentId,\n 'aria-haspopup': 'dialog' as const,\n ...props,\n onClick: handleClick,\n }\n\n if (asChild) {\n if (!React.isValidElement(children)) {\n devWarn('dialog-trigger-asChild-child', 'DialogTrigger: `asChild` expects a single React element child; rendering nothing.')\n return null\n }\n const child = children as React.ReactElement<React.ButtonHTMLAttributes<HTMLButtonElement> & {\n ref?: React.Ref<HTMLButtonElement>\n }> & { ref?: React.Ref<HTMLButtonElement> }\n // React 19 moved ref into props; reading element.ref there emits a warning.\n const childRef = React.version.startsWith('18.') ? child.ref : child.props.ref\n return React.cloneElement(child, {\n ...triggerProps,\n className: cn(props.className, child.props.className),\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n child.props.onClick?.(event)\n if (!event.defaultPrevented) handleClick(event)\n },\n ref: (node: HTMLButtonElement | null) => {\n const outerCleanup: unknown = typeof ref === 'function' ? ref(node) : undefined\n if (ref && typeof ref !== 'function') ref.current = node\n const childCleanup: unknown = typeof childRef === 'function' ? childRef(node) : undefined\n if (childRef && typeof childRef !== 'function') childRef.current = node\n if (typeof outerCleanup === 'function' || typeof childCleanup === 'function') {\n return () => {\n if (typeof outerCleanup === 'function') outerCleanup()\n else if (typeof ref === 'function') ref(null)\n else if (ref) ref.current = null\n if (typeof childCleanup === 'function') childCleanup()\n else if (typeof childRef === 'function') childRef(null)\n else if (childRef) childRef.current = null\n }\n }\n },\n })\n }\n\n return React.createElement('button', { ref, ...triggerProps }, children)\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogOverlay\n// ---------------------------------------------------------------------------\n\nexport interface DialogOverlayProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogOverlay = React.forwardRef<HTMLDivElement, DialogOverlayProps>(\n function DialogOverlay({ className, onClick, ...props }, ref) {\n const { open, onOpenChange } = useDialogContext()\n\n if (!open) return null\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n // Only close if clicking the overlay itself, not children\n if (e.target === e.currentTarget) {\n onOpenChange(false)\n }\n onClick?.(e)\n }\n\n return React.createElement('div', {\n ref,\n className: cn(overlayStyles, className),\n 'data-state': open ? 'open' : 'closed',\n onClick: handleClick,\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogContent\n// ---------------------------------------------------------------------------\n\nexport interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Whether `type` appears in the element tree — covers the first and server\n * render, before a mounted DialogTitle/DialogDescription has registered.\n * Parts nested inside custom components are found once they mount.\n */\nfunction containsElement(node: React.ReactNode, type: React.ElementType): boolean {\n let found = false\n React.Children.forEach(node, (child) => {\n if (found || !React.isValidElement<{ children?: React.ReactNode }>(child)) return\n found = child.type === type || containsElement(child.props.children, type)\n })\n return found\n}\n\nexport const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(\n function DialogContent({ className, children, onKeyDown, ...props }, ref) {\n const { open, onOpenChange, modal, contentId, titleId, descriptionId, hasTitle, hasDescription } =\n useDialogContext()\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const handler = createKeyboardHandler({\n Escape: (ev) => {\n ev.preventDefault()\n onOpenChange(false)\n },\n })\n handler(e.nativeEvent)\n onKeyDown?.(e)\n }\n\n if (!open) return null\n\n const content = React.createElement(\n 'div',\n {\n ref,\n role: 'dialog',\n 'aria-modal': modal,\n // Reference only parts that exist: an id pointing at nothing leaves the\n // dialog unnamed while looking labelled.\n 'aria-labelledby': hasTitle || containsElement(children, DialogTitle) ? titleId : undefined,\n 'aria-describedby':\n hasDescription || containsElement(children, DialogDescription) ? descriptionId : undefined,\n id: contentId,\n className: cn(dialogContentVariants(), className),\n onKeyDown: handleKeyDown,\n tabIndex: -1,\n ...props,\n },\n children,\n )\n\n // Render via portal if document is available (client-side)\n if (typeof document !== 'undefined') {\n return ReactDOM.createPortal(content, document.body)\n }\n\n // SSR fallback: render inline\n return content\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogHeader\n// ---------------------------------------------------------------------------\n\nexport interface DialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogHeader = React.forwardRef<HTMLDivElement, DialogHeaderProps>(\n function DialogHeader({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn('flex flex-col space-y-1.5 text-center sm:text-left', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogFooter\n// ---------------------------------------------------------------------------\n\nexport interface DialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DialogFooter = React.forwardRef<HTMLDivElement, DialogFooterProps>(\n function DialogFooter({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn(\n 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',\n className,\n ),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogTitle\n// ---------------------------------------------------------------------------\n\nexport interface DialogTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}\n\nexport const DialogTitle = React.forwardRef<HTMLHeadingElement, DialogTitleProps>(\n function DialogTitle({ className, ...props }, ref) {\n const { titleId, setHasTitle } = useDialogContext()\n React.useEffect(() => {\n setHasTitle(true)\n return () => setHasTitle(false)\n }, [setHasTitle])\n\n return React.createElement('h2', {\n ref,\n id: titleId,\n className: cn('text-lg font-semibold leading-none tracking-tight', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DialogDescription\n// ---------------------------------------------------------------------------\n\nexport interface DialogDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}\n\nexport const DialogDescription = React.forwardRef<\n HTMLParagraphElement,\n DialogDescriptionProps\n>(function DialogDescription({ className, ...props }, ref) {\n const { descriptionId, setHasDescription } = useDialogContext()\n React.useEffect(() => {\n setHasDescription(true)\n return () => setHasDescription(false)\n }, [setHasDescription])\n\n return React.createElement('p', {\n ref,\n id: descriptionId,\n className: cn('text-sm text-muted-foreground', className),\n ...props,\n })\n})\n\n// ---------------------------------------------------------------------------\n// DialogClose\n// ---------------------------------------------------------------------------\n\nexport interface DialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}\n\nexport const DialogClose = React.forwardRef<HTMLButtonElement, DialogCloseProps>(\n function DialogClose({ onClick, children, ...props }, ref) {\n const { onOpenChange } = useDialogContext()\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onOpenChange(false)\n onClick?.(e)\n }\n\n return React.createElement(\n 'button',\n {\n ref,\n type: 'button',\n onClick: handleClick,\n ...props,\n },\n children,\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DialogTrigger", "DialogOverlay", "DialogContent", "DialogHeader", "DialogFooter", "DialogTitle", "DialogDescription", "DialogClose"]
|
|
7
7
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Keys,
|
|
3
2
|
cn,
|
|
4
|
-
createKeyboardHandler,
|
|
5
|
-
createMachine,
|
|
6
3
|
cva,
|
|
7
|
-
devWarn
|
|
8
|
-
generateId
|
|
4
|
+
devWarn
|
|
9
5
|
} from "./chunk-75BKRTPO.js";
|
|
10
6
|
|
|
11
7
|
// ../react-dropdown-menu/dist/index.js
|
|
@@ -13,88 +9,6 @@ import * as React from "react";
|
|
|
13
9
|
import * as ReactDOM from "react-dom";
|
|
14
10
|
|
|
15
11
|
// ../dropdown-menu/dist/index.js
|
|
16
|
-
function createDropdownMenu(props = {}) {
|
|
17
|
-
const { open: controlledOpen, defaultOpen = false, onOpenChange } = props;
|
|
18
|
-
const machine = createMachine({
|
|
19
|
-
initial: controlledOpen ?? defaultOpen ? "open" : "closed",
|
|
20
|
-
states: {
|
|
21
|
-
closed: { on: { OPEN: "open" } },
|
|
22
|
-
open: { on: { CLOSE: "closed" } }
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
const contentId = generateId("rfr-dropdown-menu");
|
|
26
|
-
function isOpen() {
|
|
27
|
-
if (controlledOpen !== void 0) return controlledOpen;
|
|
28
|
-
return machine.matches("open");
|
|
29
|
-
}
|
|
30
|
-
function openMenu() {
|
|
31
|
-
machine.send("OPEN");
|
|
32
|
-
onOpenChange?.(true);
|
|
33
|
-
}
|
|
34
|
-
function closeMenu() {
|
|
35
|
-
machine.send("CLOSE");
|
|
36
|
-
onOpenChange?.(false);
|
|
37
|
-
}
|
|
38
|
-
function toggleMenu() {
|
|
39
|
-
if (isOpen()) {
|
|
40
|
-
closeMenu();
|
|
41
|
-
} else {
|
|
42
|
-
openMenu();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function getItemProps(item) {
|
|
46
|
-
return {
|
|
47
|
-
role: "menuitem",
|
|
48
|
-
tabIndex: item.disabled ? -1 : 0,
|
|
49
|
-
"data-disabled": item.disabled ? "" : void 0,
|
|
50
|
-
"aria-disabled": item.disabled || void 0
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
const keyboardHandlers = {
|
|
54
|
-
[Keys.Escape]: (e) => {
|
|
55
|
-
e.preventDefault();
|
|
56
|
-
closeMenu();
|
|
57
|
-
},
|
|
58
|
-
[Keys.ArrowDown]: (e) => {
|
|
59
|
-
e.preventDefault();
|
|
60
|
-
},
|
|
61
|
-
[Keys.ArrowUp]: (e) => {
|
|
62
|
-
e.preventDefault();
|
|
63
|
-
},
|
|
64
|
-
[Keys.Enter]: (e) => {
|
|
65
|
-
e.preventDefault();
|
|
66
|
-
},
|
|
67
|
-
[Keys.Home]: (e) => {
|
|
68
|
-
e.preventDefault();
|
|
69
|
-
},
|
|
70
|
-
[Keys.End]: (e) => {
|
|
71
|
-
e.preventDefault();
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const triggerProps = {
|
|
75
|
-
"aria-expanded": isOpen(),
|
|
76
|
-
"aria-controls": contentId,
|
|
77
|
-
"aria-haspopup": "menu"
|
|
78
|
-
};
|
|
79
|
-
const contentProps = {
|
|
80
|
-
role: "menu",
|
|
81
|
-
id: contentId,
|
|
82
|
-
"data-state": isOpen() ? "open" : "closed"
|
|
83
|
-
};
|
|
84
|
-
return {
|
|
85
|
-
state: { open: isOpen() },
|
|
86
|
-
triggerProps,
|
|
87
|
-
contentProps,
|
|
88
|
-
getItemProps,
|
|
89
|
-
open: openMenu,
|
|
90
|
-
close: closeMenu,
|
|
91
|
-
toggle: toggleMenu,
|
|
92
|
-
keyboardHandlers,
|
|
93
|
-
ids: {
|
|
94
|
-
content: contentId
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
12
|
var menuContentVariants = cva({
|
|
99
13
|
base: "z-50 rounded-md border bg-popover shadow-md"
|
|
100
14
|
});
|
|
@@ -133,20 +47,16 @@ function DropdownMenu({
|
|
|
133
47
|
},
|
|
134
48
|
[isControlled, onOpenChange]
|
|
135
49
|
);
|
|
136
|
-
const
|
|
137
|
-
if (apiRef.current === null) {
|
|
138
|
-
apiRef.current = createDropdownMenu({ open });
|
|
139
|
-
}
|
|
140
|
-
const api = apiRef.current;
|
|
50
|
+
const contentId = `rfr-dropdown-menu-${React.useId()}`;
|
|
141
51
|
const triggerRef = React.useRef(null);
|
|
142
52
|
const ctx = React.useMemo(
|
|
143
53
|
() => ({
|
|
144
54
|
open,
|
|
145
55
|
onOpenChange: handleOpenChange,
|
|
146
|
-
contentId
|
|
56
|
+
contentId,
|
|
147
57
|
triggerRef
|
|
148
58
|
}),
|
|
149
|
-
[open, handleOpenChange,
|
|
59
|
+
[open, handleOpenChange, contentId]
|
|
150
60
|
);
|
|
151
61
|
return React.createElement(DropdownMenuContext.Provider, { value: ctx }, children);
|
|
152
62
|
}
|
|
@@ -245,15 +155,33 @@ var DropdownMenuContent = React.forwardRef(
|
|
|
245
155
|
document.addEventListener("pointerdown", onPointerDown);
|
|
246
156
|
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
247
157
|
}, [open, onOpenChange, triggerRef]);
|
|
158
|
+
React.useEffect(() => {
|
|
159
|
+
if (!open) return;
|
|
160
|
+
contentRef.current?.querySelector('[role="menuitem"]:not([aria-disabled="true"])')?.focus({ preventScroll: true });
|
|
161
|
+
}, [open]);
|
|
248
162
|
const handleKeyDown = (e) => {
|
|
249
|
-
const handler = createKeyboardHandler({
|
|
250
|
-
Escape: (ev) => {
|
|
251
|
-
ev.preventDefault();
|
|
252
|
-
onOpenChange(false);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
handler(e.nativeEvent);
|
|
256
163
|
onKeyDown?.(e);
|
|
164
|
+
if (e.defaultPrevented) return;
|
|
165
|
+
if (e.key === "Escape") {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
onOpenChange(false);
|
|
168
|
+
triggerRef.current?.focus();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (e.key === "Tab") {
|
|
172
|
+
triggerRef.current?.focus();
|
|
173
|
+
onOpenChange(false);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key)) return;
|
|
177
|
+
const items = Array.from(contentRef.current?.querySelectorAll(
|
|
178
|
+
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
179
|
+
) ?? []);
|
|
180
|
+
if (items.length === 0) return;
|
|
181
|
+
e.preventDefault();
|
|
182
|
+
const current = items.indexOf(document.activeElement);
|
|
183
|
+
const index = e.key === "Home" ? 0 : e.key === "End" ? items.length - 1 : e.key === "ArrowDown" ? (current + 1) % items.length : (current - 1 + items.length) % items.length;
|
|
184
|
+
items[index].focus();
|
|
257
185
|
};
|
|
258
186
|
if (!open) return null;
|
|
259
187
|
const content = React.createElement(
|
|
@@ -280,11 +208,12 @@ var DropdownMenuContent = React.forwardRef(
|
|
|
280
208
|
);
|
|
281
209
|
var DropdownMenuItem = React.forwardRef(
|
|
282
210
|
function DropdownMenuItem2({ className, disabled, onSelect, onClick, children, ...props }, ref) {
|
|
283
|
-
const { onOpenChange } = useDropdownMenuContext();
|
|
211
|
+
const { onOpenChange, triggerRef } = useDropdownMenuContext();
|
|
284
212
|
const handleClick = (e) => {
|
|
285
213
|
if (disabled) return;
|
|
286
214
|
onSelect?.();
|
|
287
215
|
onOpenChange(false);
|
|
216
|
+
triggerRef.current?.focus();
|
|
288
217
|
onClick?.(e);
|
|
289
218
|
};
|
|
290
219
|
const handleKeyDown = (e) => {
|
|
@@ -293,6 +222,7 @@ var DropdownMenuItem = React.forwardRef(
|
|
|
293
222
|
e.preventDefault();
|
|
294
223
|
onSelect?.();
|
|
295
224
|
onOpenChange(false);
|
|
225
|
+
triggerRef.current?.focus();
|
|
296
226
|
}
|
|
297
227
|
};
|
|
298
228
|
return React.createElement(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../dropdown-menu/src/dropdown-menu.ts", "../../dropdown-menu/src/dropdown-menu.styles.ts", "../../react-dropdown-menu/src/dropdown-menu.tsx"],
|
|
4
|
-
"sourcesContent": ["import type { AccessibilityProps, KeyboardHandlerMap } from '@refraction-ui/shared'\nimport { createMachine, Keys, generateId } from '@refraction-ui/shared'\n\nexport interface DropdownMenuProps {\n /** Whether the menu is open (controlled) */\n open?: boolean\n /** Whether the menu starts open (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void\n}\n\nexport interface MenuItemProps {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when the item is selected */\n onSelect?: () => void\n}\n\nexport interface DropdownMenuState {\n open: boolean\n}\n\nexport interface DropdownMenuAPI {\n /** Current menu state */\n state: DropdownMenuState\n /** ARIA attributes for the trigger button */\n triggerProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** ARIA attributes for the menu content */\n contentProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** Factory for menu item props */\n getItemProps(item: MenuItemProps): Partial<AccessibilityProps> & Record<string, unknown>\n /** Open the menu */\n open(): void\n /** Close the menu */\n close(): void\n /** Toggle the menu */\n toggle(): void\n /** Keyboard handlers for the menu */\n keyboardHandlers: KeyboardHandlerMap\n /** Generated IDs for aria linking */\n ids: {\n content: string\n }\n}\n\nexport function createDropdownMenu(props: DropdownMenuProps = {}): DropdownMenuAPI {\n const { open: controlledOpen, defaultOpen = false, onOpenChange } = props\n\n const machine = createMachine<'open' | 'closed', 'OPEN' | 'CLOSE'>({\n initial: controlledOpen ?? defaultOpen ? 'open' : 'closed',\n states: {\n closed: { on: { OPEN: 'open' } },\n open: { on: { CLOSE: 'closed' } },\n },\n })\n\n const contentId = generateId('rfr-dropdown-menu')\n\n function isOpen(): boolean {\n if (controlledOpen !== undefined) return controlledOpen\n return machine.matches('open')\n }\n\n function openMenu(): void {\n machine.send('OPEN')\n onOpenChange?.(true)\n }\n\n function closeMenu(): void {\n machine.send('CLOSE')\n onOpenChange?.(false)\n }\n\n function toggleMenu(): void {\n if (isOpen()) {\n closeMenu()\n } else {\n openMenu()\n }\n }\n\n function getItemProps(item: MenuItemProps): Partial<AccessibilityProps> & Record<string, unknown> {\n return {\n role: 'menuitem',\n tabIndex: item.disabled ? -1 : 0,\n 'data-disabled': item.disabled ? '' : undefined,\n 'aria-disabled': item.disabled || undefined,\n }\n }\n\n const keyboardHandlers: KeyboardHandlerMap = {\n [Keys.Escape]: (e: KeyboardEvent) => {\n e.preventDefault()\n closeMenu()\n },\n [Keys.ArrowDown]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.ArrowUp]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.Enter]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.Home]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.End]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n }\n\n const triggerProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n 'aria-expanded': isOpen(),\n 'aria-controls': contentId,\n 'aria-haspopup': 'menu',\n }\n\n const contentProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n role: 'menu',\n id: contentId,\n 'data-state': isOpen() ? 'open' : 'closed',\n }\n\n return {\n state: { open: isOpen() },\n triggerProps,\n contentProps,\n getItemProps,\n open: openMenu,\n close: closeMenu,\n toggle: toggleMenu,\n keyboardHandlers,\n ids: {\n content: contentId,\n },\n }\n}\n", "import { cva } from '@refraction-ui/shared'\n\nexport const menuContentVariants = cva({\n base: 'z-50 rounded-md border bg-popover shadow-md',\n})\n\nexport const menuItemVariants = cva({\n base: 'relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n})\n", "import * as React from 'react'\nimport * as ReactDOM from 'react-dom'\nimport {\n createDropdownMenu,\n menuContentVariants,\n menuItemVariants,\n type DropdownMenuProps as CoreDropdownMenuProps,\n type MenuItemProps as CoreMenuItemProps,\n} from '@refraction-ui/dropdown-menu'\nimport { cn, createKeyboardHandler, devWarn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\ninterface DropdownMenuContextValue {\n open: boolean\n onOpenChange: (open: boolean) => void\n contentId: string\n /** The trigger element, so the portalled content can anchor to it. */\n triggerRef: React.MutableRefObject<HTMLElement | null>\n}\n\nconst DropdownMenuContext = React.createContext<DropdownMenuContextValue | null>(null)\n\nfunction useDropdownMenuContext(): DropdownMenuContextValue {\n const ctx = React.useContext(DropdownMenuContext)\n if (!ctx) {\n devWarn(\n 'react-dropdown-menu/context-outside-provider',\n 'DropdownMenu compound components (DropdownMenuTrigger/DropdownMenuContent/DropdownMenuItem/etc.) must be rendered inside a <DropdownMenu>. The missing DropdownMenuContext makes this throw.',\n )\n throw new Error('DropdownMenu compound components must be used within <DropdownMenu>')\n }\n return ctx\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenu (root provider)\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuProps {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n children?: React.ReactNode\n}\n\nexport function DropdownMenu({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n children,\n}: DropdownMenuProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n },\n [isControlled, onOpenChange],\n )\n\n // Use the headless core to get stable IDs\n const apiRef = React.useRef<ReturnType<typeof createDropdownMenu> | null>(null)\n if (apiRef.current === null) {\n apiRef.current = createDropdownMenu({ open })\n }\n const api = apiRef.current\n\n const triggerRef = React.useRef<HTMLElement | null>(null)\n\n const ctx = React.useMemo<DropdownMenuContextValue>(\n () => ({\n open,\n onOpenChange: handleOpenChange,\n contentId: api.ids.content,\n triggerRef,\n }),\n [open, handleOpenChange, api.ids.content],\n )\n\n return React.createElement(DropdownMenuContext.Provider, { value: ctx }, children)\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenuTrigger\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Merge the trigger's props/behavior onto the child element instead of\n * rendering a `<button>` around it (Radix-style `asChild`). Use it with\n * styled triggers like `<Button>` — otherwise you'd nest a button in a\n * button (invalid HTML and a hydration error). */\n asChild?: boolean\n}\n\nexport const DropdownMenuTrigger = React.forwardRef<HTMLButtonElement, DropdownMenuTriggerProps>(\n function DropdownMenuTrigger({ asChild = false, onClick, children, ...props }, ref) {\n const { open, onOpenChange, contentId, triggerRef } = useDropdownMenuContext()\n const setRefs = (node: HTMLButtonElement | null) => {\n triggerRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onOpenChange(!open)\n onClick?.(e)\n }\n\n const triggerProps = {\n type: 'button' as const,\n 'aria-expanded': open,\n 'aria-controls': contentId,\n 'aria-haspopup': 'menu' as const,\n onClick: handleClick,\n ...props,\n }\n\n if (asChild) {\n if (!React.isValidElement(children)) {\n devWarn(\n 'dropdown-menu-trigger-asChild-child',\n 'DropdownMenuTrigger: `asChild` expects a single React element child; rendering nothing.',\n )\n return null\n }\n const child = children as React.ReactElement<\n Record<string, unknown>\n > & { ref?: React.Ref<HTMLButtonElement> }\n const childOnClick = child.props.onClick as\n | ((e: React.MouseEvent<HTMLButtonElement>) => void)\n | undefined\n const childRef = child.ref\n const mergedProps = {\n ...triggerProps,\n onClick: (e: React.MouseEvent<HTMLButtonElement>) => {\n childOnClick?.(e)\n if (!e.defaultPrevented) handleClick(e)\n },\n ref: (node: HTMLButtonElement | null) => {\n setRefs(node)\n if (typeof childRef === 'function') childRef(node)\n else if (childRef) childRef.current = node\n },\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return React.cloneElement(child, mergedProps as any)\n }\n\n return React.createElement('button', { ref: setRefs, ...triggerProps }, children)\n },\n)\n\n// ---------------------------------------------------------------------------\n// Positioning\n// ---------------------------------------------------------------------------\n\nexport type DropdownMenuAlign = 'start' | 'center' | 'end'\n\nconst DEFAULT_SIDE_OFFSET = 4\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\n/**\n * Fixed-position coordinates that place a menu under its trigger, aligned to\n * the trigger's start, centre or end edge. Pure, so it is unit-testable.\n */\nexport function computeMenuPosition(\n trigger: { top: number; bottom: number; left: number; right: number; width: number },\n contentWidth: number,\n align: DropdownMenuAlign,\n sideOffset: number,\n): React.CSSProperties {\n const left =\n align === 'end'\n ? trigger.right - contentWidth\n : align === 'center'\n ? trigger.left + trigger.width / 2 - contentWidth / 2\n : trigger.left\n return { position: 'fixed', top: trigger.bottom + sideOffset, left: Math.max(0, left), minWidth: trigger.width }\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenuContent\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuContentProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment to the trigger. Defaults to `start`. */\n align?: DropdownMenuAlign\n /** Gap between trigger and menu in px. Defaults to 4. */\n sideOffset?: number\n}\n\nexport const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContentProps>(\n function DropdownMenuContent(\n { className, children, onKeyDown, align = 'start', sideOffset = DEFAULT_SIDE_OFFSET, style, ...props },\n forwardedRef,\n ) {\n const { open, onOpenChange, contentId, triggerRef } = useDropdownMenuContext()\n const contentRef = React.useRef<HTMLDivElement | null>(null)\n const ref = React.useCallback(\n (node: HTMLDivElement | null) => {\n contentRef.current = node\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) forwardedRef.current = node\n },\n [forwardedRef],\n )\n const [position, setPosition] = React.useState<React.CSSProperties | null>(null)\n\n // The content is portalled to <body>, so anchor it to the trigger with\n // fixed coordinates, and follow the trigger on scroll / resize.\n useIsomorphicLayoutEffect(() => {\n if (!open) return\n const update = () => {\n const trigger = triggerRef.current\n const content = contentRef.current\n if (!trigger) return\n setPosition(\n computeMenuPosition(trigger.getBoundingClientRect(), content?.offsetWidth ?? 0, align, sideOffset),\n )\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [open, align, sideOffset, triggerRef])\n\n // Close on a pointer press outside the menu and its trigger.\n React.useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target as Node\n if (contentRef.current?.contains(target) || triggerRef.current?.contains(target)) return\n onOpenChange(false)\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, onOpenChange, triggerRef])\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const handler = createKeyboardHandler({\n Escape: (ev) => {\n ev.preventDefault()\n onOpenChange(false)\n },\n })\n handler(e.nativeEvent)\n onKeyDown?.(e)\n }\n\n if (!open) return null\n\n const content = React.createElement(\n 'div',\n {\n ref,\n role: 'menu',\n id: contentId,\n 'data-state': open ? 'open' : 'closed',\n className: cn(menuContentVariants(), className),\n style: position ? { ...position, ...style } : style,\n 'data-align': align,\n onKeyDown: handleKeyDown,\n tabIndex: -1,\n ...props,\n },\n children,\n )\n\n // Render via portal if document is available (client-side)\n if (typeof document !== 'undefined') {\n return ReactDOM.createPortal(content, document.body)\n }\n\n // SSR fallback: render inline\n return content\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuItem\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n disabled?: boolean\n onSelect?: () => void\n}\n\nexport const DropdownMenuItem = React.forwardRef<HTMLDivElement, DropdownMenuItemProps>(\n function DropdownMenuItem({ className, disabled, onSelect, onClick, children, ...props }, ref) {\n const { onOpenChange } = useDropdownMenuContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onSelect?.()\n onOpenChange(false)\n onClick?.(e)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onSelect?.()\n onOpenChange(false)\n }\n }\n\n return React.createElement(\n 'div',\n {\n ref,\n role: 'menuitem',\n tabIndex: disabled ? -1 : 0,\n 'data-disabled': disabled ? '' : undefined,\n 'aria-disabled': disabled || undefined,\n className: cn(menuItemVariants(), className),\n onClick: handleClick,\n onKeyDown: handleKeyDown,\n ...props,\n },\n children,\n )\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuSeparator\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DropdownMenuSeparator = React.forwardRef<HTMLDivElement, DropdownMenuSeparatorProps>(\n function DropdownMenuSeparator({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n role: 'separator',\n className: cn('-mx-1 my-1 h-px bg-muted', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuLabel\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DropdownMenuLabel = React.forwardRef<HTMLDivElement, DropdownMenuLabelProps>(\n function DropdownMenuLabel({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn('px-2 py-1.5 text-sm font-semibold', className),\n ...props,\n })\n },\n)\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { AccessibilityProps, KeyboardHandlerMap } from '@refraction-ui/shared'\nimport { createMachine, Keys, generateId } from '@refraction-ui/shared'\n\nexport interface DropdownMenuProps {\n /** Whether the menu is open (controlled) */\n open?: boolean\n /** Whether the menu starts open (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void\n}\n\nexport interface MenuItemProps {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Callback when the item is selected */\n onSelect?: () => void\n}\n\nexport interface DropdownMenuState {\n open: boolean\n}\n\nexport interface DropdownMenuAPI {\n /** Current menu state */\n state: DropdownMenuState\n /** ARIA attributes for the trigger button */\n triggerProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** ARIA attributes for the menu content */\n contentProps: Partial<AccessibilityProps> & Record<string, unknown>\n /** Factory for menu item props */\n getItemProps(item: MenuItemProps): Partial<AccessibilityProps> & Record<string, unknown>\n /** Open the menu */\n open(): void\n /** Close the menu */\n close(): void\n /** Toggle the menu */\n toggle(): void\n /** Keyboard handlers for the menu */\n keyboardHandlers: KeyboardHandlerMap\n /** Generated IDs for aria linking */\n ids: {\n content: string\n }\n}\n\nexport function createDropdownMenu(props: DropdownMenuProps = {}): DropdownMenuAPI {\n const { open: controlledOpen, defaultOpen = false, onOpenChange } = props\n\n const machine = createMachine<'open' | 'closed', 'OPEN' | 'CLOSE'>({\n initial: controlledOpen ?? defaultOpen ? 'open' : 'closed',\n states: {\n closed: { on: { OPEN: 'open' } },\n open: { on: { CLOSE: 'closed' } },\n },\n })\n\n const contentId = generateId('rfr-dropdown-menu')\n\n function isOpen(): boolean {\n if (controlledOpen !== undefined) return controlledOpen\n return machine.matches('open')\n }\n\n function openMenu(): void {\n machine.send('OPEN')\n onOpenChange?.(true)\n }\n\n function closeMenu(): void {\n machine.send('CLOSE')\n onOpenChange?.(false)\n }\n\n function toggleMenu(): void {\n if (isOpen()) {\n closeMenu()\n } else {\n openMenu()\n }\n }\n\n function getItemProps(item: MenuItemProps): Partial<AccessibilityProps> & Record<string, unknown> {\n return {\n role: 'menuitem',\n tabIndex: item.disabled ? -1 : 0,\n 'data-disabled': item.disabled ? '' : undefined,\n 'aria-disabled': item.disabled || undefined,\n }\n }\n\n const keyboardHandlers: KeyboardHandlerMap = {\n [Keys.Escape]: (e: KeyboardEvent) => {\n e.preventDefault()\n closeMenu()\n },\n [Keys.ArrowDown]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.ArrowUp]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.Enter]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.Home]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n [Keys.End]: (e: KeyboardEvent) => {\n e.preventDefault()\n },\n }\n\n const triggerProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n 'aria-expanded': isOpen(),\n 'aria-controls': contentId,\n 'aria-haspopup': 'menu',\n }\n\n const contentProps: Partial<AccessibilityProps> & Record<string, unknown> = {\n role: 'menu',\n id: contentId,\n 'data-state': isOpen() ? 'open' : 'closed',\n }\n\n return {\n state: { open: isOpen() },\n triggerProps,\n contentProps,\n getItemProps,\n open: openMenu,\n close: closeMenu,\n toggle: toggleMenu,\n keyboardHandlers,\n ids: {\n content: contentId,\n },\n }\n}\n", "import { cva } from '@refraction-ui/shared'\n\nexport const menuContentVariants = cva({\n base: 'z-50 rounded-md border bg-popover shadow-md',\n})\n\nexport const menuItemVariants = cva({\n base: 'relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n})\n", "import * as React from 'react'\nimport * as ReactDOM from 'react-dom'\nimport {\n menuContentVariants,\n menuItemVariants,\n type DropdownMenuProps as CoreDropdownMenuProps,\n type MenuItemProps as CoreMenuItemProps,\n} from '@refraction-ui/dropdown-menu'\nimport { cn, devWarn } from '@refraction-ui/shared'\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\ninterface DropdownMenuContextValue {\n open: boolean\n onOpenChange: (open: boolean) => void\n contentId: string\n /** The trigger element, so the portalled content can anchor to it. */\n triggerRef: React.MutableRefObject<HTMLElement | null>\n}\n\nconst DropdownMenuContext = React.createContext<DropdownMenuContextValue | null>(null)\n\nfunction useDropdownMenuContext(): DropdownMenuContextValue {\n const ctx = React.useContext(DropdownMenuContext)\n if (!ctx) {\n devWarn(\n 'react-dropdown-menu/context-outside-provider',\n 'DropdownMenu compound components (DropdownMenuTrigger/DropdownMenuContent/DropdownMenuItem/etc.) must be rendered inside a <DropdownMenu>. The missing DropdownMenuContext makes this throw.',\n )\n throw new Error('DropdownMenu compound components must be used within <DropdownMenu>')\n }\n return ctx\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenu (root provider)\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuProps {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n children?: React.ReactNode\n}\n\nexport function DropdownMenu({\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n children,\n}: DropdownMenuProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = controlledOpen !== undefined\n const open = isControlled ? controlledOpen : uncontrolledOpen\n\n const handleOpenChange = React.useCallback(\n (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n },\n [isControlled, onOpenChange],\n )\n\n // React IDs agree across SSR and hydration, unlike the core's process counter.\n const contentId = `rfr-dropdown-menu-${React.useId()}`\n\n const triggerRef = React.useRef<HTMLElement | null>(null)\n\n const ctx = React.useMemo<DropdownMenuContextValue>(\n () => ({\n open,\n onOpenChange: handleOpenChange,\n contentId,\n triggerRef,\n }),\n [open, handleOpenChange, contentId],\n )\n\n return React.createElement(DropdownMenuContext.Provider, { value: ctx }, children)\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenuTrigger\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Merge the trigger's props/behavior onto the child element instead of\n * rendering a `<button>` around it (Radix-style `asChild`). Use it with\n * styled triggers like `<Button>` — otherwise you'd nest a button in a\n * button (invalid HTML and a hydration error). */\n asChild?: boolean\n}\n\nexport const DropdownMenuTrigger = React.forwardRef<HTMLButtonElement, DropdownMenuTriggerProps>(\n function DropdownMenuTrigger({ asChild = false, onClick, children, ...props }, ref) {\n const { open, onOpenChange, contentId, triggerRef } = useDropdownMenuContext()\n const setRefs = (node: HTMLButtonElement | null) => {\n triggerRef.current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onOpenChange(!open)\n onClick?.(e)\n }\n\n const triggerProps = {\n type: 'button' as const,\n 'aria-expanded': open,\n 'aria-controls': contentId,\n 'aria-haspopup': 'menu' as const,\n onClick: handleClick,\n ...props,\n }\n\n if (asChild) {\n if (!React.isValidElement(children)) {\n devWarn(\n 'dropdown-menu-trigger-asChild-child',\n 'DropdownMenuTrigger: `asChild` expects a single React element child; rendering nothing.',\n )\n return null\n }\n const child = children as React.ReactElement<\n Record<string, unknown>\n > & { ref?: React.Ref<HTMLButtonElement> }\n const childOnClick = child.props.onClick as\n | ((e: React.MouseEvent<HTMLButtonElement>) => void)\n | undefined\n const childRef = child.ref\n const mergedProps = {\n ...triggerProps,\n onClick: (e: React.MouseEvent<HTMLButtonElement>) => {\n childOnClick?.(e)\n if (!e.defaultPrevented) handleClick(e)\n },\n ref: (node: HTMLButtonElement | null) => {\n setRefs(node)\n if (typeof childRef === 'function') childRef(node)\n else if (childRef) childRef.current = node\n },\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return React.cloneElement(child, mergedProps as any)\n }\n\n return React.createElement('button', { ref: setRefs, ...triggerProps }, children)\n },\n)\n\n// ---------------------------------------------------------------------------\n// Positioning\n// ---------------------------------------------------------------------------\n\nexport type DropdownMenuAlign = 'start' | 'center' | 'end'\n\nconst DEFAULT_SIDE_OFFSET = 4\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\n/**\n * Fixed-position coordinates that place a menu under its trigger, aligned to\n * the trigger's start, centre or end edge. Pure, so it is unit-testable.\n */\nexport function computeMenuPosition(\n trigger: { top: number; bottom: number; left: number; right: number; width: number },\n contentWidth: number,\n align: DropdownMenuAlign,\n sideOffset: number,\n): React.CSSProperties {\n const left =\n align === 'end'\n ? trigger.right - contentWidth\n : align === 'center'\n ? trigger.left + trigger.width / 2 - contentWidth / 2\n : trigger.left\n return { position: 'fixed', top: trigger.bottom + sideOffset, left: Math.max(0, left), minWidth: trigger.width }\n}\n\n// ---------------------------------------------------------------------------\n// DropdownMenuContent\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuContentProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Horizontal alignment to the trigger. Defaults to `start`. */\n align?: DropdownMenuAlign\n /** Gap between trigger and menu in px. Defaults to 4. */\n sideOffset?: number\n}\n\nexport const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenuContentProps>(\n function DropdownMenuContent(\n { className, children, onKeyDown, align = 'start', sideOffset = DEFAULT_SIDE_OFFSET, style, ...props },\n forwardedRef,\n ) {\n const { open, onOpenChange, contentId, triggerRef } = useDropdownMenuContext()\n const contentRef = React.useRef<HTMLDivElement | null>(null)\n const ref = React.useCallback(\n (node: HTMLDivElement | null) => {\n contentRef.current = node\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) forwardedRef.current = node\n },\n [forwardedRef],\n )\n const [position, setPosition] = React.useState<React.CSSProperties | null>(null)\n\n // The content is portalled to <body>, so anchor it to the trigger with\n // fixed coordinates, and follow the trigger on scroll / resize.\n useIsomorphicLayoutEffect(() => {\n if (!open) return\n const update = () => {\n const trigger = triggerRef.current\n const content = contentRef.current\n if (!trigger) return\n setPosition(\n computeMenuPosition(trigger.getBoundingClientRect(), content?.offsetWidth ?? 0, align, sideOffset),\n )\n }\n update()\n window.addEventListener('resize', update)\n window.addEventListener('scroll', update, true)\n return () => {\n window.removeEventListener('resize', update)\n window.removeEventListener('scroll', update, true)\n }\n }, [open, align, sideOffset, triggerRef])\n\n // Close on a pointer press outside the menu and its trigger.\n React.useEffect(() => {\n if (!open) return\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target as Node\n if (contentRef.current?.contains(target) || triggerRef.current?.contains(target)) return\n onOpenChange(false)\n }\n document.addEventListener('pointerdown', onPointerDown)\n return () => document.removeEventListener('pointerdown', onPointerDown)\n }, [open, onOpenChange, triggerRef])\n\n React.useEffect(() => {\n if (!open) return\n // The initial portal layout can still be below the viewport when effects run.\n // Move keyboard focus without scrolling the document away from the trigger.\n contentRef.current?.querySelector<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')?.focus({ preventScroll: true })\n }, [open])\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(e)\n if (e.defaultPrevented) return\n if (e.key === 'Escape') {\n e.preventDefault()\n onOpenChange(false)\n triggerRef.current?.focus()\n return\n }\n if (e.key === 'Tab') {\n // Native traversal must start from the trigger, before the portal unmounts.\n triggerRef.current?.focus()\n onOpenChange(false)\n return\n }\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) return\n const items = Array.from(contentRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])',\n ) ?? [])\n if (items.length === 0) return\n e.preventDefault()\n const current = items.indexOf(document.activeElement as HTMLElement)\n const index = e.key === 'Home' ? 0\n : e.key === 'End' ? items.length - 1\n : e.key === 'ArrowDown' ? (current + 1) % items.length\n : (current - 1 + items.length) % items.length\n items[index].focus()\n }\n\n if (!open) return null\n\n const content = React.createElement(\n 'div',\n {\n ref,\n role: 'menu',\n id: contentId,\n 'data-state': open ? 'open' : 'closed',\n className: cn(menuContentVariants(), className),\n style: position ? { ...position, ...style } : style,\n 'data-align': align,\n onKeyDown: handleKeyDown,\n tabIndex: -1,\n ...props,\n },\n children,\n )\n\n // Render via portal if document is available (client-side)\n if (typeof document !== 'undefined') {\n return ReactDOM.createPortal(content, document.body)\n }\n\n // SSR fallback: render inline\n return content\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuItem\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {\n disabled?: boolean\n onSelect?: () => void\n}\n\nexport const DropdownMenuItem = React.forwardRef<HTMLDivElement, DropdownMenuItemProps>(\n function DropdownMenuItem({ className, disabled, onSelect, onClick, children, ...props }, ref) {\n const { onOpenChange, triggerRef } = useDropdownMenuContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onSelect?.()\n onOpenChange(false)\n triggerRef.current?.focus()\n onClick?.(e)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onSelect?.()\n onOpenChange(false)\n triggerRef.current?.focus()\n }\n }\n\n return React.createElement(\n 'div',\n {\n ref,\n role: 'menuitem',\n tabIndex: disabled ? -1 : 0,\n 'data-disabled': disabled ? '' : undefined,\n 'aria-disabled': disabled || undefined,\n className: cn(menuItemVariants(), className),\n onClick: handleClick,\n onKeyDown: handleKeyDown,\n ...props,\n },\n children,\n )\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuSeparator\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DropdownMenuSeparator = React.forwardRef<HTMLDivElement, DropdownMenuSeparatorProps>(\n function DropdownMenuSeparator({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n role: 'separator',\n className: cn('-mx-1 my-1 h-px bg-muted', className),\n ...props,\n })\n },\n)\n\n// ---------------------------------------------------------------------------\n// DropdownMenuLabel\n// ---------------------------------------------------------------------------\n\nexport interface DropdownMenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nexport const DropdownMenuLabel = React.forwardRef<HTMLDivElement, DropdownMenuLabelProps>(\n function DropdownMenuLabel({ className, ...props }, ref) {\n return React.createElement('div', {\n ref,\n className: cn('px-2 py-1.5 text-sm font-semibold', className),\n ...props,\n })\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DropdownMenuTrigger", "DropdownMenuContent", "content", "DropdownMenuItem", "DropdownMenuSeparator", "DropdownMenuLabel"]
|
|
7
7
|
}
|
|
@@ -15,7 +15,7 @@ function createPageHeader(props = {}) {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
var pageHeaderClass = "flex flex-wrap items-start justify-between gap-4";
|
|
18
|
-
var pageHeaderTextClass = "flex min-w-0
|
|
18
|
+
var pageHeaderTextClass = "flex min-w-0 basis-64 grow flex-col gap-1";
|
|
19
19
|
var pageHeaderKickerClass = "break-words text-[11px] uppercase tracking-wider text-muted-foreground";
|
|
20
20
|
var pageHeaderTitleClass = "break-words text-xl font-semibold text-foreground";
|
|
21
21
|
var pageHeaderDescriptionClass = "max-w-prose text-sm text-muted-foreground";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../page-header/src/page-header.ts", "../../page-header/src/page-header.styles.ts", "../../react-page-header/src/page-header.tsx"],
|
|
4
|
-
"sourcesContent": ["/** Heading element used for the page title. */\nexport type PageHeaderTitleLevel = 'h1' | 'h2' | 'h3'\n\nexport interface PageHeaderProps {\n /** Whether an actions slot will be rendered. */\n hasActions?: boolean\n}\n\nexport interface PageHeaderAPI {\n /** Data attributes for styling hooks. */\n dataAttributes: Record<string, string>\n}\n\n/**\n * Build the framework-agnostic data attributes for a PageHeader — the heading\n * cluster at the top of an application page (kicker, title, description and a\n * trailing actions row). Unlike SectionHead (a centred marketing heading) it is\n * sized for dense app chrome and never centres.\n */\nexport function createPageHeader(props: PageHeaderProps = {}): PageHeaderAPI {\n return {\n dataAttributes: {\n 'data-slot': 'page-header',\n 'data-has-actions': props.hasActions ? 'true' : 'false',\n },\n }\n}\n", "/**\n * Root: text cluster on the leading edge, actions on the trailing edge; wraps\n * on narrow screens. Carries no outer margin — the page layout spaces it.\n */\nexport const pageHeaderClass = 'flex flex-wrap items-start justify-between gap-4'\n\n/**
|
|
4
|
+
"sourcesContent": ["/** Heading element used for the page title. */\nexport type PageHeaderTitleLevel = 'h1' | 'h2' | 'h3'\n\nexport interface PageHeaderProps {\n /** Whether an actions slot will be rendered. */\n hasActions?: boolean\n}\n\nexport interface PageHeaderAPI {\n /** Data attributes for styling hooks. */\n dataAttributes: Record<string, string>\n}\n\n/**\n * Build the framework-agnostic data attributes for a PageHeader — the heading\n * cluster at the top of an application page (kicker, title, description and a\n * trailing actions row). Unlike SectionHead (a centred marketing heading) it is\n * sized for dense app chrome and never centres.\n */\nexport function createPageHeader(props: PageHeaderProps = {}): PageHeaderAPI {\n return {\n dataAttributes: {\n 'data-slot': 'page-header',\n 'data-has-actions': props.hasActions ? 'true' : 'false',\n },\n }\n}\n", "/**\n * Root: text cluster on the leading edge, actions on the trailing edge; wraps\n * on narrow screens. Carries no outer margin — the page layout spaces it.\n */\nexport const pageHeaderClass = 'flex flex-wrap items-start justify-between gap-4'\n\n/** Give text a preferred width so actions wrap before squeezing the heading. */\nexport const pageHeaderTextClass = 'flex min-w-0 basis-64 grow flex-col gap-1'\n\n/** Small uppercase eyebrow above the title. */\nexport const pageHeaderKickerClass =\n 'break-words text-[11px] uppercase tracking-wider text-muted-foreground'\n\n/** Title — app-sized, not a marketing display size. */\nexport const pageHeaderTitleClass = 'break-words text-xl font-semibold text-foreground'\n\n/** Supporting copy under the title, capped at a readable measure. */\nexport const pageHeaderDescriptionClass = 'max-w-prose text-sm text-muted-foreground'\n\n/** Trailing actions row. */\nexport const pageHeaderActionsClass = 'flex flex-none flex-wrap items-center gap-2'\n", "import * as React from 'react'\nimport {\n createPageHeader,\n pageHeaderClass,\n pageHeaderTextClass,\n pageHeaderKickerClass,\n pageHeaderTitleClass,\n pageHeaderDescriptionClass,\n pageHeaderActionsClass,\n type PageHeaderTitleLevel,\n} from '@refraction-ui/page-header'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { PageHeaderTitleLevel }\n\nexport interface PageHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {\n /** Optional eyebrow rendered above the title. */\n kicker?: React.ReactNode\n /** The page title (required). */\n title: React.ReactNode\n /** Optional supporting copy under the title. */\n description?: React.ReactNode\n /** Optional actions (buttons, menus) on the trailing edge. */\n actions?: React.ReactNode\n /** Heading element for the title. Defaults to `h1`. */\n as?: PageHeaderTitleLevel\n}\n\n/**\n * PageHeader — the heading cluster at the top of an application page: kicker,\n * title, description and a trailing actions row. Renders a `<header>`.\n */\nexport const PageHeader = React.forwardRef<HTMLElement, PageHeaderProps>(function PageHeader(\n { kicker, title, description, actions, as: Title = 'h1', className, ...props },\n ref,\n) {\n const { dataAttributes } = createPageHeader({ hasActions: actions != null })\n return (\n <header ref={ref} className={cn(pageHeaderClass, className)} {...dataAttributes} {...props}>\n <div className={pageHeaderTextClass}>\n {kicker != null && <p className={pageHeaderKickerClass}>{kicker}</p>}\n <Title className={pageHeaderTitleClass}>{title}</Title>\n {description != null && <div className={pageHeaderDescriptionClass}>{description}</div>}\n </div>\n {actions != null && <div className={pageHeaderActionsClass}>{actions}</div>}\n </header>\n )\n})\n"],
|
|
5
5
|
"mappings": ";;;;;;;;AAmBO,SAAS,iBAAiB,QAAyB,CAAA,GAAmB;AAC3E,SAAO;IACL,gBAAgB;MACd,aAAa;MACb,oBAAoB,MAAM,aAAa,SAAS;IAAA;EAClD;AAEJ;ACtBO,IAAM,kBAAkB;AAGxB,IAAM,sBAAsB;AAG5B,IAAM,wBACX;AAGK,IAAM,uBAAuB;AAG7B,IAAM,6BAA6B;AAGnC,IAAM,yBAAyB;;;;ACY/B,IAAM,aAAmB,iBAAyC,SAASA,YAChF,EAAE,QAAQ,OAAO,aAAa,SAAS,IAAI,QAAQ,MAAM,WAAW,GAAG,MAAA,GACvE,KACA;AACA,QAAM,EAAE,eAAA,IAAmB,iBAAiB,EAAE,YAAY,WAAW,KAAA,CAAM;AAC3E,SACE,qBAAC,UAAA,EAAO,KAAU,WAAW,GAAG,iBAAiB,SAAS,GAAI,GAAG,gBAAiB,GAAG,OACnF,UAAA;IAAA,qBAAC,OAAA,EAAI,WAAW,qBACb,UAAA;MAAA,UAAU,QAAQ,oBAAC,KAAA,EAAE,WAAW,uBAAwB,UAAA,OAAA,CAAO;MAChE,oBAAC,OAAA,EAAM,WAAW,sBAAuB,UAAA,MAAA,CAAM;MAC9C,eAAe,QAAQ,oBAAC,OAAA,EAAI,WAAW,4BAA6B,UAAA,YAAA,CAAY;IAAA,EAAA,CACnF;IACC,WAAW,QAAQ,oBAAC,OAAA,EAAI,WAAW,wBAAyB,UAAA,QAAA,CAAQ;EAAA,EAAA,CACvE;AAEJ,CAAC;",
|
|
6
6
|
"names": ["PageHeader"]
|
|
7
7
|
}
|