@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
206 lines • 14.5 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import React, { forwardRef, useRef } from "react";
import { ChevronRightIcon } from "@navikt/aksel-icons";
import { useModalContext } from "../../modal/Modal.context.js";
import { useId } from "../../utils-external/index.js";
import { Slot } from "../../utils/components/slot/Slot.js";
import { cl, composeEventHandlers, createStrictContext, requireReactElement, } from "../../utils/helpers/index.js";
import { useControllableState, useMergeRefs } from "../../utils/hooks/index.js";
import { Menu } from "../floating-menu/Menu.js";
const { Provider: ActionMenuProvider, useContext: useActionMenuContext } = createStrictContext({
name: "ActionMenuContext",
errorMessage: "ActionMenu sub-components cannot be rendered outside the ActionMenu component.",
});
const ActionMenuRoot = ({ children, open: openProp, onOpenChange, rootElement: rootElementProp, }) => {
const triggerRef = useRef(null);
const modalContext = useModalContext(false);
const rootElement = modalContext
? modalContext.modalRef.current
: rootElementProp;
const [open = false, setOpen] = useControllableState({
value: openProp,
defaultValue: false,
onChange: onOpenChange,
});
return (React.createElement(ActionMenuProvider, { triggerId: useId(), triggerRef: triggerRef, contentId: useId(), open: open, onOpenChange: setOpen, onOpenToggle: () => setOpen((prevOpen) => !prevOpen), rootElement: rootElement },
React.createElement(Menu, { open: open, onOpenChange: setOpen, modal: true }, children)));
};
/**
* ActionMenu is a dropdown menu for actions and navigation.
*
* @example
* ```jsx
* <ActionMenu>
* <ActionMenu.Trigger>
* <button>Open Menu</button>
* </ActionMenu.Trigger>
* <ActionMenu.Content>
* <ActionMenu.Item onSelect={() => alert("Item 1 selected")}>
* Item 1
* </ActionMenu.Item>
* <ActionMenu.Item onSelect={() => alert("Item 2 selected")}>
* Item 2
* </ActionMenu.Item>
* </ActionMenu.Content>
* </ActionMenu>
* ```
*/
export const ActionMenu = ActionMenuRoot;
export const ActionMenuTrigger = forwardRef((_a, ref) => {
var { children, onKeyDown, style, onClick } = _a, rest = __rest(_a, ["children", "onKeyDown", "style", "onClick"]);
const context = useActionMenuContext();
const mergedRefs = useMergeRefs(ref, context.triggerRef);
return (React.createElement(Menu.Anchor, { asChild: true },
React.createElement(Slot, Object.assign({ type: "button", id: context.triggerId, "aria-haspopup": "menu", "aria-expanded": context.open, "aria-controls": context.open ? context.contentId : undefined, "data-state": context.open ? "open" : "closed", ref: mergedRefs }, rest, { style: Object.assign(Object.assign({}, style), { pointerEvents: context.open ? "auto" : undefined }), onClick: composeEventHandlers(onClick, context.onOpenToggle), onKeyDown: composeEventHandlers(onKeyDown, (event) => {
if (event.key === "ArrowDown") {
context.onOpenChange(true);
/* Stop keydown from scrolling window */
event.preventDefault();
}
}) }), requireReactElement(children))));
});
export const ActionMenuContent = forwardRef((_a, ref) => {
var { children, className, style, align = "start" } = _a, rest = __rest(_a, ["children", "className", "style", "align"]);
const context = useActionMenuContext();
return (React.createElement(Menu.Portal, { rootElement: context.rootElement },
React.createElement(Menu.Content, Object.assign({ ref: ref, id: context.contentId, "aria-labelledby": context.triggerId, className: cl("aksel-action-menu__content", className) }, rest, { align: align, sideOffset: 4, collisionPadding: 10, returnFocus: context.triggerRef, safeZone: {
anchor: context.triggerRef.current,
}, style: Object.assign(Object.assign({}, style), {
"--__axc-action-menu-content-transform-origin": "var(--__axc-floating-transform-origin)",
"--__axc-action-menu-content-available-height": "var(--__axc-floating-available-height)",
}) }),
React.createElement("div", { className: "aksel-action-menu__content-inner" }, children))));
});
export const ActionMenuLabel = forwardRef((_a, ref) => {
var { children, className } = _a, rest = __rest(_a, ["children", "className"]);
return (React.createElement("div", Object.assign({ ref: ref }, rest, { className: cl("aksel-action-menu__label", className) }), children));
});
export const ActionMenuGroup = forwardRef((_a, ref) => {
var { children, className, label } = _a, rest = __rest(_a, ["children", "className", "label"]);
const labelId = useId();
return (React.createElement(Menu.Group, Object.assign({ ref: ref }, rest, { className: cl("aksel-action-menu__group", className), asChild: false, "aria-labelledby": label ? labelId : undefined }),
label && (React.createElement(ActionMenu.Label, { id: labelId, "aria-hidden": true }, label)),
children));
});
const Marker = ({ children, className, placement }) => {
return (React.createElement("div", { "aria-hidden": true, className: cl(className, "aksel-action-menu__marker", `aksel-action-menu__marker--${placement}`) }, children));
};
const Shortcut = ({ children }) => {
/**
* Assumes the user will input either a single keyboard key
* or keys separated by "+"
*/
const parsed = children.split("+").filter((str) => str !== "");
return (React.createElement(Marker, { placement: "right" }, parsed.map((char, index) => (React.createElement("span", { key: char + index, className: "aksel-action-menu__shortcut" }, char)))));
};
export const ActionMenuItem = forwardRef((_a, ref) => {
var { children, as: Component = "div", className, icon, shortcut, variant, iconPosition = "left" } = _a, rest = __rest(_a, ["children", "as", "className", "icon", "shortcut", "variant", "iconPosition"]);
return (React.createElement(Menu.Item, Object.assign({}, rest, { className: cl("aksel-action-menu__item", className, {
"aksel-action-menu__item--danger": variant === "danger",
}), "data-marker": icon ? iconPosition : undefined, "aria-keyshortcuts": shortcut !== null && shortcut !== void 0 ? shortcut : undefined, asChild: true }),
React.createElement(Component, { ref: ref },
children,
icon && (React.createElement(Marker, { placement: iconPosition, className: "aksel-action-menu__marker-icon" }, icon)),
shortcut && React.createElement(Shortcut, null, shortcut))));
});
export const ActionMenuCheckboxItem = forwardRef((_a, ref) => {
var { children, className, shortcut, onSelect } = _a, rest = __rest(_a, ["children", "className", "shortcut", "onSelect"]);
return (React.createElement(Menu.CheckboxItem, Object.assign({ ref: ref }, rest, { onSelect: composeEventHandlers(onSelect, (event) => {
/**
* Prevent default to avoid the menu from closing when clicking the checkbox
*/
event.preventDefault();
}), asChild: false, className: cl("aksel-action-menu__item", className), "data-marker": "left", "aria-keyshortcuts": shortcut }),
children,
React.createElement(Marker, { placement: "left" },
React.createElement(Menu.ItemIndicator, { className: "aksel-action-menu__indicator" },
React.createElement("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "aksel-action-menu__indicator-icon", "aria-hidden": true },
React.createElement("g", { className: "aksel-action-menu__indicator-icon--unchecked" },
React.createElement("rect", { width: "24", height: "24", rx: "4", fill: "var(--ax-border-neutral)" }),
React.createElement("rect", { x: "1", y: "1", width: "22", height: "22", rx: "3", fill: "var(--ax-bg-default)", strokeWidth: "2" })),
React.createElement("g", { className: "aksel-action-menu__indicator-icon--indeterminate" },
React.createElement("rect", { width: "24", height: "24", rx: "4", fill: "var(--ax-bg-strong-pressed)" }),
React.createElement("rect", { x: "6", y: "10", width: "12", height: "4", rx: "1", fill: "var(--ax-bg-default)" })),
React.createElement("g", { className: "aksel-action-menu__indicator-icon--checked" },
React.createElement("rect", { width: "24", height: "24", rx: "4", fill: "var(--ax-bg-strong-pressed)" }),
React.createElement("path", { d: "M10.0352 13.4148L16.4752 7.40467C17.0792 6.83965 18.029 6.86933 18.5955 7.47478C19.162 8.08027 19.1296 9.03007 18.5245 9.59621L11.0211 16.5993C10.741 16.859 10.3756 17 10.0002 17C9.60651 17 9.22717 16.8462 8.93914 16.5611L6.43914 14.0611C5.85362 13.4756 5.85362 12.5254 6.43914 11.9399C7.02467 11.3544 7.97483 11.3544 8.56036 11.9399L10.0352 13.4148Z", fill: "var(--ax-bg-default)" }))))),
shortcut && React.createElement(Shortcut, null, shortcut)));
});
export const ActionMenuRadioGroup = forwardRef((_a, ref) => {
var { children, label } = _a, rest = __rest(_a, ["children", "label"]);
const labelId = useId();
return (React.createElement(Menu.RadioGroup, Object.assign({ ref: ref }, rest, { asChild: false, "aria-labelledby": label ? labelId : undefined }),
label && (React.createElement(ActionMenu.Label, { id: labelId, "aria-hidden": true }, label)),
children));
});
export const ActionMenuRadioItem = forwardRef((_a, ref) => {
var { children, className, onSelect } = _a, rest = __rest(_a, ["children", "className", "onSelect"]);
return (React.createElement(Menu.RadioItem, Object.assign({ ref: ref }, rest, { onSelect: composeEventHandlers(onSelect, (event) => {
/**
* Prevent default to avoid the menu from closing when clicking the radio
*/
event.preventDefault();
}), asChild: false, className: cl("aksel-action-menu__item", className), "data-marker": "left" }),
children,
React.createElement(Marker, { placement: "left" },
React.createElement(Menu.ItemIndicator, { className: "aksel-action-menu__indicator" },
React.createElement("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: "aksel-action-menu__indicator-icon", "aria-hidden": true },
React.createElement("g", { className: "aksel-action-menu__indicator-icon--unchecked" },
React.createElement("rect", { width: "24", height: "24", rx: "12", fill: "var(--ax-border-neutral)" }),
React.createElement("rect", { x: "1", y: "1", width: "22", height: "22", rx: "11", strokeWidth: "2", fill: "var(--ax-bg-default)" })),
React.createElement("g", { className: "aksel-action-menu__indicator-icon--checked" },
React.createElement("rect", { width: "24", height: "24", rx: "12", fill: "var(--ax-bg-strong-pressed)" }),
React.createElement("rect", { x: "8", y: "8", width: "8", height: "8", rx: "4", fill: "var(--ax-bg-default)" })))))));
});
export const ActionMenuDivider = forwardRef((_a, ref) => {
var { className } = _a, rest = __rest(_a, ["className"]);
return (React.createElement(Menu.Divider, Object.assign({ ref: ref, asChild: false }, rest, { className: cl("aksel-action-menu__divider", className) })));
});
export const ActionMenuSub = (props) => {
const { children, open: openProp, onOpenChange } = props;
const [open = false, setOpen] = useControllableState({
value: openProp,
defaultValue: false,
onChange: onOpenChange,
});
return (React.createElement(Menu.Sub, { open: open, onOpenChange: setOpen }, children));
};
export const ActionMenuSubTrigger = forwardRef((_a, ref) => {
var { children, className, icon, iconPosition = "left" } = _a, rest = __rest(_a, ["children", "className", "icon", "iconPosition"]);
return (React.createElement(Menu.SubTrigger, Object.assign({ ref: ref }, rest, { asChild: false, className: cl("aksel-action-menu__item aksel-action-menu__sub-trigger", className), "data-marker": icon ? iconPosition : undefined }),
children,
icon && (React.createElement(Marker, { placement: iconPosition, className: "aksel-action-menu__marker-icon" }, icon)),
React.createElement(Marker, { placement: "right", className: "aksel-action-menu__marker-icon" },
React.createElement(ChevronRightIcon, { "aria-hidden": true }))));
});
export const ActionMenuSubContent = forwardRef((_a, ref) => {
var { children, className } = _a, rest = __rest(_a, ["children", "className"]);
const context = useActionMenuContext();
return (React.createElement(Menu.Portal, { rootElement: context.rootElement },
React.createElement(Menu.SubContent, Object.assign({ ref: ref, alignOffset: -4, sideOffset: 1, collisionPadding: 10 }, rest, { className: cl("aksel-action-menu__content aksel-action-menu__sub-content", className) }),
React.createElement("div", { className: "aksel-action-menu__content-inner" }, children))));
});
/* -------------------------------------------------------------------------- */
ActionMenu.Trigger = ActionMenuTrigger;
ActionMenu.Content = ActionMenuContent;
ActionMenu.Group = ActionMenuGroup;
ActionMenu.Label = ActionMenuLabel;
ActionMenu.Item = ActionMenuItem;
ActionMenu.CheckboxItem = ActionMenuCheckboxItem;
ActionMenu.RadioGroup = ActionMenuRadioGroup;
ActionMenu.RadioItem = ActionMenuRadioItem;
ActionMenu.Divider = ActionMenuDivider;
ActionMenu.Sub = ActionMenuSub;
ActionMenu.SubTrigger = ActionMenuSubTrigger;
ActionMenu.SubContent = ActionMenuSubContent;
//# sourceMappingURL=ActionMenu.js.map