UNPKG

analytica-frontend-lib

Version:

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

355 lines (353 loc) 10.8 kB
// src/components/Menu/Menu.tsx import { create, useStore } from "zustand"; import { useEffect, useRef, forwardRef, isValidElement, Children, cloneElement, useState } from "react"; import { CaretLeft, CaretRight } from "phosphor-react"; // src/utils/utils.ts import { clsx } from "clsx"; import { twMerge } from "tailwind-merge"; function cn(...inputs) { return twMerge(clsx(inputs)); } // src/components/Menu/Menu.tsx import { jsx, jsxs } from "react/jsx-runtime"; var createMenuStore = (onValueChange) => create((set) => ({ value: "", setValue: (value) => { set({ value }); onValueChange?.(value); }, onValueChange })); var useMenuStore = (externalStore) => { if (!externalStore) throw new Error("MenuItem must be inside Menu"); return externalStore; }; var VARIANT_CLASSES = { menu: "bg-background shadow-soft-shadow-1 px-6", menu2: "", "menu-overflow": "", breadcrumb: "bg-transparent shadow-none !px-0" }; var Menu = forwardRef( ({ className, children, defaultValue, value: propValue, variant = "menu", onValueChange, ...props }, ref) => { const storeRef = useRef(null); storeRef.current ??= createMenuStore(onValueChange); const store = storeRef.current; const { setValue } = useStore(store, (s) => s); useEffect(() => { setValue(propValue ?? defaultValue); }, [defaultValue, propValue, setValue]); const baseClasses = variant === "menu-overflow" ? "w-fit py-2 flex flex-row items-center justify-center" : "w-full py-2 flex flex-row items-center justify-center"; const variantClasses = VARIANT_CLASSES[variant]; return /* @__PURE__ */ jsx( "div", { ref, className: ` ${baseClasses} ${variantClasses} ${className ?? ""} `, ...props, children: injectStore(children, store) } ); } ); Menu.displayName = "Menu"; var MenuContent = forwardRef( ({ className, children, variant = "menu", ...props }, ref) => { const baseClasses = "w-full flex flex-row items-center gap-2"; const variantClasses = variant === "menu2" || variant === "menu-overflow" ? "overflow-x-auto scroll-smooth" : ""; return /* @__PURE__ */ jsx( "ul", { ref, className: ` ${baseClasses} ${variantClasses} ${variant == "breadcrumb" ? "flex-wrap" : ""} ${className ?? ""} `, style: variant === "menu2" || variant === "menu-overflow" ? { scrollbarWidth: "none", msOverflowStyle: "none" } : void 0, ...props, children } ); } ); MenuContent.displayName = "MenuContent"; var MenuItem = forwardRef( ({ className, children, value, disabled = false, store: externalStore, variant = "menu", separator = false, ...props }, ref) => { const store = useMenuStore(externalStore); const { value: selectedValue, setValue } = useStore(store, (s) => s); const handleClick = (e) => { if (!disabled) { setValue(value); } props.onClick?.(e); }; const commonProps = { role: "menuitem", "aria-disabled": disabled, ref, onClick: handleClick, onKeyDown: (e) => { if (["Enter", " "].includes(e.key)) handleClick(e); }, tabIndex: disabled ? -1 : 0, onMouseDown: (e) => { e.preventDefault(); }, ...props }; const variants = { menu: /* @__PURE__ */ jsx( "li", { "data-variant": "menu", className: ` w-full flex flex-col items-center justify-center gap-0.5 py-1 px-2 rounded-sm font-medium text-xs [&>svg]:size-6 cursor-pointer hover:bg-primary-600 hover:text-text focus:outline-none focus:border-indicator-info focus:border-2 ${selectedValue === value ? "bg-primary-50 text-primary-950" : "text-text-950"} ${className ?? ""} `, ...commonProps, children } ), menu2: /* @__PURE__ */ jsxs( "li", { "data-variant": "menu2", className: ` w-full flex flex-col items-center px-2 pt-4 gap-3 cursor-pointer focus:rounded-sm justify-center hover:bg-background-100 rounded-lg focus:outline-none focus:border-indicator-info focus:border-2 ${selectedValue === value ? "" : "pb-4"} `, ...commonProps, children: [ /* @__PURE__ */ jsx( "span", { className: cn( "flex flex-row items-center gap-2 px-4 text-text-950 text-xs font-bold", className ), children } ), selectedValue === value && /* @__PURE__ */ jsx("div", { className: "h-1 w-full bg-primary-950 rounded-lg" }) ] } ), "menu-overflow": /* @__PURE__ */ jsxs( "li", { "data-variant": "menu-overflow", className: ` w-fit flex flex-col items-center px-2 pt-4 gap-3 cursor-pointer focus:rounded-sm justify-center hover:bg-background-100 rounded-lg focus:outline-none focus:border-indicator-info focus:border-2 ${selectedValue === value ? "" : "pb-4"} `, ...commonProps, children: [ /* @__PURE__ */ jsx( "span", { className: cn( "flex flex-row items-center gap-2 px-4 text-text-950 text-xs font-bold", className ), children } ), selectedValue === value && /* @__PURE__ */ jsx("div", { className: "h-1 w-full bg-primary-950 rounded-lg" }) ] } ), breadcrumb: /* @__PURE__ */ jsxs( "li", { "data-variant": "breadcrumb", className: ` flex flex-row gap-2 items-center w-fit p-2 rounded-lg hover:text-primary-600 cursor-pointer font-bold text-xs focus:outline-none focus:border-indicator-info focus:border-2 ${selectedValue === value ? "text-text-950" : "text-text-600"} ${className ?? ""} `, ...commonProps, children: [ /* @__PURE__ */ jsx( "span", { className: cn( "border-b border-text-600 hover:border-primary-600 text-inherit text-xs", selectedValue === value ? "border-b-0 font-bold" : "border-b-text-600" ), children } ), separator && /* @__PURE__ */ jsx( CaretRight, { size: 16, className: "text-text-600", "data-testid": "separator" } ) ] } ) }; return variants[variant] ?? variants["menu"]; } ); MenuItem.displayName = "MenuItem"; var MenuItemIcon = ({ className, icon, ...props }) => /* @__PURE__ */ jsx( "span", { className: cn( "bg-background-500 w-[21px] h-[21px] flex items-center justify-center [&>svg]:w-[17px] [&>svg]:h-[17px] rounded-sm", className ), ...props, children: icon } ); var internalScroll = (container, direction) => { if (!container) return; container.scrollBy({ left: direction === "left" ? -150 : 150, behavior: "smooth" }); }; var internalCheckScroll = (container, setShowLeftArrow, setShowRightArrow) => { if (!container) return; const { scrollLeft, scrollWidth, clientWidth } = container; setShowLeftArrow(scrollLeft > 0); setShowRightArrow(scrollLeft + clientWidth < scrollWidth); }; var MenuOverflow = ({ children, className, defaultValue, value, onValueChange, ...props }) => { const containerRef = useRef(null); const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); useEffect(() => { const checkScroll = () => internalCheckScroll( containerRef.current, setShowLeftArrow, setShowRightArrow ); checkScroll(); const container = containerRef.current; container?.addEventListener("scroll", checkScroll); window.addEventListener("resize", checkScroll); return () => { container?.removeEventListener("scroll", checkScroll); window.removeEventListener("resize", checkScroll); }; }, []); return /* @__PURE__ */ jsxs( "div", { "data-testid": "menu-overflow-wrapper", className: cn("relative w-full overflow-hidden", className), children: [ showLeftArrow && /* @__PURE__ */ jsxs( "button", { onClick: () => internalScroll(containerRef.current, "left"), className: "absolute left-0 top-1/2 -translate-y-1/2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white shadow-md cursor-pointer", "data-testid": "scroll-left-button", children: [ /* @__PURE__ */ jsx(CaretLeft, { size: 16 }), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Scroll left" }) ] } ), /* @__PURE__ */ jsx( Menu, { defaultValue, onValueChange, value, variant: "menu2", ...props, children: /* @__PURE__ */ jsx(MenuContent, { ref: containerRef, variant: "menu2", children }) } ), showRightArrow && /* @__PURE__ */ jsxs( "button", { onClick: () => internalScroll(containerRef.current, "right"), className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white shadow-md cursor-pointer", "data-testid": "scroll-right-button", children: [ /* @__PURE__ */ jsx(CaretRight, { size: 16 }), /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Scroll right" }) ] } ) ] } ); }; var injectStore = (children, store) => Children.map(children, (child) => { if (!isValidElement(child)) return child; const typedChild = child; const shouldInject = typedChild.type === MenuItem; return cloneElement(typedChild, { ...shouldInject ? { store } : {}, ...typedChild.props.children ? { children: injectStore(typedChild.props.children, store) } : {} }); }); var Menu_default = Menu; export { Menu, MenuContent, MenuItem, MenuItemIcon, MenuOverflow, Menu_default as default, internalCheckScroll, internalScroll, useMenuStore }; //# sourceMappingURL=index.mjs.map