319 lines
12 KiB
JavaScript
319 lines
12 KiB
JavaScript
"use client";
|
|
|
|
// packages/react/popover/src/Popover.tsx
|
|
import * as React from "react";
|
|
import { composeEventHandlers } from "@radix-ui/primitive";
|
|
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
import { createContextScope } from "@radix-ui/react-context";
|
|
import { DismissableLayer } from "@radix-ui/react-dismissable-layer";
|
|
import { useFocusGuards } from "@radix-ui/react-focus-guards";
|
|
import { FocusScope } from "@radix-ui/react-focus-scope";
|
|
import { useId } from "@radix-ui/react-id";
|
|
import * as PopperPrimitive from "@radix-ui/react-popper";
|
|
import { createPopperScope } from "@radix-ui/react-popper";
|
|
import { Portal as PortalPrimitive } from "@radix-ui/react-portal";
|
|
import { Presence } from "@radix-ui/react-presence";
|
|
import { Primitive } from "@radix-ui/react-primitive";
|
|
import { Slot } from "@radix-ui/react-slot";
|
|
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
import { hideOthers } from "aria-hidden";
|
|
import { RemoveScroll } from "react-remove-scroll";
|
|
import { jsx } from "react/jsx-runtime";
|
|
var POPOVER_NAME = "Popover";
|
|
var [createPopoverContext, createPopoverScope] = createContextScope(POPOVER_NAME, [
|
|
createPopperScope
|
|
]);
|
|
var usePopperScope = createPopperScope();
|
|
var [PopoverProvider, usePopoverContext] = createPopoverContext(POPOVER_NAME);
|
|
var Popover = (props) => {
|
|
const {
|
|
__scopePopover,
|
|
children,
|
|
open: openProp,
|
|
defaultOpen,
|
|
onOpenChange,
|
|
modal = false
|
|
} = props;
|
|
const popperScope = usePopperScope(__scopePopover);
|
|
const triggerRef = React.useRef(null);
|
|
const [hasCustomAnchor, setHasCustomAnchor] = React.useState(false);
|
|
const [open = false, setOpen] = useControllableState({
|
|
prop: openProp,
|
|
defaultProp: defaultOpen,
|
|
onChange: onOpenChange
|
|
});
|
|
return /* @__PURE__ */ jsx(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ jsx(
|
|
PopoverProvider,
|
|
{
|
|
scope: __scopePopover,
|
|
contentId: useId(),
|
|
triggerRef,
|
|
open,
|
|
onOpenChange: setOpen,
|
|
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
hasCustomAnchor,
|
|
onCustomAnchorAdd: React.useCallback(() => setHasCustomAnchor(true), []),
|
|
onCustomAnchorRemove: React.useCallback(() => setHasCustomAnchor(false), []),
|
|
modal,
|
|
children
|
|
}
|
|
) });
|
|
};
|
|
Popover.displayName = POPOVER_NAME;
|
|
var ANCHOR_NAME = "PopoverAnchor";
|
|
var PopoverAnchor = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopePopover, ...anchorProps } = props;
|
|
const context = usePopoverContext(ANCHOR_NAME, __scopePopover);
|
|
const popperScope = usePopperScope(__scopePopover);
|
|
const { onCustomAnchorAdd, onCustomAnchorRemove } = context;
|
|
React.useEffect(() => {
|
|
onCustomAnchorAdd();
|
|
return () => onCustomAnchorRemove();
|
|
}, [onCustomAnchorAdd, onCustomAnchorRemove]);
|
|
return /* @__PURE__ */ jsx(PopperPrimitive.Anchor, { ...popperScope, ...anchorProps, ref: forwardedRef });
|
|
}
|
|
);
|
|
PopoverAnchor.displayName = ANCHOR_NAME;
|
|
var TRIGGER_NAME = "PopoverTrigger";
|
|
var PopoverTrigger = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopePopover, ...triggerProps } = props;
|
|
const context = usePopoverContext(TRIGGER_NAME, __scopePopover);
|
|
const popperScope = usePopperScope(__scopePopover);
|
|
const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
|
|
const trigger = /* @__PURE__ */ jsx(
|
|
Primitive.button,
|
|
{
|
|
type: "button",
|
|
"aria-haspopup": "dialog",
|
|
"aria-expanded": context.open,
|
|
"aria-controls": context.contentId,
|
|
"data-state": getState(context.open),
|
|
...triggerProps,
|
|
ref: composedTriggerRef,
|
|
onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
|
|
}
|
|
);
|
|
return context.hasCustomAnchor ? trigger : /* @__PURE__ */ jsx(PopperPrimitive.Anchor, { asChild: true, ...popperScope, children: trigger });
|
|
}
|
|
);
|
|
PopoverTrigger.displayName = TRIGGER_NAME;
|
|
var PORTAL_NAME = "PopoverPortal";
|
|
var [PortalProvider, usePortalContext] = createPopoverContext(PORTAL_NAME, {
|
|
forceMount: void 0
|
|
});
|
|
var PopoverPortal = (props) => {
|
|
const { __scopePopover, forceMount, children, container } = props;
|
|
const context = usePopoverContext(PORTAL_NAME, __scopePopover);
|
|
return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopePopover, forceMount, children: /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(PortalPrimitive, { asChild: true, container, children }) }) });
|
|
};
|
|
PopoverPortal.displayName = PORTAL_NAME;
|
|
var CONTENT_NAME = "PopoverContent";
|
|
var PopoverContent = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const portalContext = usePortalContext(CONTENT_NAME, props.__scopePopover);
|
|
const { forceMount = portalContext.forceMount, ...contentProps } = props;
|
|
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsx(PopoverContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(PopoverContentNonModal, { ...contentProps, ref: forwardedRef }) });
|
|
}
|
|
);
|
|
PopoverContent.displayName = CONTENT_NAME;
|
|
var PopoverContentModal = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
const contentRef = React.useRef(null);
|
|
const composedRefs = useComposedRefs(forwardedRef, contentRef);
|
|
const isRightClickOutsideRef = React.useRef(false);
|
|
React.useEffect(() => {
|
|
const content = contentRef.current;
|
|
if (content) return hideOthers(content);
|
|
}, []);
|
|
return /* @__PURE__ */ jsx(RemoveScroll, { as: Slot, allowPinchZoom: true, children: /* @__PURE__ */ jsx(
|
|
PopoverContentImpl,
|
|
{
|
|
...props,
|
|
ref: composedRefs,
|
|
trapFocus: context.open,
|
|
disableOutsidePointerEvents: true,
|
|
onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
event.preventDefault();
|
|
if (!isRightClickOutsideRef.current) context.triggerRef.current?.focus();
|
|
}),
|
|
onPointerDownOutside: composeEventHandlers(
|
|
props.onPointerDownOutside,
|
|
(event) => {
|
|
const originalEvent = event.detail.originalEvent;
|
|
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
|
|
const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
|
|
isRightClickOutsideRef.current = isRightClick;
|
|
},
|
|
{ checkForDefaultPrevented: false }
|
|
),
|
|
onFocusOutside: composeEventHandlers(
|
|
props.onFocusOutside,
|
|
(event) => event.preventDefault(),
|
|
{ checkForDefaultPrevented: false }
|
|
)
|
|
}
|
|
) });
|
|
}
|
|
);
|
|
var PopoverContentNonModal = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const context = usePopoverContext(CONTENT_NAME, props.__scopePopover);
|
|
const hasInteractedOutsideRef = React.useRef(false);
|
|
const hasPointerDownOutsideRef = React.useRef(false);
|
|
return /* @__PURE__ */ jsx(
|
|
PopoverContentImpl,
|
|
{
|
|
...props,
|
|
ref: forwardedRef,
|
|
trapFocus: false,
|
|
disableOutsidePointerEvents: false,
|
|
onCloseAutoFocus: (event) => {
|
|
props.onCloseAutoFocus?.(event);
|
|
if (!event.defaultPrevented) {
|
|
if (!hasInteractedOutsideRef.current) context.triggerRef.current?.focus();
|
|
event.preventDefault();
|
|
}
|
|
hasInteractedOutsideRef.current = false;
|
|
hasPointerDownOutsideRef.current = false;
|
|
},
|
|
onInteractOutside: (event) => {
|
|
props.onInteractOutside?.(event);
|
|
if (!event.defaultPrevented) {
|
|
hasInteractedOutsideRef.current = true;
|
|
if (event.detail.originalEvent.type === "pointerdown") {
|
|
hasPointerDownOutsideRef.current = true;
|
|
}
|
|
}
|
|
const target = event.target;
|
|
const targetIsTrigger = context.triggerRef.current?.contains(target);
|
|
if (targetIsTrigger) event.preventDefault();
|
|
if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
);
|
|
var PopoverContentImpl = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const {
|
|
__scopePopover,
|
|
trapFocus,
|
|
onOpenAutoFocus,
|
|
onCloseAutoFocus,
|
|
disableOutsidePointerEvents,
|
|
onEscapeKeyDown,
|
|
onPointerDownOutside,
|
|
onFocusOutside,
|
|
onInteractOutside,
|
|
...contentProps
|
|
} = props;
|
|
const context = usePopoverContext(CONTENT_NAME, __scopePopover);
|
|
const popperScope = usePopperScope(__scopePopover);
|
|
useFocusGuards();
|
|
return /* @__PURE__ */ jsx(
|
|
FocusScope,
|
|
{
|
|
asChild: true,
|
|
loop: true,
|
|
trapped: trapFocus,
|
|
onMountAutoFocus: onOpenAutoFocus,
|
|
onUnmountAutoFocus: onCloseAutoFocus,
|
|
children: /* @__PURE__ */ jsx(
|
|
DismissableLayer,
|
|
{
|
|
asChild: true,
|
|
disableOutsidePointerEvents,
|
|
onInteractOutside,
|
|
onEscapeKeyDown,
|
|
onPointerDownOutside,
|
|
onFocusOutside,
|
|
onDismiss: () => context.onOpenChange(false),
|
|
children: /* @__PURE__ */ jsx(
|
|
PopperPrimitive.Content,
|
|
{
|
|
"data-state": getState(context.open),
|
|
role: "dialog",
|
|
id: context.contentId,
|
|
...popperScope,
|
|
...contentProps,
|
|
ref: forwardedRef,
|
|
style: {
|
|
...contentProps.style,
|
|
// re-namespace exposed content custom properties
|
|
...{
|
|
"--radix-popover-content-transform-origin": "var(--radix-popper-transform-origin)",
|
|
"--radix-popover-content-available-width": "var(--radix-popper-available-width)",
|
|
"--radix-popover-content-available-height": "var(--radix-popper-available-height)",
|
|
"--radix-popover-trigger-width": "var(--radix-popper-anchor-width)",
|
|
"--radix-popover-trigger-height": "var(--radix-popper-anchor-height)"
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
)
|
|
}
|
|
);
|
|
}
|
|
);
|
|
var CLOSE_NAME = "PopoverClose";
|
|
var PopoverClose = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopePopover, ...closeProps } = props;
|
|
const context = usePopoverContext(CLOSE_NAME, __scopePopover);
|
|
return /* @__PURE__ */ jsx(
|
|
Primitive.button,
|
|
{
|
|
type: "button",
|
|
...closeProps,
|
|
ref: forwardedRef,
|
|
onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false))
|
|
}
|
|
);
|
|
}
|
|
);
|
|
PopoverClose.displayName = CLOSE_NAME;
|
|
var ARROW_NAME = "PopoverArrow";
|
|
var PopoverArrow = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopePopover, ...arrowProps } = props;
|
|
const popperScope = usePopperScope(__scopePopover);
|
|
return /* @__PURE__ */ jsx(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef });
|
|
}
|
|
);
|
|
PopoverArrow.displayName = ARROW_NAME;
|
|
function getState(open) {
|
|
return open ? "open" : "closed";
|
|
}
|
|
var Root2 = Popover;
|
|
var Anchor2 = PopoverAnchor;
|
|
var Trigger = PopoverTrigger;
|
|
var Portal = PopoverPortal;
|
|
var Content2 = PopoverContent;
|
|
var Close = PopoverClose;
|
|
var Arrow2 = PopoverArrow;
|
|
export {
|
|
Anchor2 as Anchor,
|
|
Arrow2 as Arrow,
|
|
Close,
|
|
Content2 as Content,
|
|
Popover,
|
|
PopoverAnchor,
|
|
PopoverArrow,
|
|
PopoverClose,
|
|
PopoverContent,
|
|
PopoverPortal,
|
|
PopoverTrigger,
|
|
Portal,
|
|
Root2 as Root,
|
|
Trigger,
|
|
createPopoverScope
|
|
};
|
|
//# sourceMappingURL=index.mjs.map
|