UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

376 lines (375 loc) 12.1 kB
"use client"; // src/components/DropdownMenu/DropdownMenu.tsx import { SignOut, User } from "phosphor-react"; import { forwardRef, useEffect, useRef, isValidElement, Children, cloneElement, useState } from "react"; import { create, useStore } from "zustand"; import { jsx, jsxs } from "react/jsx-runtime"; function createDropdownStore() { return create((set) => ({ open: false, setOpen: (open) => set({ open }) })); } var useDropdownStore = (externalStore) => { if (!externalStore) { throw new Error( "Component must be used within a DropdownMenu (store is missing)" ); } return externalStore; }; var injectStore = (children, store) => { return Children.map(children, (child) => { if (isValidElement(child)) { const typedChild = child; const newProps = { store }; if (typedChild.props.children) { newProps.children = injectStore(typedChild.props.children, store); } return cloneElement(typedChild, newProps); } return child; }); }; var DropdownMenu = ({ children, open, onOpenChange }) => { const storeRef = useRef(null); storeRef.current ??= createDropdownStore(); const store = storeRef.current; const isControlled = open !== void 0; const uncontrolledOpen = useStore(store, (s) => s.open); const currentOpen = isControlled ? open : uncontrolledOpen; const setOpen = (newOpen) => { onOpenChange?.(newOpen); if (!isControlled) store.setState({ open: newOpen }); }; const menuRef = useRef(null); const handleArrowDownOrArrowUp = (event) => { const menuContent = menuRef.current?.querySelector('[role="menu"]'); if (menuContent) { event.preventDefault(); const items = Array.from( menuContent.querySelectorAll( '[role="menuitem"]:not([aria-disabled="true"])' ) ).filter((el) => el instanceof HTMLElement); if (items.length === 0) return; const focusedItem = document.activeElement; const currentIndex = items.findIndex((item) => item === focusedItem); let nextIndex; if (event.key === "ArrowDown") { nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length; } else { nextIndex = currentIndex === -1 ? items.length - 1 : (currentIndex - 1 + items.length) % items.length; } items[nextIndex]?.focus(); } }; const handleDownkey = (event) => { if (event.key === "Escape") { setOpen(false); } else if (event.key === "ArrowDown" || event.key === "ArrowUp") { handleArrowDownOrArrowUp(event); } }; const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target)) { setOpen(false); } }; useEffect(() => { onOpenChange?.(currentOpen); if (currentOpen) { document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleDownkey); } return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("keydown", handleDownkey); }; }, [currentOpen]); useEffect(() => { if (isControlled) { store.setState({ open }); } }, []); return /* @__PURE__ */ jsx("div", { className: "relative", ref: menuRef, children: injectStore(children, store) }); }; var DropdownMenuTrigger = forwardRef(({ className, children, onClick, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = useStore(store, (s) => s.open); const toggleOpen = () => store.setState({ open: !open }); return /* @__PURE__ */ jsx( "button", { ref, className: `border border-border-200 cursor-pointer bg-background-muted hover:bg-background-200 transition-colors px-4 py-2 rounded-sm ${className}`, onClick: (e) => { e.stopPropagation(); toggleOpen(); if (onClick) onClick(e); }, "aria-expanded": open, ...props, children } ); }); DropdownMenuTrigger.displayName = "DropdownMenuTrigger"; var ITEM_SIZE_CLASSES = { small: "text-sm", medium: "text-md" }; var SIDE_CLASSES = { top: "bottom-full", right: "top-full", bottom: "top-full", left: "top-full" }; var ALIGN_CLASSES = { start: "left-0", center: "left-1/2 -translate-x-1/2", end: "right-0" }; var MenuLabel = forwardRef(({ className, inset, store: _store, ...props }, ref) => { return /* @__PURE__ */ jsx( "div", { ref, className: `text-sm w-full ${inset ? "pl-8" : ""} ${className ?? ""}`, ...props } ); }); MenuLabel.displayName = "MenuLabel"; var MenuContent = forwardRef( ({ className, align = "start", side = "bottom", sideOffset = 4, children, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = useStore(store, (s) => s.open); const [isVisible, setIsVisible] = useState(open); useEffect(() => { if (open) { setIsVisible(true); } else { const timer = setTimeout(() => setIsVisible(false), 200); return () => clearTimeout(timer); } }, [open]); if (!isVisible) return null; const getPositionClasses = () => { const vertical = SIDE_CLASSES[side]; const horizontal = ALIGN_CLASSES[align]; return `absolute ${vertical} ${horizontal}`; }; return /* @__PURE__ */ jsx( "div", { ref, role: "menu", className: ` bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md border-border-100 ${open ? "animate-in fade-in-0 zoom-in-95" : "animate-out fade-out-0 zoom-out-95"} ${getPositionClasses()} ${className} `, style: { marginTop: side === "bottom" ? sideOffset : void 0, marginBottom: side === "top" ? sideOffset : void 0, marginLeft: side === "right" ? sideOffset : void 0, marginRight: side === "left" ? sideOffset : void 0 }, ...props, children } ); } ); MenuContent.displayName = "MenuContent"; var MenuItem = forwardRef( ({ className, size = "small", children, iconRight, iconLeft, disabled = false, onClick, variant = "menu", store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const setOpen = useStore(store, (s) => s.setOpen); const sizeClasses = ITEM_SIZE_CLASSES[size]; const handleClick = (e) => { if (disabled) { e.preventDefault(); e.stopPropagation(); return; } onClick?.(e); setOpen(false); }; const getVariantClasses = () => { if (variant === "profile") { return "relative flex flex-row justify-between select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-6 [&>svg]:shrink-0"; } return "relative flex select-none items-center gap-2 rounded-sm p-3 text-sm outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0"; }; const getVariantProps = () => { return variant === "profile" ? { "data-variant": "profile" } : {}; }; return /* @__PURE__ */ jsxs( "div", { ref, role: "menuitem", ...getVariantProps(), "aria-disabled": disabled, className: ` focus-visible:bg-background-50 ${getVariantClasses()} ${sizeClasses} ${className} ${disabled ? "cursor-not-allowed text-text-400" : "cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"} `, onClick: handleClick, onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") handleClick(e); }, tabIndex: disabled ? -1 : 0, ...props, children: [ iconLeft, children, iconRight ] } ); } ); MenuItem.displayName = "MenuItem"; var MenuSeparator = forwardRef(({ className, store: _store, ...props }, ref) => /* @__PURE__ */ jsx( "div", { ref, className: `my-1 h-px bg-border-200 ${className}`, ...props } )); MenuSeparator.displayName = "MenuSeparator"; var ProfileMenuTrigger = forwardRef(({ className, onClick, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = useStore(store, (s) => s.open); const toggleOpen = () => store.setState({ open: !open }); return /* @__PURE__ */ jsx( "button", { ref, className: `rounded-lg size-10 bg-background-50 flex items-center justify-center ${className}`, onClick: (e) => { e.stopPropagation(); toggleOpen(); onClick?.(e); }, "aria-expanded": open, ...props, children: /* @__PURE__ */ jsx("span", { className: "size-6 rounded-full bg-background-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(User, { className: "text-background-950", size: 18 }) }) } ); }); ProfileMenuTrigger.displayName = "ProfileMenuTrigger"; var ProfileMenuHeader = forwardRef(({ className, name, email, store: _store, ...props }, ref) => { return /* @__PURE__ */ jsxs( "div", { ref, "data-component": "ProfileMenuHeader", className: ` flex flex-row gap-4 items-center ${className} `, ...props, children: [ /* @__PURE__ */ jsx("span", { className: "size-16 bg-background-100 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx(User, { size: 34, className: "text-background-950" }) }), /* @__PURE__ */ jsxs("div", { className: "flex flex-col ", children: [ /* @__PURE__ */ jsx("p", { className: "text-xl font-bold text-text-950", children: name }), /* @__PURE__ */ jsx("p", { className: "text-md text-text-600", children: email }) ] }) ] } ); }); ProfileMenuHeader.displayName = "ProfileMenuHeader"; var ProfileMenuSection = forwardRef(({ className, children, store: _store, ...props }, ref) => { return /* @__PURE__ */ jsx( "div", { ref, className: ` flex flex-col p-2 ${className} `, ...props, children } ); }); ProfileMenuSection.displayName = "ProfileMenuSection"; var ProfileMenuFooter = forwardRef( ({ className, disabled = false, onClick, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const setOpen = useStore(store, (s) => s.setOpen); return /* @__PURE__ */ jsxs( "button", { ref, className: `inline-flex items-center justify-center rounded-full cursor-pointer font-medium text-md px-5 py-2.5 w-full bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed ${className}`, disabled, onClick: (e) => { setOpen(false); onClick?.(e); }, ...props, children: [ /* @__PURE__ */ jsx("span", { className: "mr-2 flex items-center", children: /* @__PURE__ */ jsx(SignOut, {}) }), /* @__PURE__ */ jsx("span", { children: "Sair" }) ] } ); } ); ProfileMenuFooter.displayName = "ProfileMenuFooter"; var DropdownMenu_default = DropdownMenu; export { DropdownMenuTrigger, MenuContent, MenuItem, MenuLabel, MenuSeparator, ProfileMenuFooter, ProfileMenuHeader, ProfileMenuSection, ProfileMenuTrigger, createDropdownStore, DropdownMenu_default as default, useDropdownStore }; //# sourceMappingURL=index.mjs.map