@kobalte/core
Version:
Unstyled components and primitives for building accessible web apps and design systems with SolidJS.
571 lines (561 loc) • 16.5 kB
JSX
import {
createSize
} from "../chunk/KVL5CS3M.jsx";
import {
MenubarMenu,
MenubarRoot,
MenubarTrigger
} from "../chunk/BOXQKWL5.jsx";
import {
MenuCheckboxItem,
MenuContent,
MenuGroup,
MenuGroupLabel,
MenuIcon,
MenuItem,
MenuItemDescription,
MenuItemIndicator,
MenuItemLabel,
MenuPortal,
MenuRadioGroup,
MenuRadioItem,
MenuSub,
MenuSubContent,
MenuSubTrigger,
NavigationMenuContext,
useMenuContext,
useMenuRootContext,
useMenubarContext,
useNavigationMenuContext,
useOptionalMenuContext
} from "../chunk/LEK3K6R3.jsx";
import "../chunk/G4JQYERT.jsx";
import {
SeparatorRoot
} from "../chunk/T4C3DMHT.jsx";
import {
Popper,
PopperArrow
} from "../chunk/2CTBMVJ4.jsx";
import "../chunk/N3GAC5SS.jsx";
import "../chunk/QZDH5R5B.jsx";
import "../chunk/YRH543JR.jsx";
import "../chunk/SOM3K36D.jsx";
import "../chunk/LR7LBJN3.jsx";
import "../chunk/7A3GDF4Y.jsx";
import "../chunk/FBCYWU27.jsx";
import {
DismissableLayer
} from "../chunk/3VFJM5NZ.jsx";
import "../chunk/MGQGUY64.jsx";
import "../chunk/IGYOA2ZZ.jsx";
import "../chunk/3NI6FTA2.jsx";
import "../chunk/JHMNWOLY.jsx";
import "../chunk/E53DB7BS.jsx";
import "../chunk/UKTBL2JL.jsx";
import "../chunk/VI7QYH27.jsx";
import "../chunk/EJ5I5XML.jsx";
import "../chunk/JNCCF6MP.jsx";
import {
createControllableSignal
} from "../chunk/FN6EICGO.jsx";
import "../chunk/OYES4GOP.jsx";
import "../chunk/FLVHQV4A.jsx";
import "../chunk/5WXHJDCZ.jsx";
// src/navigation-menu/navigation-menu-arrow.tsx
import { mergeDefaultProps, mergeRefs } from "@kobalte/utils";
import {
createEffect,
createSignal,
on,
splitProps
} from "solid-js";
function NavigationMenuArrow(props) {
let ref;
const menubarContext = useMenubarContext();
const mergedProps = mergeDefaultProps(
{
size: 15
},
props
);
const [local, others] = splitProps(mergedProps, ["ref"]);
const [offset, setOffset] = createSignal(0);
const horizontal = () => menubarContext.orientation() === "horizontal";
createEffect(
on(menubarContext.value, (value) => {
setTimeout(() => {
if (!value || value.includes("link-trigger-"))
return;
const triggerRef = document.querySelector(
`[data-kb-menu-value-trigger="${value}"]`
);
if (!triggerRef || !ref)
return;
const middle = triggerRef.getBoundingClientRect()[horizontal() ? "x" : "y"] + triggerRef.getBoundingClientRect()[horizontal() ? "width" : "height"] / 2;
const computed = window.getComputedStyle(ref);
const initalArrowPos = ref.getBoundingClientRect()[horizontal() ? "x" : "y"] + ref.getBoundingClientRect()[horizontal() ? "width" : "height"] / 2 - Number.parseFloat(
computed.transform.split(",")[horizontal() ? 4 : 5]
);
setOffset(middle - initalArrowPos);
});
})
);
return <PopperArrow
ref={mergeRefs((el) => ref = el, local.ref)}
style={{
transform: `translate${horizontal() ? "X" : "Y"}(${offset()}px)`,
color: "red"
}}
{...others}
/>;
}
// src/navigation-menu/navigation-menu-content.tsx
import { callHandler } from "@kobalte/utils";
import {
batch,
createEffect as createEffect2,
createSignal as createSignal2,
on as on2,
splitProps as splitProps2
} from "solid-js";
function NavigationMenuContent(props) {
const context = useNavigationMenuContext();
const menubarContext = useMenubarContext();
const menuRootContext = useMenuRootContext();
const [motion, setMotion] = createSignal2();
const [local, others] = splitProps2(props, [
"onPointerEnter",
"onPointerLeave"
]);
const onPointerEnter = (e) => {
callHandler(e, local.onPointerEnter);
context.cancelLeaveTimer();
};
const onPointerLeave = (e) => {
callHandler(e, local.onPointerLeave);
context.startLeaveTimer();
};
createEffect2(
on2(menubarContext.value, (contextValue) => {
batch(() => {
if (!contextValue || contextValue.includes("link-trigger-")) {
context.setPreviousMenu(void 0);
return;
}
if (contextValue === menuRootContext.value()) {
if (context.previousMenu() != null) {
const menus2 = [...menubarContext.menus()];
const prevIndex2 = menus2.indexOf(context.previousMenu());
const nextIndex2 = menus2.indexOf(contextValue);
if (prevIndex2 < nextIndex2)
setMotion("from-end");
else
setMotion("from-start");
} else {
setMotion(void 0);
}
context.setPreviousMenu(contextValue);
return;
}
const menus = [...menubarContext.menus()];
const prevIndex = menus.indexOf(context.previousMenu());
const nextIndex = menus.indexOf(contextValue);
if (prevIndex > nextIndex)
setMotion("to-end");
else
setMotion("to-start");
});
})
);
return <MenuContent
as="ul"
onPointerEnter={onPointerEnter}
onPointerLeave={onPointerLeave}
onInteractOutside={() => {
context.setAutoFocusMenu(false);
}}
data-motion={motion()}
{...others}
/>;
}
// src/navigation-menu/navigation-menu-item.tsx
function NavigationMenuItem(props) {
return <li role="presentation"><MenuItem as="a" {...props} /></li>;
}
// src/navigation-menu/navigation-menu-menu.tsx
import { mergeDefaultProps as mergeDefaultProps2 } from "@kobalte/utils";
import {
createEffect as createEffect3,
createSignal as createSignal3,
createUniqueId,
on as on3,
splitProps as splitProps3
} from "solid-js";
function NavigationMenuMenu(props) {
const menubarContext = useMenubarContext();
const context = useNavigationMenuContext();
const [local, others] = splitProps3(props, ["value"]);
const uniqueid = createUniqueId();
const defaultId = menubarContext.generateId(
`navigation-menu-menu-${uniqueid}`
);
const mergedPropsWithId = mergeDefaultProps2({ id: defaultId }, others);
const value = () => local.value ?? uniqueid;
const [forceMount, setForceMount] = createSignal3(false);
const animationEnd = () => {
if (menubarContext.value() !== value()) {
setForceMount(false);
}
context.viewportRef()?.removeEventListener("animationend", animationEnd);
context.viewportRef()?.removeEventListener("animationcancel", animationEnd);
};
createEffect3(
on3(menubarContext.value, (contextValue) => {
if (contextValue === value()) {
setForceMount(true);
} else {
const viewportRef = context.viewportRef();
if (!viewportRef || ["", "none"].includes(
window.getComputedStyle(viewportRef).animationName
)) {
setForceMount(false);
return;
}
viewportRef.addEventListener("animationend", animationEnd);
}
})
);
return <MenubarMenu
forceMount={forceMount()}
value={value()}
{...mergedPropsWithId}
/>;
}
// src/navigation-menu/navigation-menu-portal.tsx
import { mergeRefs as mergeRefs2 } from "@kobalte/utils";
import { Show, splitProps as splitProps4 } from "solid-js";
function NavigationMenuPortal(props) {
const context = useNavigationMenuContext();
const menuContext = useMenuContext();
const [local, others] = splitProps4(props, ["ref"]);
return <Show when={context.viewportPresent()}><MenuPortal
ref={mergeRefs2((ref) => {
if (ref)
ref.setAttribute("role", "presentation");
}, local.ref)}
mount={menuContext.parentMenuContext() == null ? context.viewportRef() : void 0}
{...others}
/></Show>;
}
// src/navigation-menu/navigation-menu-root.tsx
import { mergeDefaultProps as mergeDefaultProps3, mergeRefs as mergeRefs3 } from "@kobalte/utils";
import {
batch as batch3,
createEffect as createEffect4,
createMemo,
createSignal as createSignal4,
splitProps as splitProps5
} from "solid-js";
import createPresence from "solid-presence";
function NavigationMenuRoot(props) {
const mergedProps = mergeDefaultProps3(
{
delayDuration: 200,
skipDelayDuration: 300
},
props
);
const [local, popperProps, others] = splitProps5(
mergedProps,
[
"ref",
"delayDuration",
"skipDelayDuration",
"autoFocusMenu",
"onAutoFocusMenuChange",
"defaultValue",
"value",
"onValueChange",
"forceMount"
],
[
"getAnchorRect",
"placement",
"gutter",
"shift",
"flip",
"slide",
"overlap",
"sameWidth",
"fitViewport",
"hideWhenDetached",
"detachedPadding",
"arrowPadding",
"overflowPadding"
]
);
const [value, setValue] = createControllableSignal(
{
value: () => local.value,
defaultValue: () => local.defaultValue,
onChange: (value2) => local.onValueChange?.(value2)
}
);
const [autoFocusMenu, setAutoFocusMenu] = createControllableSignal({
value: () => local.autoFocusMenu,
defaultValue: () => false,
onChange: local.onAutoFocusMenuChange
});
const [viewportRef, setViewportRef] = createSignal4();
const [rootRef, setRootRef] = createSignal4();
const [currentPlacement, setCurrentPlacement] = createSignal4(
popperProps.placement ?? others.orientation === "vertical" ? "right" : "bottom"
);
createEffect4(() => {
setCurrentPlacement(others.orientation === "vertical" ? "right" : "bottom");
});
let timeoutId;
const [previousMenu, setPreviousMenu] = createSignal4();
const [show, setShow] = createSignal4(false);
const [expanded, setExpanded] = createSignal4(false);
createEffect4(() => {
if (value() && !value().includes("link-trigger-") && autoFocusMenu()) {
batch3(() => {
setExpanded(true);
setShow(true);
});
} else {
setExpanded(false);
setShow(false);
}
});
const dataset = createMemo(() => ({
"data-expanded": expanded() ? "" : void 0,
"data-closed": !expanded() ? "" : void 0
}));
const { present: viewportPresent } = createPresence({
show: () => local.forceMount || show() || expanded(),
element: () => viewportRef() ?? null
});
createEffect4(() => {
if (!viewportPresent()) {
context.setPreviousMenu(void 0);
}
});
const context = {
dataset,
delayDuration: () => local.delayDuration,
skipDelayDuration: () => local.skipDelayDuration,
autoFocusMenu,
setAutoFocusMenu,
startLeaveTimer: () => {
timeoutId = window.setTimeout(() => {
context.setAutoFocusMenu(false);
setValue(void 0);
}, context.skipDelayDuration());
},
cancelLeaveTimer: () => {
if (timeoutId)
clearTimeout(timeoutId);
},
rootRef,
setRootRef,
viewportRef,
setViewportRef,
viewportPresent,
currentPlacement,
previousMenu,
setPreviousMenu
};
return <NavigationMenuContext.Provider value={context}><Popper
anchorRef={rootRef}
contentRef={viewportRef}
placement={currentPlacement()}
onCurrentPlacementChange={setCurrentPlacement}
{...popperProps}
><nav><MenubarRoot
as="ul"
ref={mergeRefs3(context.setRootRef, local.ref)}
value={value() ?? null}
onValueChange={setValue}
autoFocusMenu={autoFocusMenu()}
onAutoFocusMenuChange={setAutoFocusMenu}
{...others}
/></nav></Popper></NavigationMenuContext.Provider>;
}
// src/navigation-menu/navigation-menu-trigger.tsx
import { callHandler as callHandler3 } from "@kobalte/utils";
import {
splitProps as splitProps6
} from "solid-js";
function NavigationMenuTrigger(props) {
const context = useNavigationMenuContext();
const menuContext = useOptionalMenuContext();
const [local, others] = splitProps6(props, [
"onPointerEnter",
"onPointerLeave",
"onClick"
]);
let timeoutId;
const onClick = (e) => {
callHandler3(e, local.onClick);
if (timeoutId)
clearTimeout(timeoutId);
};
const onPointerEnter = (e) => {
callHandler3(e, local.onPointerEnter);
if (e.pointerType === "touch")
return;
context.cancelLeaveTimer();
if (context.dataset()["data-expanded"] === "")
return;
timeoutId = window.setTimeout(() => {
menuContext?.triggerRef()?.focus();
setTimeout(() => {
context.setAutoFocusMenu(true);
});
}, context.delayDuration());
};
const onPointerLeave = (e) => {
callHandler3(e, local.onPointerLeave);
if (e.pointerType === "touch")
return;
context.startLeaveTimer();
if (timeoutId)
clearTimeout(timeoutId);
};
return <li role="presentation"><MenubarTrigger
onClick={onClick}
onPointerEnter={onPointerEnter}
onPointerLeave={onPointerLeave}
{...others}
/></li>;
}
// src/navigation-menu/navigation-menu-viewport.tsx
import {
composeEventHandlers,
mergeRefs as mergeRefs4
} from "@kobalte/utils";
import { combineStyle } from "@solid-primitives/props";
import {
Show as Show2,
createEffect as createEffect5,
createMemo as createMemo2,
createSignal as createSignal5,
on as on4,
splitProps as splitProps7
} from "solid-js";
function NavigationMenuViewport(props) {
const context = useNavigationMenuContext();
const menubarContext = useMenubarContext();
const [ref, setRef] = createSignal5();
const [local, others] = splitProps7(props, [
"ref",
"style",
"onEscapeKeyDown"
]);
const close = () => {
menubarContext.setAutoFocusMenu(false);
menubarContext.closeMenu();
};
const onEscapeKeyDown = (e) => {
close();
};
const size = createSize(ref);
createEffect5(
on4(
() => menubarContext.value() ? menubarContext.menuRefMap().get(menubarContext.value()) : void 0,
(menu) => {
if (menu === void 0 || menu[0] === void 0)
return;
setRef(menu[0]);
}
)
);
const height = createMemo2((prev) => {
if (ref() === void 0 || !context.viewportPresent())
return void 0;
if (size.height() === 0)
return prev;
return size.height();
});
const width = createMemo2((prev) => {
if (ref() === void 0 || !context.viewportPresent())
return void 0;
if (size.width() === 0)
return prev;
return size.width();
});
return <Show2 when={context.viewportPresent()}><Popper.Positioner role="presentation"><DismissableLayer
as="li"
ref={mergeRefs4(context.setViewportRef, local.ref)}
excludedElements={[context.rootRef]}
bypassTopMostLayerCheck
style={combineStyle(
{
"--kb-menu-content-transform-origin": "var(--kb-popper-content-transform-origin)",
"--kb-navigation-menu-viewport-height": height() ? `${height()}px` : void 0,
"--kb-navigation-menu-viewport-width": width() ? `${width()}px` : void 0,
position: "relative"
},
local.style
)}
onEscapeKeyDown={composeEventHandlers([
local.onEscapeKeyDown,
onEscapeKeyDown
])}
onDismiss={close}
data-orientation={menubarContext.orientation()}
{...context.dataset()}
{...others}
/></Popper.Positioner></Show2>;
}
// src/navigation-menu/index.tsx
var NavigationMenu = Object.assign(NavigationMenuRoot, {
Arrow: NavigationMenuArrow,
CheckboxItem: MenuCheckboxItem,
Content: NavigationMenuContent,
Group: MenuGroup,
GroupLabel: MenuGroupLabel,
Icon: MenuIcon,
Item: NavigationMenuItem,
ItemDescription: MenuItemDescription,
ItemIndicator: MenuItemIndicator,
ItemLabel: MenuItemLabel,
Portal: NavigationMenuPortal,
RadioGroup: MenuRadioGroup,
RadioItem: MenuRadioItem,
Menu: NavigationMenuMenu,
Separator: SeparatorRoot,
Sub: MenuSub,
SubContent: MenuSubContent,
SubTrigger: MenuSubTrigger,
Trigger: NavigationMenuTrigger,
Viewport: NavigationMenuViewport
});
export {
NavigationMenuArrow as Arrow,
MenuCheckboxItem as CheckboxItem,
NavigationMenuContent as Content,
MenuGroup as Group,
MenuGroupLabel as GroupLabel,
MenuIcon as Icon,
NavigationMenuItem as Item,
MenuItemDescription as ItemDescription,
MenuItemIndicator as ItemIndicator,
MenuItemLabel as ItemLabel,
NavigationMenuMenu as Menu,
NavigationMenu,
NavigationMenuPortal as Portal,
MenuRadioGroup as RadioGroup,
MenuRadioItem as RadioItem,
NavigationMenuRoot as Root,
SeparatorRoot as Separator,
MenuSub as Sub,
MenuSubContent as SubContent,
MenuSubTrigger as SubTrigger,
NavigationMenuTrigger as Trigger,
NavigationMenuViewport as Viewport,
useNavigationMenuContext
};