UNPKG

shinodalabs-ui

Version:

A collection of reusable React components styled with Tailwind CSS, including buttons, date pickers, modals, tabs, progress bars, forms, checkboxes, radio buttons, selects, inputs, textareas, dropdowns, tooltips, alerts, and badges.

1,202 lines (1,185 loc) 50.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { Button: () => Button_default, DatePicker: () => DatePicker_default, DateRange: () => DateRange_default, Dialog: () => Dialog_default, FilterCard: () => FilterCard_default, IconButton: () => IconButton_default, Input: () => Input_default, Modal: () => Modal_default, MonthDatePicker: () => MonthDatePicker_default, Pagination: () => Pagination_default, ProgressBar: () => ProgressBar_default, RadioGroup: () => RadioGroup_default, Select: () => Select_default, Table: () => Table_default, Tabs: () => Tabs_default, ToggleSwitch: () => ToggleSwitch_default }); module.exports = __toCommonJS(index_exports); // src/components/Button/index.tsx var import_lucide_react = require("lucide-react"); var import_clsx = __toESM(require("clsx")); var import_jsx_runtime = require("react/jsx-runtime"); var baseStyles = "flex items-center h-10 justify-center rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 transition duration-200 ease-in-out cursor-pointer"; var variants = { emerald: "bg-emerald-600 hover:bg-emerald-700 dark:bg-emerald-700 dark:hover:bg-emerald-600 text-white dark:text-white", blue: "bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-600 text-white dark:text-white", red: "bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-600 text-white dark:text-white", neutral: "border border-zinc-300 dark:border-zinc-700 bg-transparent text-zinc-900 dark:text-zinc-100 hover:bg-zinc-100 dark:hover:bg-zinc-800" }; var sizes = { sm: "px-3 py-1 text-sm", md: "px-4 py-2 text-base", lg: "px-5 py-3 text-lg" }; var Button = ({ variant = "emerald", size = "md", icon: Icon, loading = false, children, disabled = false, className, ...props }) => { const isDisabled = disabled || loading; const hasIcon = Icon || loading; const showGap = hasIcon && children; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( "button", { disabled: isDisabled, className: (0, import_clsx.default)( baseStyles, variants[variant], sizes[size], showGap && "gap-2", isDisabled && "opacity-50 cursor-not-allowed focus:ring-0", className ), ...props, children: [ loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader, { className: "h-4 w-4 animate-spin" }) : Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { className: "h-4 w-4" }), children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children }) ] } ); }; var Button_default = Button; // src/components/Input/index.tsx var import_react = __toESM(require("react")); var import_lucide_react2 = require("lucide-react"); var import_jsx_runtime2 = require("react/jsx-runtime"); var Input = import_react.default.forwardRef( ({ label, id, error, type = "text", onChange, value, headerRight, centerContent, maxLength, currency = "BRL", language = "pt-BR", ...props }, ref) => { const inputId = id || `input-${label.toLowerCase().replace(/\s+/g, "-")}`; const [moneyValue, setMoneyValue] = (0, import_react.useState)(""); const [showPassword, setShowPassword] = (0, import_react.useState)(false); const formatCurrency = (0, import_react.useCallback)( (num) => { if (isNaN(num) || num === null) return ""; return new Intl.NumberFormat(language, { style: "currency", currency }).format(num); }, [currency, language] ); const parseNumber = (str) => { if (!str) return 0; const onlyNumbers = str.replace(/[^\d,.-]/g, "").replace(",", "."); const parsed = parseFloat(onlyNumbers); return isNaN(parsed) ? 0 : parsed; }; (0, import_react.useEffect)(() => { if (type === "money") { if (typeof value === "number") { setMoneyValue(formatCurrency(value)); } else if (typeof value === "string") { const numeric = parseNumber(value); setMoneyValue(formatCurrency(numeric)); } } }, [value, type, formatCurrency]); const handleMoneyChange = (e) => { const rawValue = e.target.value; const onlyDigits = rawValue.replace(/\D/g, ""); const numericValue = parseFloat(onlyDigits) / 100; setMoneyValue(formatCurrency(numericValue)); if (onChange) { onChange(numericValue); } }; const renderLabel = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "div", { className: `flex mb-2 ${centerContent ? "flex-col items-center text-center gap-1" : "items-center justify-between"}`, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: inputId, className: "block text-sm font-medium", children: label }), headerRight && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: headerRight }) ] } ); const baseInputClass = `w-full px-3 py-2 border rounded-md bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 dark:focus:ring-emerald-600 ${error ? "border-red-500" : "border-zinc-300 dark:border-zinc-700"} ${centerContent ? "text-center" : ""}`; if (type === "money") { return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [ renderLabel(), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: inputId, ref, type: "text", value: moneyValue, onChange: handleMoneyChange, placeholder: formatCurrency(0), className: baseInputClass, ...props } ), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); } if (type === "password") { return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [ renderLabel(), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center border border-zinc-300 dark:border-zinc-700 rounded-md px-3 py-2 focus-within:ring-2 focus-within:ring-primary-500 bg-white dark:bg-zinc-900", children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: inputId, ref, type: showPassword ? "text" : "password", value: value ?? "", onChange, className: "flex-1 bg-transparent outline-none text-sm text-zinc-900 dark:text-zinc-100", ...props } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "ml-2 text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 transition-all", "aria-label": showPassword ? "Ocultar senha" : "Mostrar senha", "aria-live": "polite", tabIndex: -1, children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.EyeOff, { size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Eye, { size: 18 }) } ) ] }), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); } if (type === "number") { return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [ renderLabel(), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { id: inputId, ref, type: "text", value: value ?? "", maxLength, onChange: (e) => { let val = e.target.value; val = val.replace(/[^0-9]/g, ""); if (maxLength && val.length > maxLength) { val = val.slice(0, maxLength); } const syntheticEvent = { ...e, target: { ...e.target, value: val } }; if (onChange) { onChange(syntheticEvent); } }, className: baseInputClass, ...props } ), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); } return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [ renderLabel(), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { ref, id: inputId, type, value: value ?? "", onChange, className: baseInputClass, ...props } ), error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); } ); Input.displayName = "Input"; var Input_default = Input; // src/components/Select/index.tsx var import_react2 = __toESM(require("react")); var import_jsx_runtime3 = require("react/jsx-runtime"); var Select = import_react2.default.forwardRef( ({ label, options, id, error, ...props }, ref) => { const selectId = id || `select-${label?.toLowerCase().replace(/\s+/g, "-")}`; return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [ label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { htmlFor: selectId, className: "block text-sm font-medium mb-2", children: label }), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "select", { id: selectId, ref, ...props, className: `w-full px-3 py-2 border rounded-md bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-emerald-500 dark:focus:ring-emerald-600 cursor-pointer ${error ? "border-red-500" : "border-zinc-300 dark:border-zinc-700"}`, children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: option.value, children: option.label }, option.value)) } ), error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); } ); Select.displayName = "Select"; var Select_default = Select; // src/components/RadioGroup/index.tsx var import_react3 = require("react"); var import_jsx_runtime4 = require("react/jsx-runtime"); var RadioGroup = ({ name, label, value, options, onChange }) => { const groupId = (0, import_react3.useId)(); return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "space-y-3", children: [ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm font-medium", children: label }), options.map((option) => { const id = `${groupId}-${option.value}`; return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( "label", { htmlFor: id, className: "flex items-center space-x-2 cursor-pointer", children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( "input", { id, type: "radio", name, value: option.value, checked: value === option.value, onChange: () => onChange(option.value), className: "w-4 h-4 text-emerald-600 border-zinc-300 dark:border-zinc-700 focus:ring-emerald-500 dark:focus:ring-emerald-600" } ), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sm capitalize", children: option.label }) ] }, option.value ); }) ] }); }; var RadioGroup_default = RadioGroup; // src/components/ToggleSwitch/index.tsx var import_react4 = require("react"); var import_jsx_runtime5 = require("react/jsx-runtime"); var ToggleSwitch = ({ label, description, checked, onChange }) => { const id = (0, import_react4.useId)(); return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-0.5", children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: id, className: "text-sm font-medium", children: label }), description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: description }) ] }), /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)( "label", { htmlFor: id, className: "relative inline-flex items-center cursor-pointer", children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( "input", { id, type: "checkbox", checked, onChange: (e) => onChange(e.target.checked), className: "sr-only peer" } ), /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-11 h-6 bg-zinc-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-emerald-300 dark:peer-focus:ring-emerald-800 rounded-full peer dark:bg-zinc-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-zinc-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-zinc-600 peer-checked:bg-emerald-600" }) ] } ) ] }); }; var ToggleSwitch_default = ToggleSwitch; // src/components/DatePicker/index.tsx var import_react5 = require("react"); var import_react_dom = require("react-dom"); var import_react_day_picker = require("react-day-picker"); var import_date_fns = require("date-fns"); var import_locale = require("date-fns/locale"); var import_lucide_react3 = require("lucide-react"); var import_jsx_runtime6 = require("react/jsx-runtime"); var DatePicker = ({ field, error, label = "Data", placeholder = "Selecione a data" }) => { const [showCalendar, setShowCalendar] = (0, import_react5.useState)(false); const [selectedDate, setSelectedDate] = (0, import_react5.useState)(); const id = (0, import_react5.useId)(); const containerRef = (0, import_react5.useRef)(null); const calendarRef = (0, import_react5.useRef)(null); const inputRef = (0, import_react5.useRef)(null); const [position, setPosition] = (0, import_react5.useState)({ top: 0, left: 0 }); (0, import_react5.useEffect)(() => { if (!field.value) { setSelectedDate(void 0); return; } try { const [year, month, day] = field.value.split("T")[0].split("-").map(Number); const localDate = new Date(year, month - 1, day); if ((0, import_date_fns.isValid)(localDate)) { setSelectedDate(localDate); } else { setSelectedDate(void 0); } } catch { setSelectedDate(void 0); } }, [field.value]); (0, import_react5.useEffect)(() => { const updatePosition = () => { if (!inputRef.current || !calendarRef.current) return; const inputRect = inputRef.current.getBoundingClientRect(); const calendarHeight = calendarRef.current.offsetHeight; const spaceBelow = window.innerHeight - inputRect.bottom; const spaceAbove = inputRect.top; let top; if (spaceBelow > calendarHeight + 20) { top = inputRect.bottom + window.scrollY + 8; } else if (spaceAbove > calendarHeight + 20) { top = inputRect.top + window.scrollY - calendarHeight - 8; } else { top = window.scrollY + 20; } setPosition({ top, left: inputRect.left + window.scrollX }); }; if (showCalendar) { updatePosition(); window.addEventListener("resize", updatePosition); window.addEventListener("scroll", updatePosition); return () => { window.removeEventListener("resize", updatePosition); window.removeEventListener("scroll", updatePosition); }; } }, [showCalendar]); (0, import_react5.useEffect)(() => { function handleClickOutside(event) { if (containerRef.current && !containerRef.current.contains(event.target) && calendarRef.current && !calendarRef.current.contains(event.target)) { setShowCalendar(false); } } if (showCalendar) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [showCalendar]); const calendar = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)( "div", { ref: calendarRef, className: "fixed z-[99999] bg-white dark:bg-zinc-900 rounded-lg border border-zinc-200 dark:border-zinc-700 shadow-lg p-4 max-w-[320px] w-[min(320px,calc(100vw-40px))] pointer-events-auto", style: { top: position.top, left: position.left }, children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-medium text-sm text-zinc-700 dark:text-zinc-200", children: "Selecione uma data" }), /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-2 items-center", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "button", { type: "button", className: "text-xs text-emerald-500 cursor-pointer", onClick: () => { field.onChange(""); setShowCalendar(false); }, children: "Limpar" } ), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "button", { type: "button", className: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200 text-sm cursor-pointer p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full", onClick: () => setShowCalendar(false), "aria-label": "Fechar calend\xE1rio", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.XIcon, { className: "w-5 h-5" }) } ) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( import_react_day_picker.DayPicker, { mode: "single", navLayout: "around", selected: selectedDate, onSelect: (date) => { if (date) { const localDate = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 12, 0, 0 ); const yyyy = localDate.getFullYear(); const mm = String(localDate.getMonth() + 1).padStart(2, "0"); const dd = String(localDate.getDate()).padStart(2, "0"); const formattedDate = `${yyyy}-${mm}-${dd}`; field.onChange(formattedDate); } else { field.onChange(""); } setShowCalendar(false); }, locale: import_locale.ptBR, formatters: { formatCaption: (date, options) => { const formatted = (0, import_date_fns.format)(date, "MMMM yyyy", { locale: options?.locale }); return formatted.charAt(0).toUpperCase() + formatted.slice(1); } }, classNames: { selected: "bg-emerald-500 text-white rounded-full", today: "text-emerald-100 dark:text-emerald-800" } } ) ] } ); return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: containerRef, className: "relative", children: [ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "label", { htmlFor: id, className: "block text-sm font-medium mb-2 text-zinc-700 dark:text-zinc-300", children: label } ), /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "input", { ref: inputRef, id, type: "text", readOnly: true, className: `w-full px-3 py-2 border rounded-md bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 cursor-pointer focus:outline-none focus:ring-2 focus:ring-emerald-500 dark:focus:ring-emerald-600 ${error ? "border-red-500" : "border-zinc-300 dark:border-zinc-700"}`, onClick: () => setShowCalendar((v) => !v), value: selectedDate ? (0, import_date_fns.format)(selectedDate, "dd/MM/yyyy") : "", placeholder } ), showCalendar && typeof window !== "undefined" ? (0, import_react_dom.createPortal)(calendar, document.body) : null, error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-red-500 mt-2", children: error }) ] }); }; var DatePicker_default = DatePicker; // src/components/MonthDatePicker/index.tsx var import_lucide_react4 = require("lucide-react"); var import_react6 = require("react"); var import_jsx_runtime7 = require("react/jsx-runtime"); var monthNames = [ "Janeiro", "Fevereiro", "Mar\xE7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro" ]; var MonthDatePicker = ({ selectedDate, onChange, minDate, maxDate, isOpen, onClose }) => { const [year, setYear] = (0, import_react6.useState)( selectedDate?.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear() ); const modalRef = (0, import_react6.useRef)(null); (0, import_react6.useEffect)(() => { function handleClickOutside(event) { if (modalRef.current && !modalRef.current.contains(event.target)) { onClose(); } } if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [isOpen, onClose]); (0, import_react6.useEffect)(() => { if (selectedDate) { setYear(selectedDate.getFullYear()); } }, [selectedDate]); const changeYear = (delta) => { setYear((prev) => prev + delta); }; const isDisabled = (year2, month) => { const date = new Date(year2, month, 1); if (minDate && date < new Date(minDate.getFullYear(), minDate.getMonth(), 1)) return true; if (maxDate && date > new Date(maxDate.getFullYear(), maxDate.getMonth(), 1)) return true; return false; }; if (!isOpen) return null; return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "fixed inset-0 z-40 bg-black/20 backdrop-blur-sm" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( "div", { ref: modalRef, className: "fixed z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\n bg-white dark:bg-zinc-900 rounded-lg border border-zinc-300 dark:border-zinc-700\n shadow-lg p-6 w-[320px] max-w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex justify-end mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { type: "button", className: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200 p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-full", onClick: onClose, "aria-label": "Fechar calend\xE1rio", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.XIcon, { className: "w-5 h-5" }) } ) }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex justify-between items-center mb-4", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { onClick: () => changeYear(-1), className: "px-2 py-1 rounded hover:bg-zinc-100 dark:hover:bg-zinc-800", "aria-label": "Ano anterior", type: "button", children: "<" } ), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-semibold text-lg text-zinc-900 dark:text-zinc-100", children: year }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { onClick: () => changeYear(1), className: "px-2 py-1 rounded hover:bg-zinc-100 dark:hover:bg-zinc-800", "aria-label": "Ano seguinte", type: "button", children: ">" } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "grid grid-cols-3 gap-2", children: monthNames.map((monthName, index) => { const disabled = isDisabled(year, index); const isSelected = selectedDate?.getFullYear() === year && selectedDate?.getMonth() === index; return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { disabled, onClick: () => { onChange(new Date(year, index, 1)); onClose(); }, className: `py-2 rounded text-sm font-medium ${disabled ? "text-zinc-400 cursor-not-allowed" : isSelected ? "bg-emerald-500 text-white" : "hover:bg-emerald-100 dark:hover:bg-emerald-700"}`, type: "button", children: monthName }, monthName ); }) }) ] } ) ] }); }; var MonthDatePicker_default = MonthDatePicker; // src/components/Modal/index.tsx var import_jsx_runtime8 = require("react/jsx-runtime"); var Modal = ({ isOpen, title, description, children, onClose, onConfirm, isLoading = false, confirmLabel = "Salvar", cancelLabel = "Cancelar" }) => { if (!isOpen) return null; const handleBackdropClick = (e) => { if (e.target === e.currentTarget) { onClose(); } }; return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)( "div", { className: "fixed inset-0 z-40 bg-black/60 animate-in fade-in duration-200 flex items-center justify-center p-4 sm:p-6", onClick: handleBackdropClick, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)( "div", { className: "\n bg-white dark:bg-zinc-950\n border border-zinc-200 dark:border-zinc-800\n rounded-lg\n flex flex-col\n max-h-[90vh]\n overflow-visible\n z-50\n relative\n w-auto max-w-[90vw]\n ", children: [ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-6 overflow-y-auto max-h-[calc(90vh - 96px)]", children: [ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "text-lg font-semibold mb-2", children: title }), /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "w-full max-w-full", children: [ description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-zinc-500 dark:text-zinc-400 mb-4", children: description }), children ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-6 border-t border-zinc-200 dark:border-zinc-800 flex gap-2 justify-end", children: [ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button_default, { disabled: isLoading, onClick: onClose, variant: "neutral", children: cancelLabel }), onConfirm && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button_default, { disabled: isLoading, onClick: onConfirm, children: confirmLabel }) ] }) ] } ) } ); }; var Modal_default = Modal; // src/components/Tabs/index.tsx var import_jsx_runtime9 = require("react/jsx-runtime"); var Tabs = ({ tabs, selectedValue, onChange, disabled = false }) => { return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex border-b border-zinc-200 dark:border-zinc-800", children: tabs.map((tab, index) => { const isActive = selectedValue === tab.value; const isFirst = index === 0; const isLast = index === tabs.length - 1; return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)( "button", { onClick: () => !disabled && onChange(tab.value), disabled, className: ` px-4 py-2 text-sm font-medium transition-all ${disabled ? "cursor-not-allowed text-zinc-400 dark:text-zinc-600" : "cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800"} ${isActive && !disabled ? "border-b-2 border-emerald-600 dark:border-emerald-500 text-emerald-600 dark:text-emerald-500" : !disabled ? "text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-300" : ""} ${isFirst ? "rounded-tl-md" : ""} ${isLast ? "rounded-tr-md" : ""} `, children: tab.label }, tab.value ); }) }); }; var Tabs_default = Tabs; // src/components/ProgressBar/index.tsx var import_jsx_runtime10 = require("react/jsx-runtime"); var ProgressBar = ({ percentage }) => { const isWarning = percentage >= 75 && percentage < 90; const isDanger = percentage >= 90; const progressBarBg = isDanger ? "bg-red-200 dark:bg-red-900" : isWarning ? "bg-amber-200 dark:bg-amber-900" : "bg-zinc-200 dark:bg-zinc-700"; const progressFillColor = isDanger ? "bg-red-500" : isWarning ? "bg-amber-500" : "bg-emerald-500"; const percentageTextColor = isDanger ? "text-red-500" : isWarning ? "text-amber-500" : "text-zinc-500 dark:text-zinc-400"; return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `w-full rounded-full h-2 ${progressBarBg}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)( "div", { className: `h-2 rounded-full transition-all duration-300 ${progressFillColor}`, style: { width: `${Math.min(percentage, 100)}%` } } ) }), /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: `text-right text-sm mt-1 ${percentageTextColor}`, children: [ percentage, "%" ] }) ] }); }; var ProgressBar_default = ProgressBar; // src/components/IconButton/index.tsx var import_jsx_runtime11 = require("react/jsx-runtime"); var IconButton = ({ icon: Icon, name, onClick, disabled }) => { return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)( "button", { onClick, disabled, className: "p-2 rounded-md cursor-pointer hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100 transition-all duration-200 ease-in-out disabled:opacity-50 disabled:cursor-not-allowed", children: [ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { className: "h-5 w-5" }), /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "sr-only", children: name }) ] } ); }; var IconButton_default = IconButton; // src/components/Dialog/index.tsx var import_jsx_runtime12 = require("react/jsx-runtime"); var Dialog = ({ isOpen, title, description, confirmLabel = "Confirmar", cancelLabel = "Cancelar", onClose, onConfirm, isLoading = false }) => { if (!isOpen) return null; return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "fixed inset-0 z-50 bg-black/60 backdrop-blur-sm animate-in fade-in duration-200 flex items-center justify-center p-4 sm:p-6", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)( "div", { className: "\n w-full h-full sm:h-auto sm:max-w-sm\n bg-white dark:bg-zinc-950\n border border-zinc-200 dark:border-zinc-800\n sm:rounded-lg rounded-none\n flex flex-col justify-between\n ", children: [ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-6 overflow-y-auto", children: [ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "text-lg font-semibold mb-2", children: title }), description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-sm text-zinc-600 dark:text-zinc-400", children: description }) ] }), /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-6 border-t border-zinc-200 dark:border-zinc-800 flex gap-2 justify-end", children: [ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button_default, { onClick: onClose, disabled: isLoading, variant: "neutral", children: cancelLabel }), /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Button_default, { onClick: onConfirm, loading: isLoading, children: confirmLabel }) ] }) ] } ) }); }; var Dialog_default = Dialog; // src/components/Pagination/index.tsx var import_react7 = require("react"); var import_lucide_react5 = require("lucide-react"); var import_jsx_runtime13 = require("react/jsx-runtime"); var Pagination = ({ currentPage, totalPages, totalItems, onPageChange, perPage, onItemsPerPageChange, optionsItemsPerPage, labels }) => { const [itemsPerPage, setItemsPerPage] = (0, import_react7.useState)(perPage || 10); (0, import_react7.useEffect)(() => { onItemsPerPageChange(itemsPerPage); }, [itemsPerPage, onItemsPerPageChange, onPageChange]); const handlePrevious = () => { if (currentPage > 1) onPageChange(currentPage - 1); }; const handleNext = () => { if (currentPage < totalPages) onPageChange(currentPage + 1); }; return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "rounded-lg border bg-white dark:bg-zinc-950 shadow-sm flex flex-row sm:flex-col items-center justify-between p-6 border-zinc-200 dark:border-zinc-800 text-sm text-zinc-600 dark:text-zinc-400 gap-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "hidden sm:inline", children: [ labels.showing, " ", (currentPage - 1) * itemsPerPage + 1, " -", " ", Math.min(currentPage * itemsPerPage, totalItems), " ", labels.of, " ", totalItems, " ", labels.results ] }), /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-row sm:flex-col items-center justify-center gap-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( Select_default, { id: "itemsPerPageSelect", className: "flex-1", value: itemsPerPage, onChange: (e) => setItemsPerPage(Number(e.target.value)), "aria-label": labels.itemsPerPage, options: optionsItemsPerPage } ), /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-row items-center gap-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( IconButton_default, { onClick: handlePrevious, disabled: currentPage === 1, icon: import_lucide_react5.ChevronLeft, name: labels.previous, "aria-label": labels.previous } ), /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "px-2", children: [ labels.page, " ", currentPage, " ", labels.of, " ", totalPages ] }), /* @__PURE__ */ (0, import_jsx_runtime13.jsx)( IconButton_default, { onClick: handleNext, disabled: currentPage === totalPages, icon: import_lucide_react5.ChevronRight, name: labels.next, "aria-label": labels.next } ) ] }) ] }) ] }); }; var Pagination_default = Pagination; // src/components/DateRange/index.tsx var import_react8 = require("react"); var import_lucide_react6 = require("lucide-react"); var import_date_fns2 = require("date-fns"); var import_locale2 = require("date-fns/locale"); var import_react_day_picker2 = require("react-day-picker"); var import_jsx_runtime14 = require("react/jsx-runtime"); var DateRange = ({ selectedRange, onChange, labels = {} }) => { const [showCalendar, setShowCalendar] = (0, import_react8.useState)(false); const buttonRef = (0, import_react8.useRef)(null); const [position, setPosition] = (0, import_react8.useState)({ top: 0, left: 0, transform: "" }); const updatePosition = (0, import_react8.useCallback)(() => { if (buttonRef.current) { const rect = buttonRef.current.getBoundingClientRect(); const isMobile = window.innerWidth < 640; setPosition({ top: rect.bottom + 8, left: isMobile ? window.innerWidth / 2 : rect.left, transform: isMobile ? "translateX(-50%)" : "none" }); } }, []); (0, import_react8.useEffect)(() => { if (showCalendar) { updatePosition(); window.addEventListener("resize", updatePosition); return () => window.removeEventListener("resize", updatePosition); } }, [showCalendar, updatePosition]); const formatRange = (from, to) => { if (!from) return ""; if (!to) return (0, import_date_fns2.format)(from, "dd/MM/yyyy"); return `${(0, import_date_fns2.format)(from, "dd/MM/yyyy")} a ${(0, import_date_fns2.format)(to, "dd/MM/yyyy")}`; }; return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)( "button", { ref: buttonRef, className: `p-3 rounded-md border flex items-center gap-2 transition-colors cursor-pointer ${selectedRange?.from ? "bg-emerald-100 dark:bg-emerald-900 text-emerald-600 dark:text-emerald-400" : "border-zinc-300 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-zinc-800"}`, "aria-label": labels.filterByDate || "Filtrar por data", title: labels.filterByDate || "Filtrar por data", onClick: () => setShowCalendar(true), children: [ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.Calendar, { className: "h-4 w-4" }), selectedRange?.from && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm", children: formatRange(selectedRange.from, selectedRange.to) }) ] } ), showCalendar && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)( "div", { className: "absolute z-50 p-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 shadow-lg rounded-lg", style: { top: position.top, left: position.left, transform: position.transform }, children: [ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between items-center w-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "font-medium text-sm text-zinc-700 dark:text-zinc-200", children: labels.selectDate || "Selecione uma data" }), /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-2 items-center", children: [ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)( "button", { onClick: () => { onChange({ from: void 0, to: void 0 }); setShowCalendar(false); }, className: "text-xs text-emerald-500 cursor-pointer", children: labels.clear || "Limpar" } ), /* @__PURE__ */ (0, import_jsx_runtime14.jsx)( "button", { onClick: () => setShowCalendar(false), className: "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200 p-2 rounded-full hover:bg-zinc-100 dark:hover:bg-zinc-800", "aria-label": labels.closeCalendar || "Fechar calend\xE1rio", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.XIcon, { className: "w-4 h-4" }) } ) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime14.jsx)( import_react_day_picker2.DayPicker, { mode: "range", selected: selectedRange, onSelect: (range) => { if (range) { onChange({ from: range.from ?? void 0, to: range.to ?? void 0 }); } }, locale: import_locale2.ptBR, formatters: { formatCaption: (date, options) => { const formatted = (0, import_date_fns2.format)(date, "MMMM yyyy", { locale: options?.locale }); return formatted.charAt(0).toUpperCase() + formatted.slice(1); } }, classNames: { selected: "bg-emerald-500 text-white rounded-full", today: "text-emerald-100 dark:text-white rounded-full bg-emerald-500", range_start: "bg-emerald-200 dark:bg-emerald-800 rounded-full", range_end: "bg-emerald-200 dark:bg-emerald-800 rounded-full", range_middle: "bg-emerald-200 dark:bg-emerald-200 rounded-full text-emerald-800 dark:text-emerald-800" } } ) ] } ) ] }); }; var DateRange_default = DateRange; // src/components/FilterCard/index.tsx var import_react9 = require("react"); var import_lucide_react7 = require("lucide-react"); var import_jsx_runtime15 = require("react/jsx-runtime"); var FilterCard = ({ children, title = "Filtros" }) => { const [isOpen, setIsOpen] = (0, import_react9.useState)(true); const [height, setHeight] = (0, import_react9.useState)(0); const contentRef = (0, import_react9.useRef)(null); (0, import_react9.useEffect)(() => { if (isOpen && contentRef.current) { setHeight(contentRef.current.scrollHeight); const timeout = setTimeout(() => setHeight("auto"), 300); return () => clearTimeout(timeout); } else if (!isOpen) { setHeight(contentRef.current?.scrollHeight || 0); requestAnimationFrame(() => { setHeight(0); }); } }, [isOpen]); return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 shadow-sm", children: [ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "p-6 flex items-center justify-between", children: [ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: "text-lg font-semibold", children: title }), /* @__PURE__ */ (0, import_jsx_runtime15.jsx)( IconButton_default, { icon: isOpen ? import_lucide_react7.ChevronUp : import_lucide_react7.ChevronDown, onClick: () => setIsOpen((prev) => !prev), "aria-label": isOpen ? "Ocultar filtros" : "Mostrar filtros", name: "toggle-filters" } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime15.jsx)( "div", { ref: contentRef, style: { height: height === "auto" ? "auto" : `${height}px`, overflow: "hidden", transition: "height 300ms ease" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "px-6 py-6", children }) } ) ] }); }; var FilterCard_default = FilterCard; // src/components/Table/index.tsx var import_lucide_react8 = require("lucide-react"); var import_jsx_runtime16 = require("react/jsx-runtime"); var TableRoot = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rounded-lg border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-950 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "p-6", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("table", { className: "w-full", children }) }) }) }); var TableHeader = ({ columns }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tr", { className: "border-b border-zinc-200 dark:border-zinc-800", children: columns.map( ({ key, label, align, hidden, showMobile }) => hidden ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( "th", { className: `py-3 px-4 font-medium text-zinc-500 dark:text-zinc-400 ${align === "right" ? "text-right" : "text-left"} ${showMobile === true ? "table-cell sm:hidden" : showMobile === false ? "hidden sm:table-cell" : "table-cell"}`, children: label }, String(key) ) ) }) }); var TableBody = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tbody", { children }); function TableRow({ data, columns, onClickEdit, onClickDelete }) { return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("tr", { className: "border-b border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-900", children: columns.map( ({ key, align, hidden, render, format: format3, style, showMobile }) => { if (hidden) return null; const visibilityClass = showMobile === true ? "table-cell sm:hidden" : showMobile === false ? "hidden sm:table-cell" : "table-cell"; if (key === "actions") { return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( "td", { className: `py-3 px-4 ${align === "right" ? "text-right" : "text-left"} ${visibilityClass}`, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex justify-end gap-2", children: [ onClickEdit && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( "button", { className: "p-1 rounded-md text-zinc-500 hover:text-emerald-500 dark:text-zinc-400 dark:hover:text-emerald-500 transition-all", "aria-label": "Editar", title: "Editar", onClick: onClickEdit, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react8.Edit, { className: "w-4 h-4" }) } ), onClickDelete && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( "button", { className: "p-1 rounded-md text-zinc-500 hover:text-red-500 dark:text-zinc-400 dark:hover:text-red-400 transition-all", "aria-label": "Excluir", title: "Excluir", onClick: onClickDelete, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react8.Trash, { className: "w-4 h-4" }) } ) ] }) }, "actions" ); } const value = data[key]; const content = render ? render(value, data) : format3 ? format3(value, data) : String(value); const cellStyle = style ? style(value, data) : ""; return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)( "td", { className: `py-3 px-4 ${align === "right" ? "text-right" : "text-left"} ${cellStyle} ${visibilityClass}`, children: content }, String(key) ); } ) }); } var Table = Object.assign(TableRoot, { Header: TableHeader, Body: TableBody, Row: TableRow }); var Table_default = Table; // Annotate the CommonJS export names for ESM import in node: 0 && (modul