UNPKG

analytica-frontend-lib

Version:

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

291 lines (287 loc) 13.2 kB
// src/components/Modal/Modal.tsx import { useEffect, useId } from "react"; import { X } 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/Button/Button.tsx import { jsx, jsxs } from "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__ */ jsxs( "button", { className: cn(baseClasses, variantClasses, sizeClasses, className), disabled, type, ...props, children: [ iconLeft && /* @__PURE__ */ jsx("span", { className: "mr-2 flex items-center", children: iconLeft }), children, iconRight && /* @__PURE__ */ jsx("span", { className: "ml-2 flex items-center", children: iconRight }) ] } ); }; var Button_default = Button; // 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 import { jsx as jsx2, jsxs as jsxs2 } from "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 }) => { const titleId = useId(); 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]); useEffect(() => { const originalOverflow = document.body.style.overflow; if (isOpen) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = originalOverflow; } return () => { document.body.style.overflow = originalOverflow; }; }, [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__ */ jsx2("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__ */ jsxs2( "dialog", { className: modalClasses, "aria-labelledby": titleId, "aria-modal": "true", open: true, children: [ /* @__PURE__ */ jsx2("div", { className: "flex justify-end p-6 pb-0", children: !hideCloseButton && /* @__PURE__ */ jsx2( "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__ */ jsx2(X, { size: 18 }) } ) }), /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center px-6 pb-6 gap-5", children: [ image && /* @__PURE__ */ jsx2("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx2( "img", { src: image, alt: imageAlt ?? "", className: "w-[122px] h-[122px] object-contain" } ) }), /* @__PURE__ */ jsx2( "h2", { id: titleId, className: "text-lg font-semibold text-text-950 text-center", children: title } ), description && /* @__PURE__ */ jsx2("p", { className: "text-sm font-normal text-text-400 text-center max-w-md leading-[21px]", children: description }), actionLink && /* @__PURE__ */ jsxs2("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__ */ jsx2( Button_default, { variant: "solid", action: "primary", size: "large", className: "w-full", onClick: handleActionClick, children: actionLabel || "Iniciar Atividade" } ); } return /* @__PURE__ */ jsx2( "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__ */ jsx2( Button_default, { variant: "solid", action: "primary", size: "large", className: "w-full", onClick: handleActionClick, children: actionLabel || "Iniciar Atividade" } ) ] }) ] }) ] } ) }); } return /* @__PURE__ */ jsx2("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__ */ jsxs2( "dialog", { className: modalClasses, "aria-labelledby": titleId, "aria-modal": "true", open: true, children: [ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between px-6 py-6", children: [ /* @__PURE__ */ jsx2("h2", { id: titleId, className: "text-lg font-semibold text-text-950", children: title }), !hideCloseButton && /* @__PURE__ */ jsx2( "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__ */ jsx2(X, { size: 18 }) } ) ] }), children && /* @__PURE__ */ jsx2("div", { className: "px-6 pb-6", children: /* @__PURE__ */ jsx2("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }), footer && /* @__PURE__ */ jsx2("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer }) ] } ) }); }; var Modal_default = Modal; export { Modal_default as default }; //# sourceMappingURL=index.mjs.map