@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
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 { 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, };