UNPKG

@codeworker.br/govbr-tw-react

Version:

Biblioteca de componentes React usando Tailwind CSS que implementa o Padrão Digital de Governo.

390 lines (389 loc) 24.1 kB
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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { createContext, forwardRef, useCallback, useContext, useEffect, useMemo, useState, } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBars, faChevronDown, } from "@fortawesome/free-solid-svg-icons"; import { cn } from "../../libs/utils"; import { Button } from "../Button"; import Input from "../Input"; import Skeleton from "../Skeleton"; import Tooltip from "../Tooltip"; import { sidebarContainerVariants, sidebarContentVariants, sidebarFooterVariants, sidebarGroupActionVariants, sidebarGroupLabelVariants, sidebarHeaderVariants, sidebarMenuActionVariants, sidebarMenuBadgeVariants, sidebarMenuButtonVariants, sidebarMenuSubButtonVariants, sidebarMenuSubVariants, sidebarRailVariants, sidebarSeparatorVariants, } from "./variants"; const SIDEBAR_WIDTH = "16rem"; const SIDEBAR_WIDTH_MOBILE = "18rem"; const SIDEBAR_WIDTH_ICON = "3.5rem"; const SIDEBAR_MEDIA_QUERY = "(max-width: 1023px)"; const SidebarContext = createContext(null); function useSidebarContext() { const ctx = useContext(SidebarContext); if (!ctx) { throw new Error("useSidebar must be used within <SidebarProvider>."); } return ctx; } function useMediaQuery(query) { const [matches, setMatches] = useState(false); useEffect(() => { if (typeof window === "undefined") { return; } const media = window.matchMedia(query); const listener = () => setMatches(media.matches); listener(); media.addEventListener("change", listener); return () => media.removeEventListener("change", listener); }, [query]); return matches; } function useIsMobile() { return useMediaQuery(SIDEBAR_MEDIA_QUERY); } export const SidebarProvider = forwardRef((_a, ref) => { var { defaultOpen = true, open: openProp, onOpenChange, theme = "light", side = "left", variant = "sidebar", collapsible = "offcanvas", showRail = true, className, children, style } = _a, rest = __rest(_a, ["defaultOpen", "open", "onOpenChange", "theme", "side", "variant", "collapsible", "showRail", "className", "children", "style"]); const isMobile = useIsMobile(); const [openMobile, setOpenMobile] = useState(false); const [internalOpen, setInternalOpen] = useState(defaultOpen); const open = collapsible === "none" ? true : openProp !== null && openProp !== void 0 ? openProp : internalOpen; const setOpen = useCallback((value) => { if (collapsible === "none") { return; } const next = typeof value === "function" ? value(open) : value; onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next); if (openProp === undefined) { setInternalOpen(next); } }, [collapsible, onOpenChange, open, openProp]); const toggleSidebar = useCallback(() => { if (isMobile) { setOpenMobile((prev) => !prev); return; } if (collapsible === "none") { return; } setOpen((prev) => !prev); }, [collapsible, isMobile, setOpen]); useEffect(() => { if (!isMobile) { setOpenMobile(false); } }, [isMobile]); const state = open ? "expanded" : "collapsed"; const isIconCollapsed = !isMobile && collapsible === "icon" && state === "collapsed"; const contextValue = useMemo(() => ({ state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar, theme, side, variant, collapsible, isIconCollapsed, showRail, }), [ collapsible, isIconCollapsed, isMobile, open, openMobile, showRail, setOpen, side, state, theme, toggleSidebar, variant, ]); return (_jsx(SidebarContext.Provider, { value: contextValue, children: _jsx("div", Object.assign({ ref: ref, className: cn("group/sidebar flex min-h-dvh w-full", className), "data-state": state, "data-side": side, "data-theme": theme, "data-variant": variant, "data-collapsible": collapsible, style: Object.assign({ ["--sidebar-width"]: SIDEBAR_WIDTH, ["--sidebar-width-mobile"]: SIDEBAR_WIDTH_MOBILE, ["--sidebar-width-icon"]: SIDEBAR_WIDTH_ICON }, style) }, rest, { children: children })) })); }); SidebarProvider.displayName = "SidebarProvider"; export function useSidebar() { return useSidebarContext(); } export const Sidebar = forwardRef((_a, ref) => { var { className, children, style, theme: themeProp, side: sideProp, variant: variantProp, collapsible: collapsibleProp } = _a, rest = __rest(_a, ["className", "children", "style", "theme", "side", "variant", "collapsible"]); const context = useSidebarContext(); const theme = themeProp !== null && themeProp !== void 0 ? themeProp : context.theme; const side = sideProp !== null && sideProp !== void 0 ? sideProp : context.side; const variant = variantProp !== null && variantProp !== void 0 ? variantProp : context.variant; const collapsible = collapsibleProp !== null && collapsibleProp !== void 0 ? collapsibleProp : context.collapsible; const { state, openMobile, isMobile } = context; const isIconCollapsed = !isMobile && collapsible === "icon" && state === "collapsed"; const isOffCanvasCollapsed = !isMobile && collapsible === "offcanvas" && state === "collapsed"; const width = isMobile ? "var(--sidebar-width-mobile)" : isIconCollapsed ? "var(--sidebar-width-icon)" : "var(--sidebar-width)"; const verticalInset = !isMobile && variant !== "sidebar" ? "top-4 bottom-4" : "top-0 bottom-0"; const horizontalInset = !isMobile && variant !== "sidebar" ? side === "left" ? "left-4" : "right-4" : side === "left" ? "left-0" : "right-0"; const containerClasses = sidebarContainerVariants({ theme, variant, side }); let translateClass = "translate-x-0"; if (isMobile) { translateClass = side === "left" ? openMobile ? "translate-x-0" : "-translate-x-full" : openMobile ? "translate-x-0" : "translate-x-full"; } else if (isOffCanvasCollapsed) { translateClass = side === "left" ? "-translate-x-full" : "translate-x-full"; } const pointerEventsClass = isMobile && !openMobile ? "pointer-events-none" : "pointer-events-auto"; return (_jsx("aside", Object.assign({ ref: ref, "data-sidebar": "container", "data-state": state, "data-side": side, "data-variant": variant, "data-theme": theme, "data-collapsible": collapsible, "aria-hidden": isMobile && !openMobile ? true : undefined, className: cn(containerClasses, verticalInset, horizontalInset, translateClass, pointerEventsClass, className), style: Object.assign({ width, ["--sidebar-current-width"]: width }, style) }, rest, { children: _jsx("div", { className: "flex h-full flex-col overflow-hidden", children: children }) }))); }); Sidebar.displayName = "Sidebar"; export const SidebarOverlay = () => { const { openMobile, setOpenMobile, isMobile } = useSidebarContext(); if (!isMobile) { return null; } return (_jsx("div", { "data-sidebar": "overlay", className: cn("fixed inset-0 z-30 transition-opacity duration-300", openMobile ? "opacity-100" : "pointer-events-none opacity-0"), onClick: () => setOpenMobile(false) })); }; export const SidebarRail = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { toggleSidebar, collapsible, isMobile, side, state, theme, showRail } = useSidebarContext(); if (isMobile || collapsible !== "icon" || !showRail) { return null; } return (_jsx("button", Object.assign({ ref: ref, type: "button", "data-sidebar": "rail", "data-state": state, className: cn("absolute top-1/2 z-50", side === "left" ? "right-0 translate-x-1/2" : "left-0 -translate-x-1/2", sidebarRailVariants({ theme }), className), onClick: toggleSidebar, "aria-label": "Alternar largura da barra lateral" }, rest))); }); SidebarRail.displayName = "SidebarRail"; export const SidebarContent = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { isIconCollapsed, theme } = useSidebarContext(); const contentClasses = sidebarContentVariants({ theme }); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "content", className: cn(contentClasses, isIconCollapsed && "items-center overflow-hidden", className) }, rest))); }); SidebarContent.displayName = "SidebarContent"; export const SidebarHeader = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "header", className: cn(sidebarHeaderVariants({ theme }), isIconCollapsed && "items-center justify-center p-4", className) }, rest))); }); SidebarHeader.displayName = "SidebarHeader"; export const SidebarFooter = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "footer", className: cn(sidebarFooterVariants({ theme }), isIconCollapsed && "items-center px-2 py-4", className) }, rest))); }); SidebarFooter.displayName = "SidebarFooter"; export const SidebarSeparator = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme } = useSidebarContext(); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "separator", className: cn(sidebarSeparatorVariants({ theme }), className) }, rest))); }); SidebarSeparator.displayName = "SidebarSeparator"; export const SidebarGroup = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "group", className: cn("relative flex w-full min-w-0 flex-col", className) }, rest))); }); SidebarGroup.displayName = "SidebarGroup"; export const SidebarGroupLabel = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { collapsible, isIconCollapsed, theme } = useSidebarContext(); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "group-label", className: cn(sidebarGroupLabelVariants({ theme }), collapsible === "icon" && isIconCollapsed && "-mt-8 opacity-0", className) }, rest))); }); SidebarGroupLabel.displayName = "SidebarGroupLabel"; export const SidebarGroupAction = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); return (_jsx("button", Object.assign({ ref: ref, type: "button", "data-sidebar": "group-action", className: cn(sidebarGroupActionVariants({ theme }), isIconCollapsed && "hidden", className) }, rest))); }); SidebarGroupAction.displayName = "SidebarGroupAction"; export const SidebarGroupContent = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "group-content", className: cn("flex w-full flex-col gap-1", className) }, rest))); }); SidebarGroupContent.displayName = "SidebarGroupContent"; const SidebarMenuCollapsibleContext = createContext(null); function useSidebarMenuCollapsible() { return useContext(SidebarMenuCollapsibleContext); } export const SidebarMenuItem = forwardRef((_a, ref) => { var { collapsible = false, defaultOpen = false, open: openProp, onOpenChange, disabled = false, className, children } = _a, rest = __rest(_a, ["collapsible", "defaultOpen", "open", "onOpenChange", "disabled", "className", "children"]); const { isIconCollapsed } = useSidebarContext(); const [internalOpen, setInternalOpen] = useState(defaultOpen); const open = openProp !== null && openProp !== void 0 ? openProp : internalOpen; const setOpen = useCallback((value) => { const next = typeof value === "function" ? value(open) : value; onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next); if (openProp === undefined) { setInternalOpen(next); } }, [onOpenChange, open, openProp]); useEffect(() => { if (isIconCollapsed) { if (open) { setOpen(false); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isIconCollapsed]); const collapsibleContext = collapsible ? { open, toggle: () => setOpen((prev) => !prev), disabled, } : null; const content = (_jsx("li", Object.assign({ ref: ref, "data-sidebar": "menu-item", "data-state": collapsible ? (open ? "open" : "closed") : undefined, "data-collapsible": collapsible ? "true" : undefined, className: cn("group/menu-item relative", className) }, rest, { children: children }))); if (!collapsibleContext) { return content; } return (_jsx(SidebarMenuCollapsibleContext.Provider, { value: collapsibleContext, children: content })); }); SidebarMenuItem.displayName = "SidebarMenuItem"; export const SidebarMenuButton = forwardRef((_a, ref) => { var { className, children, icon, isActive, tooltip, size = "md", variant = "default", onClick, collapsedIconVariant = "ghost", collapsedIconActiveVariant = "default" } = _a, rest = __rest(_a, ["className", "children", "icon", "isActive", "tooltip", "size", "variant", "onClick", "collapsedIconVariant", "collapsedIconActiveVariant"]); const { collapsible, isIconCollapsed, theme } = useSidebarContext(); const collapsibleContext = useSidebarMenuCollapsible(); const _b = rest, { ["aria-label"]: ariaLabelProp } = _b, buttonRest = __rest(_b, ["aria-label"]); const accessibleLabel = ariaLabelProp !== null && ariaLabelProp !== void 0 ? ariaLabelProp : (typeof children === "string" ? String(children) : undefined); const collapsedAriaLabel = accessibleLabel !== null && accessibleLabel !== void 0 ? accessibleLabel : "Abrir item do menu"; const buttonClasses = cn(sidebarMenuButtonVariants({ size, variant, iconOnly: isIconCollapsed ? true : false, theme, active: isActive ? true : false, }), "group-has-[data-sidebar=menu-action]/menu-item:pr-12"); const handleClick = (event) => { onClick === null || onClick === void 0 ? void 0 : onClick(event); if (event.defaultPrevented) { return; } if (collapsibleContext && !isIconCollapsed && !collapsibleContext.disabled) { event.preventDefault(); collapsibleContext.toggle(); } }; const resolvedTooltip = tooltip !== null && tooltip !== void 0 ? tooltip : (typeof children === "string" ? children : undefined); if (isIconCollapsed) { const collapsedVariant = isActive ? collapsedIconActiveVariant : collapsedIconVariant; const iconButton = (_jsx(Button, Object.assign({ ref: ref, type: "button", size: "icon", variant: collapsedVariant, className: cn("h-10 w-10", className), onClick: handleClick, "aria-expanded": collapsibleContext ? collapsibleContext.open : undefined, "aria-disabled": collapsibleContext === null || collapsibleContext === void 0 ? void 0 : collapsibleContext.disabled, "aria-label": collapsedAriaLabel }, buttonRest, { children: icon !== null && icon !== void 0 ? icon : (_jsx("span", { className: "sr-only", children: collapsedAriaLabel })) }))); if (!resolvedTooltip) { return iconButton; } const tooltipContent = typeof resolvedTooltip === "string" ? (_jsx("span", { className: "text-xs", children: resolvedTooltip })) : (resolvedTooltip); return (_jsxs(Tooltip, { position: "right", className: "z-[999]", children: [iconButton, tooltipContent] })); } const buttonContent = (_jsxs("button", Object.assign({ ref: ref, type: "button", className: cn(buttonClasses, className), "aria-expanded": collapsibleContext ? collapsibleContext.open : undefined, "aria-disabled": collapsibleContext === null || collapsibleContext === void 0 ? void 0 : collapsibleContext.disabled, "aria-label": ariaLabelProp, onClick: handleClick }, buttonRest, { children: [icon ? _jsx("span", { className: "inline-flex size-5 items-center justify-center", children: icon }) : null, !isIconCollapsed && _jsx("span", { className: "flex-1 truncate", children: children }), collapsibleContext ? (_jsx("span", { className: cn("ml-auto inline-flex size-4 items-center justify-center transition-transform", collapsibleContext.open ? "rotate-180" : "rotate-0", (collapsible === "icon" && isIconCollapsed) && "hidden"), "aria-hidden": true, children: _jsx(FontAwesomeIcon, { icon: faChevronDown, className: "size-3" }) })) : null] }))); return buttonContent; }); SidebarMenuButton.displayName = "SidebarMenuButton"; export const SidebarMenu = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx("ul", Object.assign({ ref: ref, "data-sidebar": "menu", className: cn("flex w-full min-w-0 flex-col gap-1", className) }, rest))); }); SidebarMenu.displayName = "SidebarMenu"; export const SidebarMenuAction = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); return (_jsx("button", Object.assign({ ref: ref, type: "button", "data-sidebar": "menu-action", className: cn(sidebarMenuActionVariants({ theme }), isIconCollapsed && "hidden", className) }, rest))); }); SidebarMenuAction.displayName = "SidebarMenuAction"; export const SidebarMenuBadge = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "menu-badge", className: cn(sidebarMenuBadgeVariants({ theme }), isIconCollapsed && "hidden", className) }, rest))); }); SidebarMenuBadge.displayName = "SidebarMenuBadge"; export const SidebarMenuSkeleton = forwardRef((_a, ref) => { var { className, showIcon = false } = _a, rest = __rest(_a, ["className", "showIcon"]); const width = useMemo(() => `${Math.floor(Math.random() * 40) + 50}%`, []); return (_jsxs("div", Object.assign({ ref: ref, "data-sidebar": "menu-skeleton", className: cn("flex h-9 items-center gap-2 rounded-md px-4", className) }, rest, { children: [showIcon ? _jsx(Skeleton, { className: "size-5 rounded-md" }) : null, _jsx(Skeleton, { className: "h-4 flex-none rounded-md", style: { width } })] }))); }); SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton"; export const SidebarMenuSub = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); const { theme, isIconCollapsed } = useSidebarContext(); const collapsibleContext = useSidebarMenuCollapsible(); const hidden = collapsibleContext ? !collapsibleContext.open : false; return (_jsx("ul", Object.assign({ ref: ref, "data-sidebar": "menu-sub", "data-state": collapsibleContext ? (collapsibleContext.open ? "open" : "closed") : undefined, className: cn(sidebarMenuSubVariants({ theme }), (hidden || isIconCollapsed) && "hidden", className) }, rest))); }); SidebarMenuSub.displayName = "SidebarMenuSub"; export const SidebarMenuSubItem = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx("li", Object.assign({ ref: ref, "data-sidebar": "menu-sub-item", className: cn("relative", className) }, rest))); }); SidebarMenuSubItem.displayName = "SidebarMenuSubItem"; export const SidebarMenuSubButton = forwardRef((_a, ref) => { var { className, children, isActive, size = "md" } = _a, rest = __rest(_a, ["className", "children", "isActive", "size"]); const { theme } = useSidebarContext(); return (_jsx("button", Object.assign({ ref: ref, type: "button", "data-sidebar": "menu-sub-button", className: cn(sidebarMenuSubButtonVariants({ theme, size, active: isActive !== null && isActive !== void 0 ? isActive : false, }), className) }, rest, { children: _jsx("span", { className: "truncate", children: children }) }))); }); SidebarMenuSubButton.displayName = "SidebarMenuSubButton"; export const SidebarMenuSubAction = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx("button", Object.assign({ ref: ref, type: "button", "data-sidebar": "menu-sub-action", className: cn("ml-auto inline-flex h-6 w-6 items-center justify-center rounded-md text-xs", className) }, rest))); }); SidebarMenuSubAction.displayName = "SidebarMenuSubAction"; export const SidebarTrigger = forwardRef((_a, ref) => { var { className, variant, onClick, icon } = _a, rest = __rest(_a, ["className", "variant", "onClick", "icon"]); const { toggleSidebar, theme, isMobile, collapsible } = useSidebarContext(); const resolvedVariant = variant !== null && variant !== void 0 ? variant : (theme === "dark" ? "ghost-dark" : "ghost"); return (_jsxs(Button, Object.assign({ ref: ref, size: "icon", variant: resolvedVariant, className: cn(className), onClick: (event) => { onClick === null || onClick === void 0 ? void 0 : onClick(event); if (event.defaultPrevented) { return; } if (!isMobile && collapsible === "none") { return; } toggleSidebar(); } }, rest, { children: [icon !== null && icon !== void 0 ? icon : _jsx(FontAwesomeIcon, { icon: faBars }), _jsx("span", { className: "sr-only", children: "Alternar barra lateral" })] }))); }); SidebarTrigger.displayName = "SidebarTrigger"; export const SidebarInput = forwardRef((_a, ref) => { var { className } = _a, rest = __rest(_a, ["className"]); return (_jsx(Input, Object.assign({ ref: ref, className: cn("h-9 w-full bg-transparent shadow-none focus-visible:ring-2 focus-visible:ring-govbr-blue-warm-vivid-50", className), density: "default" }, rest))); }); SidebarInput.displayName = "SidebarInput"; export const SidebarInset = forwardRef((_a, ref) => { var { className, style } = _a, rest = __rest(_a, ["className", "style"]); const { side, variant, collapsible, state, isMobile, isIconCollapsed } = useSidebarContext(); const baseWidth = isMobile ? "0px" : collapsible === "offcanvas" && state === "collapsed" ? "0px" : isIconCollapsed ? "var(--sidebar-width-icon)" : "var(--sidebar-width)"; const extraGap = !isMobile && variant !== "sidebar" ? "1.5rem" : "0px"; const total = extraGap !== "0px" ? `calc(${baseWidth} + ${extraGap})` : baseWidth; const insetStyle = side === "left" ? { marginLeft: total } : { marginRight: total }; return (_jsx("div", Object.assign({ ref: ref, "data-sidebar": "inset", className: cn("min-h-dvh w-full transition-[margin] duration-300 ease-in-out", className), style: Object.assign(Object.assign({}, insetStyle), style) }, rest))); }); SidebarInset.displayName = "SidebarInset"; export { SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON, SIDEBAR_WIDTH_MOBILE, };