UNPKG

analytica-frontend-lib

Version:

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

1,330 lines (1,314 loc) 84.1 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/components/NotificationCard/NotificationCard.tsx var NotificationCard_exports = {}; __export(NotificationCard_exports, { LegacyNotificationCard: () => LegacyNotificationCard, default: () => NotificationCard_default }); module.exports = __toCommonJS(NotificationCard_exports); var import_phosphor_react5 = require("phosphor-react"); var import_react9 = require("react"); // src/utils/utils.ts var import_clsx = require("clsx"); var import_tailwind_merge = require("tailwind-merge"); function cn(...inputs) { return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs)); } // src/components/DropdownMenu/DropdownMenu.tsx var import_phosphor_react3 = require("phosphor-react"); var import_react5 = require("react"); var import_react_dom = require("react-dom"); var import_zustand2 = require("zustand"); // src/components/Button/Button.tsx var import_jsx_runtime = require("react/jsx-runtime"); var VARIANT_ACTION_CLASSES = { solid: { primary: "bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed", positive: "bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed" }, outline: { primary: "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", positive: "bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed" }, link: { primary: "bg-transparent text-primary-950 hover:text-primary-400 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 disabled:opacity-40 disabled:cursor-not-allowed", positive: "bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed", negative: "bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed" } }; var SIZE_CLASSES = { "extra-small": "text-xs px-3.5 py-2", small: "text-sm px-4 py-2.5", medium: "text-md px-5 py-2.5", large: "text-lg px-6 py-3", "extra-large": "text-lg px-7 py-3.5" }; var Button = ({ children, iconLeft, iconRight, size = "medium", variant = "solid", action = "primary", className = "", disabled, type = "button", ...props }) => { const sizeClasses = SIZE_CLASSES[size]; const variantClasses = VARIANT_ACTION_CLASSES[variant][action]; const baseClasses = "inline-flex items-center justify-center rounded-full cursor-pointer font-medium"; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "button", { className: cn(baseClasses, variantClasses, sizeClasses, className), disabled, type, ...props, children: [ iconLeft && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mr-2 flex items-center", children: iconLeft }), children, iconRight && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ml-2 flex items-center", children: iconRight }) ] } ); }; var Button_default = Button; // src/components/Text/Text.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var Text = ({ children, size = "md", weight = "normal", color = "text-text-950", as, className = "", ...props }) => { let sizeClasses = ""; let weightClasses = ""; const sizeClassMap = { "2xs": "text-2xs", xs: "text-xs", sm: "text-sm", md: "text-md", lg: "text-lg", xl: "text-xl", "2xl": "text-2xl", "3xl": "text-3xl", "4xl": "text-4xl", "5xl": "text-5xl", "6xl": "text-6xl" }; sizeClasses = sizeClassMap[size] ?? sizeClassMap.md; const weightClassMap = { hairline: "font-hairline", light: "font-light", normal: "font-normal", medium: "font-medium", semibold: "font-semibold", bold: "font-bold", extrabold: "font-extrabold", black: "font-black" }; weightClasses = weightClassMap[weight] ?? weightClassMap.normal; const baseClasses = "font-primary"; const Component = as ?? "p"; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Component, { className: cn(baseClasses, sizeClasses, weightClasses, color, className), ...props, children } ); }; var Text_default = Text; // src/components/Modal/Modal.tsx var import_react = require("react"); var import_phosphor_react = require("phosphor-react"); // src/components/Modal/utils/videoUtils.ts var isYouTubeUrl = (url) => { const youtubeRegex = /^(https?:\/\/)?((www|m|music)\.)?(youtube\.com|youtu\.be|youtube-nocookie\.com)\/.+/i; return youtubeRegex.test(url); }; var isValidYouTubeHost = (host) => { if (host === "youtu.be") return "youtu.be"; const isValidYouTubeCom = host === "youtube.com" || host.endsWith(".youtube.com") && /^(www|m|music)\.youtube\.com$/.test(host); if (isValidYouTubeCom) return "youtube"; const isValidNoCookie = host === "youtube-nocookie.com" || host.endsWith(".youtube-nocookie.com") && /^(www|m|music)\.youtube-nocookie\.com$/.test(host); if (isValidNoCookie) return "nocookie"; return null; }; var extractYoutuBeId = (pathname) => { const firstSeg = pathname.split("/").filter(Boolean)[0]; return firstSeg || null; }; var extractYouTubeId = (pathname, searchParams) => { const parts = pathname.split("/").filter(Boolean); const [first, second] = parts; if (first === "embed" && second) return second; if (first === "shorts" && second) return second; if (first === "live" && second) return second; const v = searchParams.get("v"); if (v) return v; return null; }; var getYouTubeVideoId = (url) => { try { const u = new URL(url); const hostType = isValidYouTubeHost(u.hostname.toLowerCase()); if (!hostType) return null; if (hostType === "youtu.be") { return extractYoutuBeId(u.pathname); } return extractYouTubeId(u.pathname, u.searchParams); } catch { return null; } }; var getYouTubeEmbedUrl = (videoId) => { return `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=0&rel=0&modestbranding=1`; }; // src/components/Modal/Modal.tsx var import_jsx_runtime3 = require("react/jsx-runtime"); var SIZE_CLASSES2 = { xs: "max-w-[360px]", sm: "max-w-[420px]", md: "max-w-[510px]", lg: "max-w-[640px]", xl: "max-w-[970px]" }; var Modal = ({ isOpen, onClose, title, children, size = "md", className = "", closeOnEscape = true, footer, hideCloseButton = false, variant = "default", description, image, imageAlt, actionLink, actionLabel, contentClassName = "" }) => { const titleId = (0, import_react.useId)(); (0, import_react.useEffect)(() => { if (!isOpen || !closeOnEscape) return; const handleEscape = (event) => { if (event.key === "Escape") { onClose(); } }; document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); }, [isOpen, closeOnEscape, onClose]); (0, import_react.useEffect)(() => { if (!isOpen) return; const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; const originalOverflow = document.body.style.overflow; const originalPaddingRight = document.body.style.paddingRight; document.body.style.overflow = "hidden"; if (scrollbarWidth > 0) { document.body.style.paddingRight = `${scrollbarWidth}px`; const overlay = document.createElement("div"); overlay.id = "modal-scrollbar-overlay"; overlay.style.cssText = ` position: fixed; top: 0; right: 0; width: ${scrollbarWidth}px; height: 100vh; background-color: rgb(0 0 0 / 0.6); z-index: 40; pointer-events: none; `; document.body.appendChild(overlay); } return () => { document.body.style.overflow = originalOverflow; document.body.style.paddingRight = originalPaddingRight; const overlay = document.getElementById("modal-scrollbar-overlay"); if (overlay) { overlay.remove(); } }; }, [isOpen]); if (!isOpen) return null; const sizeClasses = SIZE_CLASSES2[size]; const baseClasses = "bg-secondary-50 rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4"; const dialogResetClasses = "p-0 m-0 border-none outline-none max-h-none static"; const modalClasses = cn( baseClasses, sizeClasses, dialogResetClasses, className ); const normalizeUrl = (href) => /^https?:\/\//i.test(href) ? href : `https://${href}`; const handleActionClick = () => { if (actionLink) { window.open(normalizeUrl(actionLink), "_blank", "noopener,noreferrer"); } }; if (variant === "activity") { return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "dialog", { className: modalClasses, "aria-labelledby": titleId, "aria-modal": "true", open: true, children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex justify-end p-6 pb-0", children: !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { onClick: onClose, className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2", "aria-label": "Fechar modal", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_phosphor_react.X, { size: 18 }) } ) }), /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center px-6 pb-6 gap-5", children: [ image && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "img", { src: image, alt: imageAlt ?? "", className: "w-[122px] h-[122px] object-contain" } ) }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "h2", { id: titleId, className: "text-lg font-semibold text-text-950 text-center", children: title } ), description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-normal text-text-400 text-center max-w-md leading-[21px]", children: description }), actionLink && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "w-full", children: [ (() => { const normalized = normalizeUrl(actionLink); const isYT = isYouTubeUrl(normalized); if (!isYT) return null; const id = getYouTubeVideoId(normalized); if (!id) { return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( Button_default, { variant: "solid", action: "primary", size: "large", className: "w-full", onClick: handleActionClick, children: actionLabel || "Iniciar Atividade" } ); } return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "iframe", { src: getYouTubeEmbedUrl(id), className: "w-full aspect-video rounded-lg", allowFullScreen: true, allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", title: "V\xEDdeo YouTube" } ); })(), !isYouTubeUrl(normalizeUrl(actionLink)) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( Button_default, { variant: "solid", action: "primary", size: "large", className: "w-full", onClick: handleActionClick, children: actionLabel || "Iniciar Atividade" } ) ] }) ] }) ] } ) }); } return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "dialog", { className: modalClasses, "aria-labelledby": titleId, "aria-modal": "true", open: true, children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between px-6 py-6", children: [ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { id: titleId, className: "text-lg font-semibold text-text-950", children: title }), !hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { onClick: onClose, className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2", "aria-label": "Fechar modal", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_phosphor_react.X, { size: 18 }) } ) ] }), children && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn("px-6 pb-6", contentClassName), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }), footer && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer }) ] } ) }); }; var Modal_default = Modal; // src/components/ThemeToggle/ThemeToggle.tsx var import_phosphor_react2 = require("phosphor-react"); var import_react4 = require("react"); // src/components/SelectionButton/SelectionButton.tsx var import_react2 = require("react"); var import_jsx_runtime4 = require("react/jsx-runtime"); var SelectionButton = (0, import_react2.forwardRef)( ({ icon, label, selected = false, className = "", disabled, ...props }, ref) => { const baseClasses = [ "inline-flex", "items-center", "justify-start", "gap-2", "p-4", "rounded-xl", "cursor-pointer", "border", "border-border-50", "bg-background", "text-sm", "text-text-700", "font-bold", "shadow-soft-shadow-1", "hover:bg-background-100", "focus-visible:outline-none", "focus-visible:ring-2", "focus-visible:ring-indicator-info", "focus-visible:ring-offset-0", "focus-visible:shadow-none", "active:ring-2", "active:ring-primary-950", "active:ring-offset-0", "active:shadow-none", "disabled:opacity-50", "disabled:cursor-not-allowed" ]; const stateClasses = selected ? ["ring-primary-950", "ring-2", "ring-offset-0", "shadow-none"] : []; const allClasses = [...baseClasses, ...stateClasses].join(" "); return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( "button", { ref, type: "button", className: cn(allClasses, className), disabled, "aria-pressed": selected, ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex items-center justify-center w-6 h-6", children: icon }), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: label }) ] } ); } ); SelectionButton.displayName = "SelectionButton"; var SelectionButton_default = SelectionButton; // src/hooks/useTheme.ts var import_react3 = require("react"); // src/store/themeStore.ts var import_zustand = require("zustand"); var import_middleware = require("zustand/middleware"); var applyThemeToDOM = (mode) => { const htmlElement = document.documentElement; const originalTheme = htmlElement.getAttribute("data-original-theme"); if (mode === "dark") { htmlElement.setAttribute("data-theme", "dark"); return true; } else if (mode === "light") { if (originalTheme) { htmlElement.setAttribute("data-theme", originalTheme); } return false; } else if (mode === "system") { const isSystemDark = window.matchMedia( "(prefers-color-scheme: dark)" ).matches; if (isSystemDark) { htmlElement.setAttribute("data-theme", "dark"); return true; } else if (originalTheme) { htmlElement.setAttribute("data-theme", originalTheme); return false; } } return false; }; var saveOriginalTheme = () => { const htmlElement = document.documentElement; const currentTheme = htmlElement.getAttribute("data-theme"); if (currentTheme && !htmlElement.getAttribute("data-original-theme")) { htmlElement.setAttribute("data-original-theme", currentTheme); } }; var useThemeStore = (0, import_zustand.create)()( (0, import_middleware.devtools)( (0, import_middleware.persist)( (set, get) => ({ // Initial state themeMode: "system", isDark: false, // Actions applyTheme: (mode) => { const isDark = applyThemeToDOM(mode); set({ isDark }); }, toggleTheme: () => { const { themeMode, applyTheme } = get(); let newMode; if (themeMode === "light") { newMode = "dark"; } else if (themeMode === "dark") { newMode = "light"; } else { newMode = "dark"; } set({ themeMode: newMode }); applyTheme(newMode); }, setTheme: (mode) => { const { applyTheme } = get(); set({ themeMode: mode }); applyTheme(mode); }, initializeTheme: () => { const { themeMode, applyTheme } = get(); saveOriginalTheme(); applyTheme(themeMode); }, handleSystemThemeChange: () => { const { themeMode, applyTheme } = get(); if (themeMode === "system") { applyTheme("system"); } } }), { name: "theme-store", // Nome da chave no localStorage partialize: (state) => ({ themeMode: state.themeMode }) // Só persiste o themeMode, não o isDark } ), { name: "theme-store" } ) ); // src/hooks/useTheme.ts var useTheme = () => { const { themeMode, isDark, toggleTheme, setTheme, initializeTheme, handleSystemThemeChange } = useThemeStore(); (0, import_react3.useEffect)(() => { initializeTheme(); const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); mediaQuery.addEventListener("change", handleSystemThemeChange); return () => { mediaQuery.removeEventListener("change", handleSystemThemeChange); }; }, [initializeTheme, handleSystemThemeChange]); return { themeMode, isDark, toggleTheme, setTheme }; }; // src/components/ThemeToggle/ThemeToggle.tsx var import_jsx_runtime5 = require("react/jsx-runtime"); var ThemeToggle = ({ variant = "default", onToggle }) => { const { themeMode, setTheme } = useTheme(); const [tempTheme, setTempTheme] = (0, import_react4.useState)(themeMode); (0, import_react4.useEffect)(() => { setTempTheme(themeMode); }, [themeMode]); const problemTypes = [ { id: "light", title: "Claro", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_phosphor_react2.Sun, { size: 24 }) }, { id: "dark", title: "Escuro", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_phosphor_react2.Moon, { size: 24 }) }, { id: "system", title: "Sistema", icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( "svg", { width: "25", height: "25", viewBox: "0 0 25 25", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( "path", { d: "M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z", fill: "#525252" } ) } ) } ]; const handleThemeSelect = (selectedTheme) => { if (variant === "with-save") { setTempTheme(selectedTheme); } else { setTheme(selectedTheme); } if (onToggle) { onToggle(selectedTheme); } }; const currentTheme = variant === "with-save" ? tempTheme : themeMode; return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex flex-row gap-2 sm:gap-4 py-2", children: problemTypes.map((type) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( SelectionButton_default, { icon: type.icon, label: type.title, selected: currentTheme === type.id, onClick: () => handleThemeSelect(type.id), className: "w-full p-2 sm:p-4" }, type.id )) }); }; // src/components/DropdownMenu/DropdownMenu.tsx var import_jsx_runtime6 = require("react/jsx-runtime"); function createDropdownStore() { return (0, import_zustand2.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 import_react5.Children.map(children, (child) => { if ((0, import_react5.isValidElement)(child)) { const typedChild = child; const displayName = typedChild.type.displayName; const allowed = [ "DropdownMenuTrigger", "DropdownContent", "DropdownMenuContent", "DropdownMenuSeparator", "DropdownMenuItem", "MenuLabel", "ProfileMenuTrigger", "ProfileMenuHeader", "ProfileMenuFooter", "ProfileToggleTheme" ]; let newProps = {}; if (allowed.includes(displayName)) { newProps.store = store; } if (typedChild.props.children) { newProps.children = injectStore(typedChild.props.children, store); } return (0, import_react5.cloneElement)(typedChild, newProps); } return child; }); }; var DropdownMenu = ({ children, open: propOpen, onOpenChange }) => { const storeRef = (0, import_react5.useRef)(null); storeRef.current ??= createDropdownStore(); const store = storeRef.current; const { open, setOpen: storeSetOpen } = (0, import_zustand2.useStore)(store, (s) => s); const setOpen = (newOpen) => { storeSetOpen(newOpen); }; const menuRef = (0, import_react5.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.indexOf(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) => { const target = event.target; if (menuRef.current?.contains(target)) { return; } if (target instanceof Element && target.closest('[data-dropdown-content="true"]')) { return; } setOpen(false); }; (0, import_react5.useEffect)(() => { if (open) { document.addEventListener("pointerdown", handleClickOutside); document.addEventListener("keydown", handleDownkey); } return () => { document.removeEventListener("pointerdown", handleClickOutside); document.removeEventListener("keydown", handleDownkey); }; }, [open]); (0, import_react5.useEffect)(() => { onOpenChange?.(open); }, [open, onOpenChange]); (0, import_react5.useEffect)(() => { if (propOpen !== void 0) { setOpen(propOpen); } }, [propOpen]); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "relative", ref: menuRef, children: injectStore(children, store) }); }; var DropdownMenuTrigger = (0, import_react5.forwardRef)(({ className, children, onClick, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = (0, import_zustand2.useStore)(store, (s) => s.open); const toggleOpen = () => store.setState({ open: !open }); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "button", { ref, type: "button", onClick: (e) => { e.stopPropagation(); toggleOpen(); onClick?.(e); }, "aria-expanded": open, className: cn( "appearance-none bg-transparent border-none p-0", className ), ...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 MENUCONTENT_VARIANT_CLASSES = { menu: "p-1", profile: "p-6" }; var MenuLabel = (0, import_react5.forwardRef)(({ className, inset, store: _store, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "div", { ref, className: cn("text-sm w-full", inset ? "pl-8" : "", className), ...props } ); }); MenuLabel.displayName = "MenuLabel"; var DropdownMenuContent = (0, import_react5.forwardRef)( ({ className, align = "start", side = "bottom", variant = "menu", sideOffset = 4, children, store: externalStore, portal = false, triggerRef, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = (0, import_zustand2.useStore)(store, (s) => s.open); const [isVisible, setIsVisible] = (0, import_react5.useState)(open); const [portalPosition, setPortalPosition] = (0, import_react5.useState)({ top: 0, left: 0 }); const contentRef = (0, import_react5.useRef)(null); (0, import_react5.useEffect)(() => { if (open) { setIsVisible(true); } else { const timer = setTimeout(() => setIsVisible(false), 200); return () => clearTimeout(timer); } }, [open]); (0, import_react5.useLayoutEffect)(() => { if (portal && open && triggerRef?.current) { const rect = triggerRef.current.getBoundingClientRect(); let top = rect.bottom + sideOffset; let left = rect.left; if (side === "left") { left = rect.left - sideOffset; top = rect.top; } else if (side === "right") { left = rect.right + sideOffset; top = rect.top; } else { if (align === "end") { left = rect.right; } else if (align === "center") { left = rect.left + rect.width / 2; } if (side === "top") { top = rect.top - sideOffset; } } setPortalPosition({ top, left }); } }, [portal, open, triggerRef, align, side, sideOffset]); if (!isVisible) return null; const getPositionClasses = () => { if (portal) { return "fixed"; } const vertical = SIDE_CLASSES[side]; const horizontal = ALIGN_CLASSES[align]; return `absolute ${vertical} ${horizontal}`; }; const getPortalAlignStyle = () => { if (!portal) return {}; const baseStyle = { top: portalPosition.top }; if (align === "end") { baseStyle.right = window.innerWidth - portalPosition.left; } else if (align === "center") { baseStyle.left = portalPosition.left; baseStyle.transform = "translateX(-50%)"; } else { baseStyle.left = portalPosition.left; } return baseStyle; }; const variantClasses = MENUCONTENT_VARIANT_CLASSES[variant]; const content = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "div", { ref: portal ? contentRef : ref, role: "menu", "data-dropdown-content": "true", className: ` bg-background z-50 min-w-[210px] overflow-hidden rounded-md border bg-popover 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()} ${variantClasses} ${className} `, style: { ...portal ? getPortalAlignStyle() : { 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 } ); if (portal && typeof document !== "undefined") { return (0, import_react_dom.createPortal)(content, document.body); } return content; } ); DropdownMenuContent.displayName = "DropdownMenuContent"; var DropdownMenuItem = (0, import_react5.forwardRef)( ({ className, size = "small", children, iconRight, iconLeft, disabled = false, onClick, variant = "menu", store: externalStore, preventClose = false, ...props }, ref) => { const store = useDropdownStore(externalStore); const setOpen = (0, import_zustand2.useStore)(store, (s) => s.setOpen); const sizeClasses = ITEM_SIZE_CLASSES[size]; const handleClick = (e) => { if (disabled) { e.preventDefault(); e.stopPropagation(); return; } if (e.type === "click") { onClick?.(e); } else if (e.type === "keydown") { if (e.key === "Enter" || e.key === " ") { onClick?.(e); } props.onKeyDown?.(e); } if (!preventClose) { setOpen(false); } }; const getVariantClasses = () => { if (variant === "profile") { return "relative flex flex-row justify-between select-none items-center gap-2 rounded-sm p-4 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__ */ (0, import_jsx_runtime6.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 === " ") { e.preventDefault(); e.stopPropagation(); handleClick(e); } }, tabIndex: disabled ? -1 : 0, ...props, children: [ iconLeft, /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-full", children }), iconRight ] } ); } ); DropdownMenuItem.displayName = "DropdownMenuItem"; var DropdownMenuSeparator = (0, import_react5.forwardRef)(({ className, store: _store, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "div", { ref, className: cn("my-1 h-px bg-border-200", className), ...props } )); DropdownMenuSeparator.displayName = "DropdownMenuSeparator"; var ProfileMenuTrigger = (0, import_react5.forwardRef)(({ className, onClick, store: externalStore, ...props }, ref) => { const store = useDropdownStore(externalStore); const open = (0, import_zustand2.useStore)(store, (s) => s.open); const toggleOpen = () => store.setState({ open: !open }); return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "button", { ref, className: cn( "rounded-lg size-10 bg-primary-50 flex items-center justify-center cursor-pointer", className ), onClick: (e) => { e.stopPropagation(); toggleOpen(); onClick?.(e); }, "aria-expanded": open, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "size-6 rounded-full bg-primary-100 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_phosphor_react3.User, { className: "text-primary-950", size: 18 }) }) } ); }); ProfileMenuTrigger.displayName = "ProfileMenuTrigger"; var ProfileMenuHeader = (0, import_react5.forwardRef)(({ className, name, email, photoUrl, store: _store, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( "div", { ref, "data-component": "ProfileMenuHeader", className: cn( "flex flex-row gap-4 items-center min-w-[280px]", className ), ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center overflow-hidden flex-shrink-0", children: photoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "img", { src: photoUrl, alt: "Foto de perfil", className: "w-full h-full object-cover" } ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_phosphor_react3.User, { size: 34, className: "text-primary-800" }) }), /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( Text_default, { size: "xl", weight: "bold", color: "text-text-950", className: "truncate", children: name } ), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { size: "md", color: "text-text-600", className: "truncate", children: email }) ] }) ] } ); }); ProfileMenuHeader.displayName = "ProfileMenuHeader"; var ProfileMenuInfo = (0, import_react5.forwardRef)( ({ className, schoolName, classYearName, schoolYearName, store: _store, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( "div", { ref, "data-component": "ProfileMenuInfo", className: cn("flex flex-row gap-4 items-center", className), ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-16 h-16" }), /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col ", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { size: "md", color: "text-text-600", children: schoolName }), /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex flex-row items-center gap-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { size: "md", color: "text-text-600", children: classYearName }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-text-600 text-xs align-middle", children: "\u25CF" }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { size: "md", color: "text-text-600", children: schoolYearName }) ] }) ] }) ] } ); } ); ProfileMenuInfo.displayName = "ProfileMenuInfo"; var ProfileToggleTheme = ({ store: externalStore, ...props }) => { const { themeMode, setTheme } = useTheme(); const [modalThemeToggle, setModalThemeToggle] = (0, import_react5.useState)(false); const [selectedTheme, setSelectedTheme] = (0, import_react5.useState)(themeMode); const internalStoreRef = (0, import_react5.useRef)(null); internalStoreRef.current ??= createDropdownStore(); const store = externalStore ?? internalStoreRef.current; const setOpen = (0, import_zustand2.useStore)(store, (s) => s.setOpen); const handleClick = (e) => { e.preventDefault(); e.stopPropagation(); setModalThemeToggle(true); }; const handleSave = () => { setTheme(selectedTheme); setModalThemeToggle(false); setOpen(false); }; const handleCancel = () => { setSelectedTheme(themeMode); setModalThemeToggle(false); setOpen(false); }; return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( DropdownMenuItem, { variant: "profile", preventClose: true, store, iconLeft: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "svg", { width: "24", height: "24", viewBox: "0 0 25 25", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "path", { d: "M12.5 2.75C15.085 2.75276 17.5637 3.78054 19.3916 5.6084C21.2195 7.43628 22.2473 9.915 22.25 12.5C22.25 14.4284 21.6778 16.3136 20.6064 17.917C19.5352 19.5201 18.0128 20.7699 16.2314 21.5078C14.4499 22.2458 12.489 22.4387 10.5977 22.0625C8.70642 21.6863 6.96899 20.758 5.60547 19.3945C4.24197 18.031 3.31374 16.2936 2.9375 14.4023C2.56129 12.511 2.75423 10.5501 3.49219 8.76855C4.23012 6.98718 5.47982 5.46483 7.08301 4.39355C8.68639 3.32221 10.5716 2.75 12.5 2.75ZM11.75 4.28516C9.70145 4.47452 7.7973 5.42115 6.41016 6.94043C5.02299 8.4599 4.25247 10.4426 4.25 12.5C4.25247 14.5574 5.02299 16.5401 6.41016 18.0596C7.7973 19.5789 9.70145 20.5255 11.75 20.7148V4.28516Z", fill: "currentColor" } ) } ), iconRight: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_phosphor_react3.CaretRight, {}), onClick: handleClick, onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); e.stopPropagation(); setModalThemeToggle(true); } }, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { size: "md", color: "text-text-700", children: "Apar\xEAncia" }) } ), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( Modal_default, { isOpen: modalThemeToggle, onClose: handleCancel, title: "Apar\xEAncia", size: "md", footer: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button_default, { variant: "outline", onClick: handleCancel, children: "Cancelar" }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button_default, { variant: "solid", onClick: handleSave, children: "Salvar" }) ] }), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-text-500", children: "Escolha o tema:" }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ThemeToggle, { variant: "with-save", onToggle: setSelectedTheme }) ] }) } ) ] }); }; ProfileToggleTheme.displayName = "ProfileToggleTheme"; var ProfileMenuSection = (0, import_react5.forwardRef)(({ className, children, store: _store, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref, className: cn("flex flex-col p-2", className), ...props, children }); }); ProfileMenuSection.displayName = "ProfileMenuSection"; var ProfileMenuFooter = ({ className, disabled = false, onClick, store: externalStore, ...props }) => { const store = useDropdownStore(externalStore); const setOpen = (0, import_zustand2.useStore)(store, (s) => s.setOpen); return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( Button_default, { variant: "outline", className: cn("w-full", className), disabled, onClick: (e) => { setOpen(false); onClick?.(e); }, ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mr-2 flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_phosphor_react3.SignOut, { className: "text-inherit" }) }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text_default, { color: "inherit", children: "Sair" }) ] } ); }; ProfileMenuFooter.displayName = "ProfileMenuFooter"; var DropdownMenu_default = DropdownMenu; // src/components/Skeleton/Skeleton.tsx var import_react6 = require("react"); var import_jsx_runtime7 = require("react/jsx-runtime"); var SKELETON_ANIMATION_CLASSES = { pulse: "animate-pulse", none: "" }; var SKELETON_VARIANT_CLASSES = { text: "h-4 bg-background-200 rounded", circular: "bg-background-200 rounded-full", rectangular: "bg-background-200", rounded: "bg-background-200 rounded-lg" }; var SPACING_CLASSES = { none: "", small: "space-y-1", medium: "space-y-2", large: "space-y-3" }; var Skeleton = (0, import_react6.forwardRef)( ({ variant = "text", width, height, animation = "pulse", lines = 1, spacing = "none", className = "", children, ...props }, ref) => { const animationClass = SKELETON_ANIMATION_CLASSES[animation]; const variantClass = SKELETON_VARIANT_CLASSES[variant]; const spacingClass = SPACING_CLASSES[spacing]; const style = { width: typeof width === "number" ? `${width}px` : width, height: typeof height === "number" ? `${height}px` : height }; if (variant === "text" && lines > 1) { return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "div", { ref, className: cn("flex flex-col", spacingClass, className), ...props, children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "div", { className: cn(variantClass, animationClass), style: index === lines - 1 ? { width: "60%" } : void 0 }, index )) } ); } return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "div", { ref, className: cn(variantClass, animationClass, className), style, ...props, children } ); } ); var SkeletonText = (0, import_react6.forwardRef)( (props, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { ref, variant: "text", ...props }) ); var SkeletonCircle = (0, import_react6.forwardRef)((props, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { ref, variant: "circular", ...props })); var SkeletonRectangle = (0, import_react6.forwardRef)((props, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { ref, variant: "rectangular", ...props })); var SkeletonRounded = (0, import_react6.forwardRef)((props, ref) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Skeleton, { ref, variant: "rounded", ...props })); var SkeletonCard = (0, import_react6.forwardRef)( ({ showAvatar = true, showTitle = true, showDescription = true, showActions = true, lines = 2, className = "", ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( "div", { ref, className: cn( "w-full p-4 bg-background border border-border-200 rounded-lg", className ), ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-start space-x-3", children: [ showAvatar && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SkeletonCircle, { width: 40, height: 40 }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 space-y-2", children: [ showTitle && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SkeletonText, { width: "60%", height: 20 }), showDescription && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SkeletonText, { lines, spacing: "small" }) ] }) ] }), showActions && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-end space-x-2 mt-4", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SkeletonRectangle, { width: 80, height: 32 }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SkeletonRectangle, { width: 80, height: 32 }) ] }) ] } ); } ); var SkeletonList