@rendra-ui/web 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +15 -14
  2. package/dist/base.css +1 -1
  3. package/dist/{empty-state-DrQjqxvp.js → brand-feedback-icon-CVux4pNh.js} +4 -64
  4. package/dist/brand-feedback-icon-CVux4pNh.js.map +1 -0
  5. package/dist/{button-B6-644xP.js → button-CrSlVIsY.js} +6 -6
  6. package/dist/{button-B6-644xP.js.map → button-CrSlVIsY.js.map} +1 -1
  7. package/dist/chart.js +7 -7
  8. package/dist/cli/auditar.js +1 -1
  9. package/dist/cli/codigos.js +2 -2
  10. package/dist/cli/executar-auditar.js +1 -1
  11. package/dist/cli/help.js +1 -1
  12. package/dist/cli/trocar.js +2 -2
  13. package/dist/cli/typescript-loader.js +1 -1
  14. package/dist/{cn-TBlI6-gS.js → cn-BlJHpkDs.js} +2 -2
  15. package/dist/{cn-TBlI6-gS.js.map → cn-BlJHpkDs.js.map} +1 -1
  16. package/dist/{components-Btk1cUg2.js → components-6RpoJ2Od.js} +20 -4
  17. package/dist/{components-Btk1cUg2.js.map → components-6RpoJ2Od.js.map} +1 -1
  18. package/dist/components.css +108 -1
  19. package/dist/document-viewer.js +4 -4
  20. package/dist/{tooltip-kgsorOg6.js → dropdown-menu-BUGKtR6k.js} +9 -75
  21. package/dist/dropdown-menu-BUGKtR6k.js.map +1 -0
  22. package/dist/empty-state-i4e-LacG.js +67 -0
  23. package/dist/empty-state-i4e-LacG.js.map +1 -0
  24. package/dist/{features-max-CC6CorTg.js → features-max-BCIy1Ic-.js} +1 -1
  25. package/dist/flow.js +1485 -0
  26. package/dist/flow.js.map +1 -0
  27. package/dist/index.js +816 -2019
  28. package/dist/index.js.map +1 -1
  29. package/dist/{motion-context-BL545cb6.js → motion-context-PJPj6X4g.js} +4 -4
  30. package/dist/{motion-context-BL545cb6.js.map → motion-context-PJPj6X4g.js.map} +1 -1
  31. package/dist/{motion-on-DMoTsWrG.js → motion-on-Bocf4Smc.js} +2 -2
  32. package/dist/{motion-on-DMoTsWrG.js.map → motion-on-Bocf4Smc.js.map} +1 -1
  33. package/dist/motion.js +2 -2
  34. package/dist/{rendra-provider-CeCkPafT.js → rendra-provider-Do_qWjJC.js} +3 -3
  35. package/dist/{rendra-provider-CeCkPafT.js.map → rendra-provider-Do_qWjJC.js.map} +1 -1
  36. package/dist/rich-text-editor.js +4 -3
  37. package/dist/rich-text-editor.js.map +1 -1
  38. package/dist/router-bridge.js +2 -2
  39. package/dist/timeline-xrMHeV92.js +1803 -0
  40. package/dist/timeline-xrMHeV92.js.map +1 -0
  41. package/dist/tokens.css +9 -1
  42. package/dist/tooltip-to0h6aPe.js +76 -0
  43. package/dist/tooltip-to0h6aPe.js.map +1 -0
  44. package/dist/types/components/ui/data-toolbar.d.ts +6 -1
  45. package/dist/types/components/ui/flow-editor.d.ts +11 -0
  46. package/dist/types/components/ui/flow-viewer.d.ts +58 -0
  47. package/dist/types/components/ui/kanban.d.ts +75 -6
  48. package/dist/types/flow.d.ts +3 -0
  49. package/dist/types/lib/flow.d.ts +121 -0
  50. package/dist/{use-breakpoint-CNxDwEpc.js → use-breakpoint-CM-yeDHJ.js} +2 -2
  51. package/dist/{use-breakpoint-CNxDwEpc.js.map → use-breakpoint-CM-yeDHJ.js.map} +1 -1
  52. package/dist/{use-motion-presets-DCJPBfNJ.js → use-motion-presets-I4StrsCi.js} +3 -3
  53. package/dist/{use-motion-presets-DCJPBfNJ.js.map → use-motion-presets-I4StrsCi.js.map} +1 -1
  54. package/dist/widget-grid.js +2 -2
  55. package/package.json +8 -1
  56. package/dist/empty-state-DrQjqxvp.js.map +0 -1
  57. package/dist/tooltip-kgsorOg6.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,22 +1,23 @@
1
- /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-TBlI6-gS.js";
3
- import { a as useMotionMode, c as getReducedMotion, i as useMotionLib, l as subscribeReducedMotion, n as useLayoutReady, o as useMotionOn, r as useMotionAdopted, u as usePrefersReducedMotion } from "./motion-context-BL545cb6.js";
4
- import { t as useMotionPresets } from "./use-motion-presets-DCJPBfNJ.js";
5
- import { a as catalogByComponent, c as findComponentsWithoutDefault, d as findPresetCollisions, f as getCatalogEntry, i as assertCatalogIntegrity, l as findDuplicateCodes, n as CATALOG_EXCLUDED_FILES, o as filesMissingCatalogEntry, p as resolveCatalogCode, r as COMPONENT_CODE_PATTERN, s as findComponentsWithMultipleDefaults, t as CATALOG, u as findInvalidFormatCodes } from "./components-Btk1cUg2.js";
6
- import { n as buttonVariants, r as Spinner, t as Button } from "./button-B6-644xP.js";
7
- import { a as DropdownMenuContent, c as DropdownMenuLabel, d as DropdownMenuSeparator, f as DropdownMenuSub, h as DropdownMenuTrigger, i as DropdownMenuCheckboxItem, l as DropdownMenuRadioGroup, m as DropdownMenuSubTrigger, n as TooltipProvider, o as DropdownMenuGroup, p as DropdownMenuSubContent, r as DropdownMenu, s as DropdownMenuItem, t as Tooltip, u as DropdownMenuRadioItem } from "./tooltip-kgsorOg6.js";
8
- import { a as BrandContext, i as useBrand, n as BrandFeedbackIcon, r as feedbackLabels, t as EmptyState } from "./empty-state-DrQjqxvp.js";
9
- import { a as useRendraNavigate, i as useRendraLink, n as useCurrentPath, r as useRendraBreadcrumbs, t as RendraProvider } from "./rendra-provider-CeCkPafT.js";
10
- import { n as useBreakpoint, t as breakpoints } from "./use-breakpoint-CNxDwEpc.js";
11
- import { ArrowDown, ArrowDownRight, ArrowLeft, ArrowRight, ArrowRightLeft, ArrowUp, ArrowUpRight, Bell, Bot, CalendarDays, Check, CheckCheck, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, Clock, Columns3, Download, EllipsisVertical, Eye, EyeOff, FileText, Film, Filter, Globe, GripVertical, Home, ImageIcon, ImagePlus, Info, LayoutTemplate, LogOut, Mail, MapPin, Menu, MessageCircle, MessagesSquare, Mic, Minus, Monitor, Moon, MoreHorizontal, Palette, PanelLeft, Paperclip, Pause, Pencil, Phone, Play, Plus, Reply, RotateCw, Search, Send, SkipForward, Smartphone, Smile, Star, Sun, Trash2, TriangleAlert, UploadCloud, User, VideoIcon, X, XCircle, Zap } from "lucide-react";
12
- import { Accordion as Accordion$1, Avatar as Avatar$1, Checkbox as Checkbox$1, Collapsible, ContextMenu, Dialog, Label as Label$1, NavigationMenu, Popover as Popover$1, Progress as Progress$1, RadioGroup as RadioGroup$1, Slider as Slider$1, Slot, Switch as Switch$1, Tabs as Tabs$1, VisuallyHidden } from "radix-ui";
13
- import { Fragment, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
1
+ /*! Rendra Design System v2.8.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-BlJHpkDs.js";
3
+ import { a as useMotionMode, c as getReducedMotion, i as useMotionLib, l as subscribeReducedMotion, n as useLayoutReady, o as useMotionOn, r as useMotionAdopted, u as usePrefersReducedMotion } from "./motion-context-PJPj6X4g.js";
4
+ import { t as useMotionPresets } from "./use-motion-presets-I4StrsCi.js";
5
+ import { A as lookupCnpj, B as ActionBar, C as toE164, D as controlInput, E as controlFrame, F as CardHeader, H as useShell, I as CardTitle, L as InfoHint, M as CardContent, N as CardDescription, O as useLookup, P as CardFooter, R as Modal, S as toCents, T as controlAdornmentSpace, V as ShellContext, _ as internationalPhoneMask, a as Label, b as percentMask, c as fieldGridClass, d as Drawer, f as PickerPanel, g as formatCurrency, h as formatCents, i as Field, j as Card, k as lookupCep, l as gapClass, m as DEFAULT_DDI, n as useFillHeight, o as useFieldControl, p as Input, r as Select, s as alignClass, t as Timeline, u as justifyClass, v as masks, w as controlAdornmentButton, x as phoneCountries, y as parseLocaleNumber, z as Alert } from "./timeline-xrMHeV92.js";
6
+ import { a as catalogByComponent, c as findComponentsWithoutDefault, d as findPresetCollisions, f as getCatalogEntry, i as assertCatalogIntegrity, l as findDuplicateCodes, n as CATALOG_EXCLUDED_FILES, o as filesMissingCatalogEntry, p as resolveCatalogCode, r as COMPONENT_CODE_PATTERN, s as findComponentsWithMultipleDefaults, t as CATALOG, u as findInvalidFormatCodes } from "./components-6RpoJ2Od.js";
7
+ import { n as buttonVariants, r as Spinner, t as Button } from "./button-CrSlVIsY.js";
8
+ import { a as DropdownMenuItem, c as DropdownMenuRadioItem, d as DropdownMenuSubContent, f as DropdownMenuSubTrigger, i as DropdownMenuGroup, l as DropdownMenuSeparator, n as DropdownMenuCheckboxItem, o as DropdownMenuLabel, p as DropdownMenuTrigger, r as DropdownMenuContent, s as DropdownMenuRadioGroup, t as DropdownMenu, u as DropdownMenuSub } from "./dropdown-menu-BUGKtR6k.js";
9
+ import { i as BrandContext, n as feedbackLabels, r as useBrand, t as BrandFeedbackIcon } from "./brand-feedback-icon-CVux4pNh.js";
10
+ import { a as useRendraNavigate, i as useRendraLink, n as useCurrentPath, r as useRendraBreadcrumbs, t as RendraProvider } from "./rendra-provider-Do_qWjJC.js";
11
+ import { n as useBreakpoint, t as breakpoints } from "./use-breakpoint-CM-yeDHJ.js";
12
+ import { t as EmptyState } from "./empty-state-i4e-LacG.js";
13
+ import { n as TooltipProvider, t as Tooltip } from "./tooltip-to0h6aPe.js";
14
+ import { ArrowDown, ArrowDownRight, ArrowLeft, ArrowRight, ArrowRightLeft, ArrowUp, ArrowUpRight, Bell, Bot, CalendarClock, CalendarDays, CalendarX2, Check, CheckCheck, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, Clock, Columns3, Download, EllipsisVertical, FileText, Film, Filter, Globe, GripVertical, Home, ImageIcon, ImagePlus, LayoutTemplate, LogOut, Mail, MapPin, Menu, MessageCircle, MessagesSquare, Mic, Minus, Monitor, Moon, MoreHorizontal, Palette, PanelLeft, Paperclip, Pause, Pencil, Phone, Play, Plus, Reply, RotateCw, Search, Send, Settings2, Smartphone, Smile, Star, Sun, Trash2, TriangleAlert, UploadCloud, User, VideoIcon, X, Zap } from "lucide-react";
15
+ import { Accordion as Accordion$1, Avatar as Avatar$1, Checkbox as Checkbox$1, Collapsible, ContextMenu, Dialog, NavigationMenu, Popover as Popover$1, Progress as Progress$1, RadioGroup as RadioGroup$1, Slider as Slider$1, Slot, Switch as Switch$1, Tabs as Tabs$1, VisuallyHidden } from "radix-ui";
16
+ import { Fragment, createContext, forwardRef, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
14
17
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
15
- import { createPortal } from "react-dom";
16
18
  import { cva } from "class-variance-authority";
17
- import { addDays, addMonths, addWeeks, differenceInMinutes, endOfMonth, endOfWeek, format, isSameDay, isSameMonth, isToday, isYesterday, startOfDay, startOfMonth, startOfWeek } from "date-fns";
19
+ import { addDays, addMonths, addWeeks, differenceInCalendarDays, differenceInMinutes, endOfMonth, endOfWeek, format, isSameDay, isSameMonth, isToday, isYesterday, startOfDay, startOfMonth, startOfWeek } from "date-fns";
18
20
  import { ptBR } from "date-fns/locale";
19
- import IMask from "imask";
20
21
  import { DayPicker } from "react-day-picker";
21
22
  import { ptBR as ptBR$1 } from "react-day-picker/locale";
22
23
  import { FormProvider, useController, useFormContext } from "react-hook-form";
@@ -171,129 +172,6 @@ function Accordion({ items, multiple, defaultValue, className }) {
171
172
  });
172
173
  }
173
174
  //#endregion
174
- //#region src/components/app-shell/shell-context.ts
175
- var ShellContext = createContext(null);
176
- function useShell() {
177
- const ctx = useContext(ShellContext);
178
- if (!ctx) throw new Error("useShell precisa estar dentro de <AppShell>.");
179
- return ctx;
180
- }
181
- //#endregion
182
- //#region src/components/ui/action-bar.tsx
183
- /**
184
- * Barra de ações única, com a regra de botões do sistema:
185
- * 1 botão = 100%; 2 botões = cancelar 30% + principal 70%; 3 ou mais = extras no menu.
186
- * Vale para drawer, modal e formulário em página, em qualquer tamanho de tela.
187
- */
188
- function ActionBar({ primary, cancel, secondary, sticky, className }) {
189
- const slot = useContext(ShellContext)?.footerSlot;
190
- const hasMenu = Boolean(secondary?.length);
191
- const layout = cancel ? hasMenu ? "grid-actions-3" : "grid-actions-2" : hasMenu ? "grid-actions-3" : "";
192
- const bar = /* @__PURE__ */ jsx("div", {
193
- "data-rendra": "ACB-001",
194
- className: cn(sticky && "border-t bg-card px-4 pt-4 pb-safe md:px-6", sticky && !slot && "sticky bottom-0 z-20", className),
195
- children: /* @__PURE__ */ jsxs("div", {
196
- className: cn("grid gap-3", layout || "grid-cols-1", sticky && "w-full"),
197
- children: [
198
- hasMenu && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
199
- asChild: true,
200
- children: /* @__PURE__ */ jsx(Button, {
201
- variant: "outline",
202
- iconOnly: true,
203
- "aria-label": "Mais ações",
204
- children: /* @__PURE__ */ jsx(MoreHorizontal, {})
205
- })
206
- }), /* @__PURE__ */ jsx(DropdownMenuContent, {
207
- align: "start",
208
- side: "top",
209
- children: secondary.map((a) => /* @__PURE__ */ jsxs(DropdownMenuItem, {
210
- onSelect: a.onClick,
211
- disabled: a.disabled,
212
- destructive: a.destructive,
213
- children: [a.icon, a.label]
214
- }, a.label))
215
- })] }),
216
- cancel ? /* @__PURE__ */ jsx(Button, {
217
- variant: "outline",
218
- icon: cancel.icon,
219
- onClick: cancel.onClick,
220
- disabled: cancel.disabled || primary.loading,
221
- fullWidth: true,
222
- children: cancel.label
223
- }) : hasMenu ? /* @__PURE__ */ jsx("span", { "aria-hidden": true }) : null,
224
- /* @__PURE__ */ jsx(Button, {
225
- type: primary.type ?? "button",
226
- form: primary.form,
227
- variant: primary.destructive ? "destructive" : "primary",
228
- icon: primary.icon,
229
- onClick: primary.onClick,
230
- disabled: primary.disabled,
231
- loading: primary.loading,
232
- fullWidth: true,
233
- children: primary.loading ? primary.loadingLabel ?? "Salvando..." : primary.label
234
- })
235
- ]
236
- })
237
- });
238
- return sticky && slot ? createPortal(bar, slot) : bar;
239
- }
240
- //#endregion
241
- //#region src/components/ui/alert.tsx
242
- /**
243
- * Alert único do sistema: ícone da marca à esquerda, título em destaque e descrição.
244
- * O tipo é prop; o formato vem do template ativo (quadrado, meio-termo ou arredondado).
245
- */
246
- var alertVariants = cva("flex w-full min-w-0 items-start gap-3 rounded-control border", {
247
- variants: { type: {
248
- success: "border-success/25 bg-success-soft text-success-soft-foreground",
249
- error: "border-destructive/25 bg-destructive-soft text-destructive-soft-foreground",
250
- warning: "border-warning/25 bg-warning-soft text-warning-soft-foreground",
251
- info: "border-info/25 bg-info-soft text-info-soft-foreground"
252
- } },
253
- defaultVariants: { type: "info" }
254
- });
255
- function Alert({ type, title, description, action, onDismiss, animated = false, className }) {
256
- const { shape } = useBrand();
257
- const pill = shape === "pill";
258
- return /* @__PURE__ */ jsxs("div", {
259
- role: type === "error" || type === "warning" ? "alert" : "status",
260
- "data-rendra": "ALRT-001",
261
- className: cn(alertVariants({ type }), pill ? "items-center py-3 pr-4 pl-4 md:pr-6" : "p-4", className),
262
- children: [
263
- /* @__PURE__ */ jsx(BrandFeedbackIcon, {
264
- type,
265
- size: "lg",
266
- animated,
267
- className: cn(!pill && "mt-px")
268
- }),
269
- /* @__PURE__ */ jsxs("div", {
270
- className: cn("flex min-w-0 flex-1 flex-col", !pill && "gap-1"),
271
- children: [/* @__PURE__ */ jsx("p", {
272
- className: "text-sm font-semibold",
273
- children: title
274
- }), description && /* @__PURE__ */ jsx("p", {
275
- className: "text-sm opacity-90",
276
- children: description
277
- })]
278
- }),
279
- action && /* @__PURE__ */ jsx("div", {
280
- className: "shrink-0 self-center",
281
- children: action
282
- }),
283
- onDismiss && /* @__PURE__ */ jsx("button", {
284
- type: "button",
285
- onClick: onDismiss,
286
- "aria-label": "Fechar aviso",
287
- className: cn("flex size-touch shrink-0 items-center justify-center rounded-full transition-colors hover:bg-card/60 md:size-8", pill ? "-my-2" : "-mt-2 -mr-2"),
288
- children: /* @__PURE__ */ jsx(X, {
289
- className: "size-icon-sm",
290
- "aria-hidden": true
291
- })
292
- })
293
- ]
294
- });
295
- }
296
- //#endregion
297
175
  //#region src/lib/initials.ts
298
176
  /** Iniciais de um nome: primeira letra do primeiro e do último nome, em maiúsculas. */
299
177
  function initials(name) {
@@ -617,264 +495,6 @@ function ButtonGroup({ children, options, value, onChange, size = "md", fullWidt
617
495
  });
618
496
  }
619
497
  //#endregion
620
- //#region src/components/ui/overlay-shell.tsx
621
- function OverlayShell({ title, description, icon, children, footer, hideHeader, onRequestClose, bodyClassName }) {
622
- const closeButton = /* @__PURE__ */ jsx("button", {
623
- type: "button",
624
- onClick: onRequestClose,
625
- "aria-label": "Fechar",
626
- className: cn("-mt-1 -mr-2 flex size-touch shrink-0 cursor-pointer items-center justify-center rounded-item text-muted-foreground transition-colors hover:bg-accent hover:text-foreground md:size-control-md", hideHeader && "pointer-events-auto m-0 cursor-default opacity-0 focus-visible:cursor-pointer focus-visible:opacity-100"),
627
- children: /* @__PURE__ */ jsx(X, {
628
- className: "size-icon-md",
629
- "aria-hidden": true
630
- })
631
- });
632
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
633
- hideHeader && /* @__PURE__ */ jsx("div", {
634
- className: "pointer-events-none absolute inset-x-0 top-0 z-10 flex justify-end px-2 pt-safe md:px-4 md:pt-4",
635
- children: closeButton
636
- }),
637
- /* @__PURE__ */ jsxs("header", {
638
- className: cn("flex shrink-0 items-start gap-3 border-b px-4 pt-safe pb-4 md:px-6 md:pt-6", hideHeader && "sr-only"),
639
- children: [
640
- icon && /* @__PURE__ */ jsx("span", {
641
- "aria-hidden": true,
642
- className: "flex size-control-md shrink-0 items-center justify-center rounded-control bg-primary-soft text-primary-soft-foreground [&_svg]:size-icon-md",
643
- children: icon
644
- }),
645
- /* @__PURE__ */ jsxs("div", {
646
- className: "flex min-w-0 flex-1 flex-col gap-1 pt-1",
647
- children: [/* @__PURE__ */ jsx(Dialog.Title, {
648
- className: "text-lg font-semibold wrap-break-word",
649
- children: title
650
- }), description ? /* @__PURE__ */ jsx(Dialog.Description, {
651
- className: "text-sm wrap-break-word text-muted-foreground",
652
- children: description
653
- }) : /* @__PURE__ */ jsx(Dialog.Description, {
654
- className: "sr-only",
655
- children: title
656
- })]
657
- }),
658
- !hideHeader && closeButton
659
- ]
660
- }),
661
- /* @__PURE__ */ jsx("div", {
662
- className: cn("min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-6 md:px-6", bodyClassName),
663
- children
664
- }),
665
- footer && /* @__PURE__ */ jsx("footer", {
666
- className: "shrink-0 border-t bg-card px-4 pt-4 pb-safe md:px-6 md:pb-6",
667
- children: footer
668
- })
669
- ] });
670
- }
671
- //#endregion
672
- //#region src/components/ui/modal.tsx
673
- var sizes$1 = {
674
- sm: "md:max-w-sm",
675
- md: "md:max-w-lg",
676
- lg: "md:max-w-2xl"
677
- };
678
- var overlayCls$1 = "fixed inset-0 z-50 bg-overlay";
679
- var overlayOffCls = cn(overlayCls$1, "data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0");
680
- var contentCls = cn("fixed inset-0 z-50 flex h-dvh flex-col bg-card text-card-foreground outline-none", "md:inset-auto md:top-1/2 md:left-1/2 md:h-auto md:max-h-sheet md:w-full md:-translate-x-1/2 md:-translate-y-1/2 md:rounded-surface md:border md:shadow-lg");
681
- var feedback = {
682
- confirm: "info",
683
- destructive: "error",
684
- info: "info"
685
- };
686
- function Modal({ open, onOpenChange, title, description, type = "confirm", size = "sm", children, confirmLabel = type === "destructive" ? "Excluir" : type === "info" ? "Entendi" : "Confirmar", cancelLabel, onConfirm, formId, loading }) {
687
- const [busy, setBusy] = useState(false);
688
- const close = () => onOpenChange(false);
689
- const confirm = async () => {
690
- if (!onConfirm) return close();
691
- try {
692
- setBusy(true);
693
- await onConfirm();
694
- close();
695
- } finally {
696
- setBusy(false);
697
- }
698
- };
699
- const isForm = type === "form";
700
- const lib = useMotionLib();
701
- const presets = useMotionPresets();
702
- const { isMobile } = useBreakpoint();
703
- const code = resolveCatalogCode("Modal", { type: type === "destructive" ? "confirm" : type });
704
- const sizeCls = sizes$1[size];
705
- const shell = /* @__PURE__ */ jsxs(OverlayShell, {
706
- title,
707
- description: isForm ? description : void 0,
708
- hideHeader: !isForm,
709
- onRequestClose: close,
710
- bodyClassName: cn(!isForm && "flex flex-col items-center justify-center gap-4 text-center"),
711
- footer: /* @__PURE__ */ jsx(ActionBar, {
712
- primary: {
713
- label: confirmLabel,
714
- destructive: type === "destructive",
715
- loading: busy || loading,
716
- loadingLabel: `${confirmLabel}...`,
717
- type: isForm && formId ? "submit" : "button",
718
- form: isForm ? formId : void 0,
719
- onClick: isForm && formId ? void 0 : confirm
720
- },
721
- cancel: type === "info" && cancelLabel === void 0 ? void 0 : {
722
- label: cancelLabel ?? "Cancelar",
723
- onClick: close
724
- }
725
- }),
726
- children: [!isForm && /* @__PURE__ */ jsxs(Fragment$1, { children: [
727
- /* @__PURE__ */ jsx(BrandFeedbackIcon, {
728
- type: feedback[type],
729
- size: "2xl",
730
- animated: true
731
- }),
732
- /* @__PURE__ */ jsxs("div", {
733
- className: "flex flex-col gap-2",
734
- children: [/* @__PURE__ */ jsx("p", {
735
- className: "text-lg font-semibold wrap-anywhere",
736
- children: title
737
- }), description && /* @__PURE__ */ jsx("p", {
738
- className: "text-sm wrap-anywhere text-muted-foreground",
739
- children: description
740
- })]
741
- }),
742
- children
743
- ] }), isForm && /* @__PURE__ */ jsx("div", {
744
- className: "flex flex-col gap-2",
745
- children
746
- })]
747
- });
748
- if (lib) return /* @__PURE__ */ jsx(Dialog.Root, {
749
- open,
750
- onOpenChange,
751
- children: /* @__PURE__ */ jsx(lib.AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Dialog.Portal, {
752
- forceMount: true,
753
- children: [/* @__PURE__ */ jsx(Dialog.Overlay, {
754
- forceMount: true,
755
- asChild: true,
756
- children: /* @__PURE__ */ jsx(lib.m.div, {
757
- className: overlayCls$1,
758
- ...presets.fade
759
- })
760
- }), /* @__PURE__ */ jsx(Dialog.Content, {
761
- forceMount: true,
762
- asChild: true,
763
- "data-rendra": code,
764
- className: cn(contentCls, sizeCls),
765
- children: /* @__PURE__ */ jsx(lib.m.div, {
766
- ...presets.open(isMobile ? { y: 8 } : void 0),
767
- children: shell
768
- })
769
- })]
770
- }, "modal") })
771
- });
772
- return /* @__PURE__ */ jsx(Dialog.Root, {
773
- open,
774
- onOpenChange,
775
- children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Overlay, { className: overlayOffCls }), /* @__PURE__ */ jsx(Dialog.Content, {
776
- "data-rendra": code,
777
- className: cn(contentCls, "data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 md:data-[state=open]:zoom-in-95", sizeCls),
778
- children: shell
779
- })] })
780
- });
781
- }
782
- //#endregion
783
- //#region src/components/ui/info-hint.tsx
784
- function InfoHint({ title, children, className }) {
785
- const [open, setOpen] = useState(false);
786
- return /* @__PURE__ */ jsxs("span", {
787
- className: "contents",
788
- "data-rendra": "INFO-001",
789
- children: [/* @__PURE__ */ jsx(Button, {
790
- variant: "ghost",
791
- size: "sm",
792
- iconOnly: true,
793
- "aria-label": `Sobre: ${title}`,
794
- onClick: () => setOpen(true),
795
- className: cn("shrink-0 text-muted-foreground hover:text-primary-text max-md:size-touch", className),
796
- children: /* @__PURE__ */ jsx(Info, { "aria-hidden": true })
797
- }), /* @__PURE__ */ jsx(Modal, {
798
- open,
799
- onOpenChange: setOpen,
800
- type: "info",
801
- title,
802
- size: "md",
803
- children: /* @__PURE__ */ jsx("div", {
804
- className: "flex flex-col gap-3 text-sm text-muted-foreground",
805
- children
806
- })
807
- })]
808
- });
809
- }
810
- //#endregion
811
- //#region src/components/ui/card.tsx
812
- function Card({ className, "data-rendra": dataRendra, ...props }) {
813
- return /* @__PURE__ */ jsx("div", {
814
- "data-slot": "card",
815
- "data-rendra": dataRendra ?? "CARD-001",
816
- className: cn("flex min-w-0 flex-col rounded-surface border bg-card text-card-foreground", className),
817
- ...props
818
- });
819
- }
820
- function CardHeader({ className, actions, children, ...props }) {
821
- if (!actions) return /* @__PURE__ */ jsx("div", {
822
- "data-slot": "card-header",
823
- className: cn("flex flex-col gap-1 p-4 md:p-6", className),
824
- ...props,
825
- children
826
- });
827
- return /* @__PURE__ */ jsxs("div", {
828
- "data-slot": "card-header",
829
- className: cn("flex flex-wrap items-start justify-between gap-x-4 gap-y-3 p-4 md:p-6", className),
830
- ...props,
831
- children: [/* @__PURE__ */ jsx("div", {
832
- className: "flex min-w-0 flex-1 flex-col gap-1",
833
- children
834
- }), /* @__PURE__ */ jsx("div", {
835
- className: "flex shrink-0 items-center gap-2",
836
- children: actions
837
- })]
838
- });
839
- }
840
- function CardTitle({ className, children, help, ...props }) {
841
- const heading = /* @__PURE__ */ jsx("h3", {
842
- "data-slot": "card-title",
843
- className: cn("text-lg font-semibold", className),
844
- ...props,
845
- children
846
- });
847
- if (!help) return heading;
848
- return /* @__PURE__ */ jsxs("div", {
849
- className: "flex min-w-0 items-center gap-1",
850
- children: [heading, /* @__PURE__ */ jsx(InfoHint, {
851
- title: typeof children === "string" ? children : "Sobre esta seção",
852
- children: help
853
- })]
854
- });
855
- }
856
- function CardDescription({ className, ...props }) {
857
- return /* @__PURE__ */ jsx("p", {
858
- "data-slot": "card-description",
859
- className: cn("text-sm text-muted-foreground", className),
860
- ...props
861
- });
862
- }
863
- function CardContent({ className, ...props }) {
864
- return /* @__PURE__ */ jsx("div", {
865
- "data-slot": "card-content",
866
- className: cn("p-4 md:p-6 [[data-slot=card-header]+&]:pt-0", className),
867
- ...props
868
- });
869
- }
870
- function CardFooter({ className, ...props }) {
871
- return /* @__PURE__ */ jsx("div", {
872
- "data-slot": "card-footer",
873
- className: cn("flex items-center gap-3 border-t px-4 py-3 md:px-6", className),
874
- ...props
875
- });
876
- }
877
- //#endregion
878
498
  //#region src/components/ui/calendar.tsx
879
499
  var viewLabels = {
880
500
  month: "Mês",
@@ -1313,575 +933,6 @@ function Calendar({ events, view: viewProp, defaultView = "month", onViewChange,
1313
933
  });
1314
934
  }
1315
935
  //#endregion
1316
- //#region src/lib/lookup.ts
1317
- var digits$1 = (v) => v.replace(/\D/g, "");
1318
- /** Endereço do CEP (8 dígitos). null se não existir; lança erro se a busca falhar. */
1319
- async function lookupCep(cep, signal) {
1320
- const d = digits$1(cep);
1321
- if (d.length !== 8) return null;
1322
- const res = await fetch(`https://viacep.com.br/ws/${d}/json/`, { signal });
1323
- if (!res.ok) throw new Error("Não foi possível buscar o CEP agora.");
1324
- const data = await res.json();
1325
- if (data.erro) return null;
1326
- return {
1327
- cep: String(data.cep ?? ""),
1328
- logradouro: String(data.logradouro ?? ""),
1329
- complemento: String(data.complemento ?? ""),
1330
- bairro: String(data.bairro ?? ""),
1331
- cidade: String(data.localidade ?? ""),
1332
- uf: String(data.uf ?? "")
1333
- };
1334
- }
1335
- /** Dados públicos do CNPJ (14 dígitos). null se não existir; lança erro se a busca falhar. */
1336
- async function lookupCnpj(cnpj, signal) {
1337
- const d = digits$1(cnpj);
1338
- if (d.length !== 14) return null;
1339
- const res = await fetch(`https://brasilapi.com.br/api/cnpj/v1/${d}`, { signal });
1340
- if (res.status === 404) return null;
1341
- if (!res.ok) throw new Error("Não foi possível buscar o CNPJ agora.");
1342
- const data = await res.json();
1343
- const s = (k) => data[k] == null ? "" : String(data[k]);
1344
- const fone = digits$1(s("ddd_telefone_1"));
1345
- return {
1346
- razaoSocial: s("razao_social"),
1347
- nomeFantasia: s("nome_fantasia"),
1348
- email: s("email").toLowerCase(),
1349
- telefone: fone.length >= 10 ? `(${fone.slice(0, 2)}) ${fone.slice(2, -4)}-${fone.slice(-4)}` : "",
1350
- situacao: s("descricao_situacao_cadastral"),
1351
- endereco: {
1352
- cep: s("cep"),
1353
- logradouro: [s("descricao_tipo_de_logradouro"), s("logradouro")].filter(Boolean).join(" "),
1354
- numero: s("numero"),
1355
- complemento: s("complemento"),
1356
- bairro: s("bairro"),
1357
- cidade: s("municipio"),
1358
- uf: s("uf")
1359
- }
1360
- };
1361
- }
1362
- //#endregion
1363
- //#region src/hooks/use-lookup.ts
1364
- /**
1365
- * Busca CEP (8 dígitos) ou CNPJ (14 dígitos) assim que o valor fica completo. Cancela a
1366
- * busca anterior quando o valor muda. `kind` 'auto' decide pelo tamanho (CPF ou CNPJ: só
1367
- * o CNPJ busca). Sem `onResult`, não faz nada.
1368
- */
1369
- function useLookup(kind, value, onResult) {
1370
- const [loading, setLoading] = useState(false);
1371
- const cb = useRef(onResult);
1372
- cb.current = onResult;
1373
- const digits = value.replace(/\D/g, "");
1374
- const target = kind === "cep" && digits.length === 8 ? "cep" : (kind === "cnpj" || kind === "auto") && digits.length === 14 ? "cnpj" : null;
1375
- const enabled = Boolean(onResult) && target !== null;
1376
- useEffect(() => {
1377
- if (!enabled || !target) return;
1378
- const ctrl = new AbortController();
1379
- const send = (r) => {
1380
- if (!ctrl.signal.aborted) cb.current?.(r);
1381
- };
1382
- setLoading(true);
1383
- send({
1384
- status: "loading",
1385
- kind: target
1386
- });
1387
- (target === "cep" ? lookupCep(digits, ctrl.signal).then((data) => data ? send({
1388
- status: "found",
1389
- kind: "cep",
1390
- data,
1391
- message: "Endereço preenchido pelo CEP."
1392
- }) : send({
1393
- status: "not-found",
1394
- kind: "cep",
1395
- message: "CEP não encontrado. Preencha o endereço."
1396
- })) : lookupCnpj(digits, ctrl.signal).then((data) => data ? send({
1397
- status: "found",
1398
- kind: "cnpj",
1399
- data,
1400
- message: `${data.situacao ? `Situação: ${data.situacao}. ` : ""}Dados preenchidos pelo CNPJ.`
1401
- }) : send({
1402
- status: "not-found",
1403
- kind: "cnpj",
1404
- message: "CNPJ não encontrado. Preencha os dados abaixo."
1405
- }))).catch((e) => send({
1406
- status: "error",
1407
- kind: target,
1408
- message: e instanceof Error ? e.message : "Não foi possível buscar agora."
1409
- })).finally(() => {
1410
- if (!ctrl.signal.aborted) setLoading(false);
1411
- });
1412
- return () => {
1413
- ctrl.abort();
1414
- setLoading(false);
1415
- };
1416
- }, [
1417
- enabled,
1418
- target,
1419
- digits
1420
- ]);
1421
- return loading;
1422
- }
1423
- //#endregion
1424
- //#region src/lib/control.ts
1425
- /**
1426
- * Moldura única dos campos (Input, Select, DatePicker, Textarea). Mesma altura dos botões
1427
- * em cada tamanho; fonte de 16px no mobile para o iOS não dar zoom ao focar.
1428
- */
1429
- /** Marque o elemento com data-slot="control": o teste de toque mede a moldura, não o input. */
1430
- var controlFrame = cva("group/control flex w-full min-w-0 items-center gap-2 rounded-control border border-input bg-field text-base text-foreground transition-[border-color,box-shadow] duration-150 focus-within:border-ring focus-within:bg-card focus-within:ring-2 focus-within:ring-ring/25 hover:border-foreground/40 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-60 has-[:disabled]:hover:border-input md:text-sm", {
1431
- variants: {
1432
- size: {
1433
- sm: "h-control-sm px-3",
1434
- md: "h-control-md px-3",
1435
- lg: "h-control-lg px-4"
1436
- },
1437
- invalid: {
1438
- true: "border-destructive focus-within:border-destructive focus-within:ring-destructive/25 hover:border-destructive",
1439
- false: ""
1440
- }
1441
- },
1442
- defaultVariants: {
1443
- size: "md",
1444
- invalid: false
1445
- }
1446
- });
1447
- /** Classe do <input> nativo dentro da moldura. */
1448
- var controlInput = "h-full min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed";
1449
- /** Botão pequeno dentro da moldura (limpar, mostrar senha). */
1450
- var controlAdornmentButton = "-mr-2 flex size-control-sm shrink-0 cursor-pointer items-center justify-center rounded-item text-muted-foreground transition-colors outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring md:-mr-1 md:size-8 [&_svg]:size-icon-sm";
1451
- /**
1452
- * Reserva, dentro de um gatilho, o espaço do botão sobreposto (limpar do Select e do
1453
- * DatePicker), para o texto não passar por baixo dele.
1454
- */
1455
- var controlAdornmentSpace = "size-control-sm shrink-0 md:size-8";
1456
- //#endregion
1457
- //#region src/lib/masks.ts
1458
- var numberBlock = (scale, max) => ({
1459
- mask: Number,
1460
- scale,
1461
- thousandsSeparator: ".",
1462
- radix: ",",
1463
- mapToRadix: ["."],
1464
- padFractionalZeros: true,
1465
- normalizeZeros: true,
1466
- min: 0,
1467
- ...max !== void 0 ? { max } : {}
1468
- });
1469
- var masks = {
1470
- cpf: {
1471
- options: { mask: "000.000.000-00" },
1472
- inputMode: "numeric",
1473
- placeholder: "000.000.000-00"
1474
- },
1475
- cnpj: {
1476
- options: { mask: "00.000.000/0000-00" },
1477
- inputMode: "numeric",
1478
- placeholder: "00.000.000/0000-00"
1479
- },
1480
- cpfCnpj: {
1481
- options: { mask: [{ mask: "000.000.000-00" }, { mask: "00.000.000/0000-00" }] },
1482
- inputMode: "numeric",
1483
- placeholder: "CPF ou CNPJ"
1484
- },
1485
- phone: {
1486
- options: { mask: [{ mask: "(00) 0000-0000" }, { mask: "(00) 00000-0000" }] },
1487
- inputMode: "tel",
1488
- placeholder: "(00) 00000-0000"
1489
- },
1490
- cep: {
1491
- options: { mask: "00000-000" },
1492
- inputMode: "numeric",
1493
- placeholder: "00000-000"
1494
- },
1495
- date: {
1496
- options: { mask: "00/00/0000" },
1497
- inputMode: "numeric",
1498
- placeholder: "DD/MM/AAAA"
1499
- },
1500
- time: {
1501
- options: { mask: "00:00" },
1502
- inputMode: "numeric",
1503
- placeholder: "00:00"
1504
- },
1505
- currency: {
1506
- options: {
1507
- mask: "R$ num",
1508
- lazy: false,
1509
- blocks: { num: numberBlock(2) }
1510
- },
1511
- inputMode: "decimal",
1512
- placeholder: "R$ 0,00"
1513
- },
1514
- percent: {
1515
- options: {
1516
- mask: "num %",
1517
- lazy: false,
1518
- blocks: { num: numberBlock(2, 100) }
1519
- },
1520
- inputMode: "decimal",
1521
- placeholder: "0,00 %"
1522
- }
1523
- };
1524
- /** Máscara de percentual com teto configurável (padrão 100), para o Input com unidades (A9). */
1525
- function percentMask(max = 100) {
1526
- return {
1527
- options: {
1528
- mask: "num %",
1529
- lazy: false,
1530
- blocks: { num: numberBlock(2, max) }
1531
- },
1532
- inputMode: "decimal",
1533
- placeholder: "0,00 %"
1534
- };
1535
- }
1536
- /** DDI padrão do campo de telefone. */
1537
- var DEFAULT_DDI = "55";
1538
- /** Lista padrão do seletor de DDI (Brasil primeiro). Troque com a prop ddiOptions do Input. */
1539
- var phoneCountries = [
1540
- {
1541
- ddi: "55",
1542
- name: "Brasil"
1543
- },
1544
- {
1545
- ddi: "54",
1546
- name: "Argentina"
1547
- },
1548
- {
1549
- ddi: "56",
1550
- name: "Chile"
1551
- },
1552
- {
1553
- ddi: "57",
1554
- name: "Colômbia"
1555
- },
1556
- {
1557
- ddi: "595",
1558
- name: "Paraguai"
1559
- },
1560
- {
1561
- ddi: "598",
1562
- name: "Uruguai"
1563
- },
1564
- {
1565
- ddi: "52",
1566
- name: "México"
1567
- },
1568
- {
1569
- ddi: "1",
1570
- name: "Estados Unidos e Canadá"
1571
- },
1572
- {
1573
- ddi: "351",
1574
- name: "Portugal"
1575
- },
1576
- {
1577
- ddi: "34",
1578
- name: "Espanha"
1579
- },
1580
- {
1581
- ddi: "33",
1582
- name: "França"
1583
- },
1584
- {
1585
- ddi: "39",
1586
- name: "Itália"
1587
- },
1588
- {
1589
- ddi: "49",
1590
- name: "Alemanha"
1591
- },
1592
- {
1593
- ddi: "44",
1594
- name: "Reino Unido"
1595
- }
1596
- ];
1597
- /** Máscara do número fora do Brasil: só dígitos, em grupos, até 15 (limite do padrão E.164). */
1598
- var internationalPhoneMask = {
1599
- options: { mask: "000 000 000 000 000" },
1600
- inputMode: "tel",
1601
- placeholder: "Número com DDD"
1602
- };
1603
- /** Número completo no padrão internacional: toE164("55", "(11) 91234-5678") = "+5511912345678". */
1604
- function toE164(ddi, national) {
1605
- const digits = national.replace(/\D/g, "");
1606
- return digits ? `+${ddi}${digits}` : "";
1607
- }
1608
- /** Converte "R$ 1.250,00" ou "12,5 %" em número. */
1609
- function parseLocaleNumber(masked) {
1610
- const clean = masked.replace(/[^\d,-]/g, "").replace(",", ".");
1611
- if (!clean) return null;
1612
- const n = Number(clean);
1613
- return Number.isFinite(n) ? n : null;
1614
- }
1615
- /**
1616
- * Moeda em centavos inteiros, sem erro de arredondamento de ponto flutuante:
1617
- * "R$ 1.250,50" -> 125050. Vazio -> null. Use centavos para guardar e somar dinheiro.
1618
- */
1619
- function toCents(masked) {
1620
- const d = masked.replace(/\D/g, "");
1621
- if (!d) return null;
1622
- if (!/,\d{1,2}\s*$/.test(masked.replace(/\s*%$/, ""))) return Number(d) * 100;
1623
- const [int = "", dec = ""] = masked.replace(/[^\d,]/g, "").split(",");
1624
- return Number(int || "0") * 100 + Number(dec.padEnd(2, "0").slice(0, 2));
1625
- }
1626
- /** Centavos para o texto do campo de moeda: 125050 -> "R$ 1.250,50". */
1627
- function formatCents(cents) {
1628
- return formatCurrency(cents / 100);
1629
- }
1630
- /** Formata número como moeda brasileira: 1250 -> "R$ 1.250,00". */
1631
- function formatCurrency(value) {
1632
- return value.toLocaleString("pt-BR", {
1633
- style: "currency",
1634
- currency: "BRL"
1635
- });
1636
- }
1637
- //#endregion
1638
- //#region src/components/ui/input.tsx
1639
- /**
1640
- * Input único. Tipos, máscaras, ícone, limpar, senha, unidades e valor guardado são props:
1641
- * nunca crie InputCPF, InputTelefone, InputSenha ou InputSegredo.
1642
- */
1643
- var Input = forwardRef(function Input({ size, mask, icon, suffix, clearable, invalid, type = "text", value, defaultValue, onChange, onValueChange, disabled, className, inputClassName, inputMode, placeholder, ddi: ddiProp, onDdiChange, ddiOptions = phoneCountries, hideDdi, onCentsChange, onLookup, units, unit: unitProp, onUnitChange, percentMax, variant, hasValue, maskedHint, isEditing, onStartEdit, onCancelEdit, onRemove, removing, ...props }, ref) {
1644
- const inputRef = useRef(null);
1645
- useImperativeHandle(ref, () => inputRef.current);
1646
- const maskRef = useRef(null);
1647
- const [showPassword, setShowPassword] = useState(false);
1648
- const [inner, setInner] = useState(String(value ?? defaultValue ?? ""));
1649
- const current = value ?? inner;
1650
- const [secretDraft, setSecretDraft] = useState("");
1651
- const [lastIsEditing, setLastIsEditing] = useState(isEditing);
1652
- if (isEditing !== lastIsEditing) {
1653
- setLastIsEditing(isEditing);
1654
- if (isEditing) setSecretDraft("");
1655
- }
1656
- const cb = useRef({
1657
- onChange,
1658
- onValueChange,
1659
- onCentsChange
1660
- });
1661
- cb.current = {
1662
- onChange,
1663
- onValueChange,
1664
- onCentsChange
1665
- };
1666
- const searching = useLookup(mask === "cep" ? "cep" : mask === "cnpj" ? "cnpj" : mask === "cpfCnpj" ? "auto" : void 0, current, onLookup);
1667
- const isPhone = mask === "phone";
1668
- const [innerDdi, setInnerDdi] = useState(ddiProp ?? "55");
1669
- const ddi = ddiProp ?? innerDdi;
1670
- const showDdi = isPhone && !hideDdi;
1671
- const intl = isPhone && ddi !== "55";
1672
- const hasUnits = Boolean(units && units.length > 0);
1673
- const [innerUnit, setInnerUnit] = useState(unitProp ?? units?.[0]?.id ?? "");
1674
- const unit = hasUnits ? unitProp ?? innerUnit : void 0;
1675
- const unitMaskName = unit === "percent" ? "percent" : unit === "currency" ? "currency" : void 0;
1676
- const def = intl ? internationalPhoneMask : hasUnits ? unitMaskName === "percent" ? percentMask(percentMax) : unitMaskName === "currency" ? masks.currency : void 0 : mask ? masks[mask] : void 0;
1677
- const maskKey = !def ? void 0 : intl ? "phone-intl" : hasUnits ? `unit-${unitMaskName}-${percentMax ?? ""}` : mask;
1678
- useEffect(() => {
1679
- if (!maskKey || !def || !inputRef.current) return;
1680
- const m = IMask(inputRef.current, def.options);
1681
- maskRef.current = m;
1682
- m.on("accept", () => {
1683
- setInner(m.value);
1684
- cb.current.onChange?.(m.value);
1685
- cb.current.onValueChange?.(m.unmaskedValue, m.value);
1686
- if (mask === "currency" || unitMaskName === "currency") cb.current.onCentsChange?.(toCents(m.value));
1687
- });
1688
- return () => {
1689
- m.destroy();
1690
- maskRef.current = null;
1691
- };
1692
- }, [maskKey]);
1693
- useEffect(() => {
1694
- const m = maskRef.current;
1695
- if (m && value !== void 0 && value !== m.value) m.value = value;
1696
- }, [value]);
1697
- const isPassword = type === "password";
1698
- const country = ddiOptions.find((c) => c.ddi === ddi);
1699
- const isSecret = variant === "secret";
1700
- const { id: fieldId, "aria-describedby": fieldDescribedBy, "aria-required": fieldRequired } = props;
1701
- const resetValue = () => {
1702
- if (maskRef.current) maskRef.current.value = "";
1703
- setInner("");
1704
- setSecretDraft("");
1705
- onChange?.("");
1706
- onValueChange?.("", "");
1707
- onCentsChange?.(null);
1708
- };
1709
- const clear = () => {
1710
- resetValue();
1711
- inputRef.current?.focus();
1712
- };
1713
- const changeUnit = (id) => {
1714
- setInnerUnit(id);
1715
- onUnitChange?.(id);
1716
- resetValue();
1717
- };
1718
- if (isSecret && !isEditing) return /* @__PURE__ */ jsxs("div", {
1719
- "data-slot": "control",
1720
- "data-rendra": "CAMP-001",
1721
- className: cn(controlFrame({
1722
- size,
1723
- invalid
1724
- }), className),
1725
- children: [
1726
- /* @__PURE__ */ jsx("span", {
1727
- className: "min-w-0 flex-1 truncate font-mono text-sm text-muted-foreground",
1728
- children: hasValue ? maskedHint ?? "••••••••" : "Nenhum valor salvo"
1729
- }),
1730
- /* @__PURE__ */ jsx(Button, {
1731
- type: "button",
1732
- variant: "ghost",
1733
- size: "sm",
1734
- onClick: onStartEdit,
1735
- disabled,
1736
- id: fieldId,
1737
- "aria-describedby": fieldDescribedBy,
1738
- "aria-required": fieldRequired,
1739
- children: "Trocar"
1740
- }),
1741
- hasValue && onRemove && /* @__PURE__ */ jsx(Button, {
1742
- type: "button",
1743
- variant: "ghost",
1744
- size: "sm",
1745
- onClick: onRemove,
1746
- disabled: disabled || removing,
1747
- loading: removing,
1748
- children: "Remover"
1749
- })
1750
- ]
1751
- });
1752
- return /* @__PURE__ */ jsxs("div", {
1753
- "data-slot": "control",
1754
- "data-rendra": "CAMP-001",
1755
- className: cn(controlFrame({
1756
- size,
1757
- invalid
1758
- }), className),
1759
- children: [
1760
- showDdi && /* @__PURE__ */ jsxs("span", {
1761
- className: "relative -ml-1 flex h-full min-w-touch shrink-0 items-center gap-1 border-r pr-2 text-sm font-medium",
1762
- children: [
1763
- /* @__PURE__ */ jsxs("span", {
1764
- "aria-hidden": true,
1765
- children: ["+", ddi]
1766
- }),
1767
- /* @__PURE__ */ jsx(ChevronDown, {
1768
- className: "size-icon-sm text-muted-foreground",
1769
- "aria-hidden": true
1770
- }),
1771
- /* @__PURE__ */ jsx("select", {
1772
- "aria-label": `Código do país (DDI): ${country?.name ?? ""} +${ddi}`,
1773
- value: ddi,
1774
- disabled,
1775
- onChange: (e) => {
1776
- setInnerDdi(e.target.value);
1777
- onDdiChange?.(e.target.value);
1778
- },
1779
- className: "absolute inset-0 cursor-pointer bg-popover text-popover-foreground opacity-0 disabled:cursor-not-allowed",
1780
- children: ddiOptions.map((c) => /* @__PURE__ */ jsxs("option", {
1781
- value: c.ddi,
1782
- className: "bg-popover text-popover-foreground",
1783
- children: [
1784
- c.name,
1785
- " (+",
1786
- c.ddi,
1787
- ")"
1788
- ]
1789
- }, c.ddi))
1790
- })
1791
- ]
1792
- }),
1793
- icon && /* @__PURE__ */ jsx("span", {
1794
- className: "flex shrink-0 text-muted-foreground [&_svg]:size-icon-sm",
1795
- "aria-hidden": true,
1796
- children: icon
1797
- }),
1798
- /* @__PURE__ */ jsx("input", {
1799
- ref: inputRef,
1800
- type: isSecret ? showPassword ? "text" : "password" : isPassword && showPassword ? "text" : type,
1801
- inputMode: inputMode ?? def?.inputMode,
1802
- placeholder: placeholder ?? def?.placeholder,
1803
- disabled,
1804
- "aria-invalid": invalid || void 0,
1805
- "aria-busy": searching || void 0,
1806
- className: cn(controlInput, inputClassName),
1807
- ...isSecret ? {
1808
- value: secretDraft,
1809
- onChange: (e) => {
1810
- setSecretDraft(e.target.value);
1811
- onChange?.(e.target.value);
1812
- onValueChange?.(e.target.value, e.target.value);
1813
- }
1814
- } : maskKey ? { defaultValue: current } : {
1815
- value: current,
1816
- onChange: (e) => {
1817
- setInner(e.target.value);
1818
- onChange?.(e.target.value);
1819
- onValueChange?.(e.target.value, e.target.value);
1820
- }
1821
- },
1822
- ...props
1823
- }, hasUnits ? `unit-${unit}` : void 0),
1824
- searching && /* @__PURE__ */ jsx(Spinner, {
1825
- size: "sm",
1826
- label: "Buscando…",
1827
- className: "shrink-0 text-muted-foreground"
1828
- }),
1829
- hasUnits && /* @__PURE__ */ jsxs("span", {
1830
- className: "relative -mr-1 flex h-full min-w-touch shrink-0 items-center gap-1 border-l pl-2 text-sm font-medium",
1831
- children: [
1832
- /* @__PURE__ */ jsx("span", {
1833
- "aria-hidden": true,
1834
- children: units?.find((u) => u.id === unit)?.label ?? unit
1835
- }),
1836
- /* @__PURE__ */ jsx(ChevronDown, {
1837
- className: "size-icon-sm text-muted-foreground",
1838
- "aria-hidden": true
1839
- }),
1840
- /* @__PURE__ */ jsx("select", {
1841
- "aria-label": `Unidade: ${units?.find((u) => u.id === unit)?.label ?? unit}`,
1842
- value: unit,
1843
- disabled,
1844
- onChange: (e) => changeUnit(e.target.value),
1845
- className: "absolute inset-0 cursor-pointer bg-popover text-popover-foreground opacity-0 disabled:cursor-not-allowed",
1846
- children: units?.map((u) => /* @__PURE__ */ jsx("option", {
1847
- value: u.id,
1848
- className: "bg-popover text-popover-foreground",
1849
- children: u.label
1850
- }, u.id))
1851
- })
1852
- ]
1853
- }),
1854
- !hasUnits && suffix && /* @__PURE__ */ jsx("span", {
1855
- className: "shrink-0 text-sm text-muted-foreground",
1856
- children: suffix
1857
- }),
1858
- clearable && (isSecret ? secretDraft : current) && !disabled && /* @__PURE__ */ jsx("button", {
1859
- type: "button",
1860
- onClick: clear,
1861
- "aria-label": "Limpar campo",
1862
- className: "-mr-2 flex size-control-sm shrink-0 cursor-pointer items-center justify-center rounded-item text-muted-foreground transition-colors outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring md:-mr-1 md:size-8 [&_svg]:size-icon-sm",
1863
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
1864
- }),
1865
- (isPassword || isSecret) && /* @__PURE__ */ jsx("button", {
1866
- type: "button",
1867
- onClick: () => setShowPassword((s) => !s),
1868
- "aria-label": showPassword ? "Ocultar senha" : "Mostrar senha",
1869
- "aria-pressed": showPassword,
1870
- className: "-mr-2 flex size-control-sm shrink-0 cursor-pointer items-center justify-center rounded-item text-muted-foreground transition-colors outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring md:-mr-1 md:size-8 [&_svg]:size-icon-sm",
1871
- children: showPassword ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": true }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": true })
1872
- }),
1873
- isSecret && /* @__PURE__ */ jsx(Button, {
1874
- type: "button",
1875
- variant: "ghost",
1876
- size: "sm",
1877
- onClick: onCancelEdit,
1878
- disabled,
1879
- children: "Cancelar"
1880
- })
1881
- ]
1882
- });
1883
- });
1884
- //#endregion
1885
936
  //#region src/components/ui/popover.tsx
1886
937
  var PopoverOpenContext = createContext(null);
1887
938
  function AnimatedRoot({ open, defaultOpen, onOpenChange, ...rest }) {
@@ -3474,226 +2525,12 @@ function applyPalette(seeds, doc = document) {
3474
2525
  const attr = "data-palette-runtime";
3475
2526
  let style = doc.head.querySelector(`style[${attr}="${seeds.id}"]`);
3476
2527
  if (!style) {
3477
- style = doc.createElement("style");
3478
- style.setAttribute(attr, seeds.id);
3479
- doc.head.appendChild(style);
3480
- }
3481
- style.textContent = paletteCss(palette);
3482
- return palette;
3483
- }
3484
- //#endregion
3485
- //#region src/components/ui/picker-panel.tsx
3486
- /** A folha fecha se o arraste passou de 40% da altura ou se foi um arremesso (> 0,11 px/ms). */
3487
- var DISMISS_FRACTION = .4;
3488
- var DISMISS_VELOCITY = 110;
3489
- /**
3490
- * A folha do celular em `on`: entra de baixo, acompanha o dedo (arraste vertical pela alça, com
3491
- * fricção ao puxar para cima), fecha por distância ou por arremesso e, se o dedo solta antes
3492
- * disso, volta à origem com a mola da folha (a velocidade do dedo entra na mola, sem costura). O
3493
- * fundo escurecido acompanha a distância. Só a alça arrasta: o corpo rola sem disputar o gesto.
3494
- * Nenhum valor passa por `style`: o Motion guarda a posição do arraste, e o escurecimento do
3495
- * fundo é gravado direto no elemento (sem render por quadro).
3496
- */
3497
- function MobileSheet({ open, onOpenChange, trigger, adornment, head, rest }) {
3498
- const { AnimatePresence, animate, m, useDragControls } = useMotionOn();
3499
- const presets = useMotionPresets();
3500
- const ready = useLayoutReady();
3501
- const controls = useDragControls();
3502
- const sheetRef = useRef(null);
3503
- const dimRef = useRef(null);
3504
- const height = useRef(1);
3505
- return /* @__PURE__ */ jsxs(Dialog.Root, {
3506
- open,
3507
- onOpenChange,
3508
- children: [/* @__PURE__ */ jsx(Anchor, {
3509
- adornment,
3510
- children: /* @__PURE__ */ jsx(Dialog.Trigger, {
3511
- asChild: true,
3512
- children: trigger
3513
- })
3514
- }), /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Dialog.Portal, {
3515
- forceMount: true,
3516
- children: [/* @__PURE__ */ jsx(Dialog.Overlay, {
3517
- forceMount: true,
3518
- asChild: true,
3519
- children: /* @__PURE__ */ jsx(m.div, {
3520
- className: "fixed inset-0 z-40",
3521
- ...presets.fade,
3522
- children: /* @__PURE__ */ jsx("div", {
3523
- ref: dimRef,
3524
- className: "absolute inset-0 bg-overlay"
3525
- })
3526
- })
3527
- }), /* @__PURE__ */ jsx(Dialog.Content, {
3528
- forceMount: true,
3529
- asChild: true,
3530
- "aria-describedby": void 0,
3531
- className: "fixed inset-x-0 bottom-0 z-50 flex max-h-sheet flex-col rounded-t-surface border-t bg-popover text-popover-foreground shadow-lg outline-none",
3532
- children: /* @__PURE__ */ jsxs(m.div, {
3533
- ref: sheetRef,
3534
- ...presets.sheet("bottom"),
3535
- drag: ready ? "y" : false,
3536
- dragControls: controls,
3537
- dragListener: false,
3538
- dragConstraints: { top: 0 },
3539
- dragElastic: {
3540
- top: .2,
3541
- bottom: 0
3542
- },
3543
- dragSnapToOrigin: true,
3544
- dragTransition: presets.sheetBounce,
3545
- onDragStart: () => {
3546
- height.current = sheetRef.current?.offsetHeight || 1;
3547
- },
3548
- onDrag: (_, info) => {
3549
- const dim = dimRef.current;
3550
- if (dim) dim.style.opacity = String(1 - Math.min(1, Math.max(0, info.offset.y / height.current)));
3551
- },
3552
- onDragEnd: (_, info) => {
3553
- if (info.offset.y > height.current * DISMISS_FRACTION || info.velocity.y > DISMISS_VELOCITY) {
3554
- onOpenChange(false);
3555
- return;
3556
- }
3557
- if (dimRef.current) animate(dimRef.current, { opacity: 1 }, presets.springSheet);
3558
- },
3559
- children: [/* @__PURE__ */ jsx("div", {
3560
- "data-sheet-handle": true,
3561
- onPointerDown: (e) => controls.start(e),
3562
- className: "touch-none",
3563
- children: head
3564
- }), rest]
3565
- })
3566
- })]
3567
- }, "sheet") })]
3568
- });
3569
- }
3570
- function Anchor({ adornment, children }) {
3571
- if (!adornment) return children;
3572
- return /* @__PURE__ */ jsxs("div", {
3573
- className: "relative w-full min-w-0",
3574
- children: [children, adornment]
3575
- });
3576
- }
3577
- function PickerPanel({ open, onOpenChange, trigger, title, header, children, footer, width = "trigger", adornment, align = "start" }) {
3578
- const { isMobile } = useBreakpoint();
3579
- const lib = useMotionLib();
3580
- const presets = useMotionPresets();
3581
- const on = lib !== null;
3582
- const sheetHead = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
3583
- className: "flex shrink-0 flex-col items-center pt-2",
3584
- children: /* @__PURE__ */ jsx("span", {
3585
- "aria-hidden": true,
3586
- className: "h-1 w-12 rounded-full bg-border"
3587
- })
3588
- }), /* @__PURE__ */ jsxs("div", {
3589
- className: "flex shrink-0 items-center justify-between gap-2 pr-2 pl-4",
3590
- children: [/* @__PURE__ */ jsx(Dialog.Title, {
3591
- className: "truncate text-base font-semibold",
3592
- children: title
3593
- }), /* @__PURE__ */ jsx(Dialog.Close, {
3594
- "aria-label": "Fechar",
3595
- className: "flex size-touch items-center justify-center rounded-item text-muted-foreground hover:bg-accent",
3596
- children: /* @__PURE__ */ jsx(X, {
3597
- className: "size-icon-md",
3598
- "aria-hidden": true
3599
- })
3600
- })]
3601
- })] });
3602
- const sheetRest = /* @__PURE__ */ jsxs(Fragment$1, { children: [
3603
- header && /* @__PURE__ */ jsx("div", {
3604
- className: "shrink-0 border-b px-4 pb-3",
3605
- children: header
3606
- }),
3607
- /* @__PURE__ */ jsx("div", {
3608
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-2",
3609
- children
3610
- }),
3611
- footer && /* @__PURE__ */ jsx("div", {
3612
- className: "shrink-0 border-t px-4 pt-3 pb-safe",
3613
- children: footer
3614
- })
3615
- ] });
3616
- const sheetBody = /* @__PURE__ */ jsxs(Fragment$1, { children: [sheetHead, sheetRest] });
3617
- const panelCls = cn("z-50 flex max-h-command origin-(--radix-popover-content-transform-origin) flex-col overflow-hidden rounded-surface border bg-popover text-popover-foreground shadow-md outline-none", width === "trigger" ? "w-trigger max-w-popover" : "w-auto max-w-popover md:max-w-none");
3618
- const panelBody = /* @__PURE__ */ jsxs(Fragment$1, { children: [
3619
- header && /* @__PURE__ */ jsx("div", {
3620
- className: "shrink-0 border-b p-2",
3621
- children: header
3622
- }),
3623
- /* @__PURE__ */ jsx("div", {
3624
- className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-1",
3625
- children
3626
- }),
3627
- footer && /* @__PURE__ */ jsx("div", {
3628
- className: "shrink-0 border-t p-2",
3629
- children: footer
3630
- })
3631
- ] });
3632
- if (on && isMobile) return /* @__PURE__ */ jsx(MobileSheet, {
3633
- open,
3634
- onOpenChange,
3635
- trigger,
3636
- adornment,
3637
- head: sheetHead,
3638
- rest: sheetRest
3639
- });
3640
- if (on) return /* @__PURE__ */ jsxs(Popover$1.Root, {
3641
- open,
3642
- onOpenChange,
3643
- children: [/* @__PURE__ */ jsx(Anchor, {
3644
- adornment,
3645
- children: /* @__PURE__ */ jsx(Popover$1.Trigger, {
3646
- asChild: true,
3647
- children: trigger
3648
- })
3649
- }), /* @__PURE__ */ jsx(lib.AnimatePresence, { children: open && /* @__PURE__ */ jsx(Popover$1.Portal, {
3650
- forceMount: true,
3651
- children: /* @__PURE__ */ jsx(Popover$1.Content, {
3652
- forceMount: true,
3653
- asChild: true,
3654
- align,
3655
- sideOffset: 6,
3656
- collisionPadding: 16,
3657
- className: panelCls,
3658
- children: /* @__PURE__ */ jsx(lib.m.div, {
3659
- ...presets.open(),
3660
- children: panelBody
3661
- })
3662
- })
3663
- }, "panel") })]
3664
- });
3665
- if (isMobile) return /* @__PURE__ */ jsxs(Dialog.Root, {
3666
- open,
3667
- onOpenChange,
3668
- children: [/* @__PURE__ */ jsx(Anchor, {
3669
- adornment,
3670
- children: /* @__PURE__ */ jsx(Dialog.Trigger, {
3671
- asChild: true,
3672
- children: trigger
3673
- })
3674
- }), /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Overlay, { className: "fixed inset-0 z-40 bg-overlay data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0" }), /* @__PURE__ */ jsx(Dialog.Content, {
3675
- "aria-describedby": void 0,
3676
- className: "fixed inset-x-0 bottom-0 z-50 flex max-h-sheet flex-col rounded-t-surface border-t bg-popover text-popover-foreground shadow-lg outline-none data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:slide-out-to-bottom data-[state=open]:animate-in data-[state=open]:slide-in-from-bottom",
3677
- children: sheetBody
3678
- })] })]
3679
- });
3680
- return /* @__PURE__ */ jsxs(Popover$1.Root, {
3681
- open,
3682
- onOpenChange,
3683
- children: [/* @__PURE__ */ jsx(Anchor, {
3684
- adornment,
3685
- children: /* @__PURE__ */ jsx(Popover$1.Trigger, {
3686
- asChild: true,
3687
- children: trigger
3688
- })
3689
- }), /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Content, {
3690
- align,
3691
- sideOffset: 6,
3692
- collisionPadding: 16,
3693
- className: cn("z-50 flex max-h-command origin-(--radix-popover-content-transform-origin) flex-col overflow-hidden rounded-surface border bg-popover text-popover-foreground shadow-md outline-none", "data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95", width === "trigger" ? "w-trigger max-w-popover" : "w-auto max-w-popover md:max-w-none"),
3694
- children: panelBody
3695
- }) })]
3696
- });
2528
+ style = doc.createElement("style");
2529
+ style.setAttribute(attr, seeds.id);
2530
+ doc.head.appendChild(style);
2531
+ }
2532
+ style.textContent = paletteCss(palette);
2533
+ return palette;
3697
2534
  }
3698
2535
  //#endregion
3699
2536
  //#region src/components/ui/color-picker.tsx
@@ -3790,82 +2627,6 @@ var ColorPicker = forwardRef(function ColorPicker({ value, onChange, swatches =
3790
2627
  });
3791
2628
  });
3792
2629
  //#endregion
3793
- //#region src/components/ui/drawer.tsx
3794
- var sizes = {
3795
- "30": "md:w-sheet-30",
3796
- "40": "md:w-sheet-40",
3797
- "50": "md:w-sheet-50",
3798
- "75": "md:w-sheet-75",
3799
- full: "md:w-full",
3800
- sm: "md:w-sheet-30",
3801
- md: "md:w-sheet-30",
3802
- lg: "md:w-sheet-50",
3803
- xl: "md:w-sheet-75"
3804
- };
3805
- var overlayCls = "fixed inset-0 z-50 bg-overlay";
3806
- var panelCls = "fixed inset-y-0 right-0 z-50 flex h-dvh w-full flex-col bg-card text-card-foreground shadow-lg outline-none md:border-l";
3807
- function Drawer({ open, onOpenChange, title, description, icon, size = "30", children, footer, dirty, discardTitle = "Descartar alterações?", discardDescription = "As informações preenchidas neste painel serão perdidas." }) {
3808
- const [confirming, setConfirming] = useState(false);
3809
- const requestClose = () => dirty ? setConfirming(true) : onOpenChange(false);
3810
- const lib = useMotionLib();
3811
- const presets = useMotionPresets();
3812
- const keepOpenIfDirty = (e) => {
3813
- if (dirty) {
3814
- e.preventDefault();
3815
- setConfirming(true);
3816
- }
3817
- };
3818
- const shell = /* @__PURE__ */ jsx(OverlayShell, {
3819
- title,
3820
- description,
3821
- icon,
3822
- footer,
3823
- onRequestClose: requestClose,
3824
- children
3825
- });
3826
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Dialog.Root, {
3827
- open,
3828
- onOpenChange: (o) => o ? onOpenChange(true) : requestClose(),
3829
- children: lib ? /* @__PURE__ */ jsx(lib.AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Dialog.Portal, {
3830
- forceMount: true,
3831
- children: [/* @__PURE__ */ jsx(Dialog.Overlay, {
3832
- forceMount: true,
3833
- asChild: true,
3834
- children: /* @__PURE__ */ jsx(lib.m.div, {
3835
- className: overlayCls,
3836
- ...presets.fade
3837
- })
3838
- }), /* @__PURE__ */ jsx(Dialog.Content, {
3839
- forceMount: true,
3840
- asChild: true,
3841
- onEscapeKeyDown: keepOpenIfDirty,
3842
- onPointerDownOutside: keepOpenIfDirty,
3843
- "data-rendra": "GAV-001",
3844
- className: cn(panelCls, sizes[size]),
3845
- children: /* @__PURE__ */ jsx(lib.m.div, {
3846
- ...presets.sheet("right"),
3847
- children: shell
3848
- })
3849
- })]
3850
- }, "drawer") }) : /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(Dialog.Overlay, { className: cn(overlayCls, "data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0") }), /* @__PURE__ */ jsx(Dialog.Content, {
3851
- onEscapeKeyDown: keepOpenIfDirty,
3852
- onPointerDownOutside: keepOpenIfDirty,
3853
- "data-rendra": "GAV-001",
3854
- className: cn(panelCls, "data-[state=closed]:animate-out data-[state=closed]:duration-(--rendra-duration-fast) data-[state=closed]:slide-out-to-right data-[state=open]:animate-in data-[state=open]:slide-in-from-right", sizes[size]),
3855
- children: shell
3856
- })] })
3857
- }), /* @__PURE__ */ jsx(Modal, {
3858
- open: confirming,
3859
- onOpenChange: setConfirming,
3860
- type: "destructive",
3861
- title: discardTitle,
3862
- description: discardDescription,
3863
- confirmLabel: "Descartar",
3864
- cancelLabel: "Continuar editando",
3865
- onConfirm: () => onOpenChange(false)
3866
- })] });
3867
- }
3868
- //#endregion
3869
2630
  //#region src/components/ui/switch.tsx
3870
2631
  /** Switch único, com rótulo e descrição à esquerda e a chave à direita. */
3871
2632
  function Switch({ checked, defaultChecked, onCheckedChange, label, description, disabled, id, name, className, ...aria }) {
@@ -3936,7 +2697,7 @@ function Switch({ checked, defaultChecked, onCheckedChange, label, description,
3936
2697
  }
3937
2698
  //#endregion
3938
2699
  //#region src/components/ui/data-toolbar.tsx
3939
- function DataToolbar({ search, filters, filterCount = 0, onClearFilters, chips = [], sort, columns, actions, primaryAction, selectionBar }) {
2700
+ function DataToolbar({ search, filters, filterCount = 0, onClearFilters, chips = [], sort, columns, actions, summary, primaryAction, selectionBar }) {
3940
2701
  const { isMobile } = useBreakpoint();
3941
2702
  const [drawer, setDrawer] = useState(false);
3942
2703
  const hasPanel = Boolean(filters || sort || columns?.length);
@@ -3974,49 +2735,57 @@ function DataToolbar({ search, filters, filterCount = 0, onClearFilters, chips =
3974
2735
  children: selectionBar
3975
2736
  }) : /* @__PURE__ */ jsxs("div", {
3976
2737
  className: "flex flex-col gap-3 md:flex-row md:items-center",
3977
- children: [search && /* @__PURE__ */ jsx(Input, {
3978
- icon: /* @__PURE__ */ jsx(Search, {}),
3979
- clearable: true,
3980
- value: search.value,
3981
- onChange: search.onChange,
3982
- placeholder: search.placeholder ?? "Buscar",
3983
- "aria-label": search.placeholder ?? "Buscar",
3984
- type: "search",
3985
- className: "md:w-3xs lg:w-sm"
3986
- }), isMobile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [(hasPanel || primaryAction) && /* @__PURE__ */ jsxs("div", {
3987
- className: cn("grid gap-3", hasPanel && primaryAction ? "grid-actions-2" : "grid-cols-1"),
3988
- children: [hasPanel && filterButton(() => setDrawer(true)), primaryAction]
3989
- }), actions && /* @__PURE__ */ jsx("div", {
3990
- className: "flex flex-wrap gap-3 *:flex-1",
3991
- children: actions
3992
- })] }) : /* @__PURE__ */ jsxs("div", {
3993
- className: "flex flex-wrap items-center gap-2 md:ml-auto",
3994
- children: [
3995
- actions,
3996
- columnsMenu,
3997
- sort,
3998
- filters && /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
3999
- asChild: true,
4000
- children: filterButton()
4001
- }), /* @__PURE__ */ jsxs(PopoverContent, {
4002
- align: "end",
4003
- className: "flex flex-col",
4004
- children: [/* @__PURE__ */ jsx("div", {
4005
- className: "max-h-command overflow-y-auto p-4",
4006
- children: filters
4007
- }), onClearFilters && filterCount > 0 && /* @__PURE__ */ jsx("div", {
4008
- className: "border-t p-2",
4009
- children: /* @__PURE__ */ jsx(Button, {
4010
- variant: "ghost",
4011
- size: "sm",
4012
- onClick: onClearFilters,
4013
- children: "Limpar filtros"
4014
- })
4015
- })]
4016
- })] }),
4017
- primaryAction
4018
- ]
4019
- })]
2738
+ children: [
2739
+ search && /* @__PURE__ */ jsx(Input, {
2740
+ icon: /* @__PURE__ */ jsx(Search, {}),
2741
+ clearable: true,
2742
+ value: search.value,
2743
+ onChange: search.onChange,
2744
+ placeholder: search.placeholder ?? "Buscar",
2745
+ "aria-label": search.placeholder ?? "Buscar",
2746
+ type: "search",
2747
+ className: "md:w-3xs lg:w-sm"
2748
+ }),
2749
+ summary && /* @__PURE__ */ jsx("div", {
2750
+ "data-toolbar-summary": true,
2751
+ className: "flex flex-col gap-1 text-sm",
2752
+ children: summary
2753
+ }),
2754
+ isMobile ? /* @__PURE__ */ jsxs(Fragment$1, { children: [(hasPanel || primaryAction) && /* @__PURE__ */ jsxs("div", {
2755
+ className: cn("grid gap-3", hasPanel && primaryAction ? "grid-actions-2" : "grid-cols-1"),
2756
+ children: [hasPanel && filterButton(() => setDrawer(true)), primaryAction]
2757
+ }), actions && /* @__PURE__ */ jsx("div", {
2758
+ className: "flex flex-wrap gap-3 *:flex-1",
2759
+ children: actions
2760
+ })] }) : /* @__PURE__ */ jsxs("div", {
2761
+ className: "flex flex-wrap items-center gap-2 md:ml-auto",
2762
+ children: [
2763
+ actions,
2764
+ columnsMenu,
2765
+ sort,
2766
+ filters && /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
2767
+ asChild: true,
2768
+ children: filterButton()
2769
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
2770
+ align: "end",
2771
+ className: "flex flex-col",
2772
+ children: [/* @__PURE__ */ jsx("div", {
2773
+ className: "max-h-command overflow-y-auto p-4",
2774
+ children: filters
2775
+ }), onClearFilters && filterCount > 0 && /* @__PURE__ */ jsx("div", {
2776
+ className: "border-t p-2",
2777
+ children: /* @__PURE__ */ jsx(Button, {
2778
+ variant: "ghost",
2779
+ size: "sm",
2780
+ onClick: onClearFilters,
2781
+ children: "Limpar filtros"
2782
+ })
2783
+ })]
2784
+ })] }),
2785
+ primaryAction
2786
+ ]
2787
+ })
2788
+ ]
4020
2789
  }),
4021
2790
  chips.length > 0 && /* @__PURE__ */ jsxs("div", {
4022
2791
  className: "flex flex-wrap items-center gap-2",
@@ -4087,121 +2856,6 @@ function DataToolbar({ search, filters, filterCount = 0, onClearFilters, chips =
4087
2856
  });
4088
2857
  }
4089
2858
  //#endregion
4090
- //#region src/components/layout/tokens.ts
4091
- var gapClass = {
4092
- "0": "gap-0",
4093
- "1": "gap-1",
4094
- "2": "gap-2",
4095
- "3": "gap-3",
4096
- "4": "gap-4",
4097
- "6": "gap-6",
4098
- "8": "gap-8",
4099
- "12": "gap-12",
4100
- "16": "gap-16",
4101
- "24": "gap-24",
4102
- section: "gap-8 md:gap-12",
4103
- fields: "gap-x-4 gap-y-4 md:gap-x-6"
4104
- };
4105
- /**
4106
- * Grade de formulário, pela largura do próprio bloco (container query), não da tela:
4107
- * 1 coluna estreito, 6 colunas a partir de 28rem, 12 colunas a partir de 48rem.
4108
- * No largo, o padrão é 3 campos por linha (md = 4/12). Nunca 2 por linha por padrão.
4109
- */
4110
- var fieldGridClass = "grid grid-cols-1 @md:grid-cols-6 @3xl:grid-cols-12";
4111
- /** Largura do Field na grade de formulário (no largo, em 12 colunas). */
4112
- var fieldSpanClass = {
4113
- /** 2/12, cerca de 17%: UF, número, DDD. */
4114
- xs: "@md:col-span-2 @3xl:col-span-2",
4115
- /** 3/12 = 25%: CEP, CNPJ, CPF, datas curtas. */
4116
- sm: "@md:col-span-2 @3xl:col-span-3",
4117
- /** 4/12 = 33%: o padrão, 3 campos por linha. */
4118
- md: "@md:col-span-3 @3xl:col-span-4",
4119
- /** 6/12 = 50%. */
4120
- lg: "@md:col-span-3 @3xl:col-span-6",
4121
- /** 8/12 = 67%: nome, logradouro longo. */
4122
- xl: "@md:col-span-6 @3xl:col-span-8",
4123
- /** Linha inteira. */
4124
- full: "@md:col-span-6 @3xl:col-span-12"
4125
- };
4126
- var alignClass = {
4127
- start: "items-start",
4128
- center: "items-center",
4129
- end: "items-end",
4130
- stretch: "items-stretch",
4131
- baseline: "items-baseline"
4132
- };
4133
- var justifyClass = {
4134
- start: "justify-start",
4135
- center: "justify-center",
4136
- end: "justify-end",
4137
- between: "justify-between"
4138
- };
4139
- //#endregion
4140
- //#region src/components/ui/field.tsx
4141
- function Label({ className, required, children, ...props }) {
4142
- return /* @__PURE__ */ jsxs(Label$1.Root, {
4143
- "data-rendra": "FLD-002",
4144
- className: cn("text-label text-label-foreground label-case", className),
4145
- ...props,
4146
- children: [
4147
- children,
4148
- required && /* @__PURE__ */ jsxs("span", {
4149
- className: "text-destructive",
4150
- "aria-hidden": true,
4151
- children: [" ", "*"]
4152
- }),
4153
- required && /* @__PURE__ */ jsx("span", {
4154
- className: "sr-only",
4155
- children: " (obrigatório)"
4156
- })
4157
- ]
4158
- });
4159
- }
4160
- var FieldContext = createContext(null);
4161
- /** Liga um controle ao Field mais próximo (id, aria-describedby, aria-invalid). */
4162
- function useFieldControl() {
4163
- return useContext(FieldContext);
4164
- }
4165
- function Field({ label, required, help, error, span = "md", newRow, reserveMessage = false, id, className, children }) {
4166
- const auto = useId();
4167
- const controlId = id ?? `campo${auto}`;
4168
- const messageId = `${controlId}-mensagem`;
4169
- const invalid = Boolean(error);
4170
- const message = error ?? help;
4171
- const child = isValidElement(children) && typeof children.type !== "string" ? cloneElement(children, {
4172
- id: children.props.id ?? controlId,
4173
- ...invalid ? { invalid: children.props.invalid ?? true } : {},
4174
- "aria-describedby": message ? messageId : void 0,
4175
- "aria-required": required || void 0
4176
- }) : children;
4177
- return /* @__PURE__ */ jsx(FieldContext.Provider, {
4178
- value: {
4179
- id: controlId,
4180
- describedBy: message ? messageId : "",
4181
- invalid
4182
- },
4183
- children: /* @__PURE__ */ jsxs("div", {
4184
- "data-slot": "field",
4185
- "data-rendra": "FLD-001",
4186
- className: cn("flex min-w-0 flex-col gap-2", fieldSpanClass[span === "half" ? "md" : span], newRow && "@md:col-start-1 @3xl:col-start-1", className),
4187
- children: [
4188
- label && /* @__PURE__ */ jsx(Label, {
4189
- htmlFor: controlId,
4190
- required,
4191
- children: label
4192
- }),
4193
- child,
4194
- (reserveMessage || message) && /* @__PURE__ */ jsx("p", {
4195
- id: messageId,
4196
- role: invalid ? "alert" : void 0,
4197
- className: cn("-mt-1 min-h-4 text-help", invalid ? "font-medium text-destructive" : "text-help-foreground"),
4198
- children: message
4199
- })
4200
- ]
4201
- })
4202
- });
4203
- }
4204
- //#endregion
4205
2859
  //#region src/components/ui/date-picker.tsx
4206
2860
  var fmtDate = (d) => format(d, "dd/MM/yyyy");
4207
2861
  var fmtTime = (d) => format(d, "HH:mm");
@@ -4584,318 +3238,6 @@ function Slider({ value, defaultValue = [50], onChange, min = 0, max = 100, step
4584
3238
  });
4585
3239
  }
4586
3240
  //#endregion
4587
- //#region src/components/ui/select.tsx
4588
- /** Acima deste número de opções, o "Selecionar todos" aparece sem a prop `selectAll`. */
4589
- var SELECT_ALL_AUTO_MIN = 5;
4590
- /**
4591
- * Select único do sistema. Busca, múltiplo, selecionar todos, contador, criar opção,
4592
- * busca remota e carregamento são props. No mobile abre como bottom sheet.
4593
- */
4594
- var Select = forwardRef(function Select(props, ref) {
4595
- const { options: staticOptions = [], placeholder = "Selecione", label, size, invalid, disabled, searchable, creatable, onCreate, loadOptions, loading, clearable, emptyText = "Nenhuma opção encontrada.", id, className } = props;
4596
- const multiple = props.multiple === true;
4597
- const { isMobile } = useBreakpoint();
4598
- const [open, setOpen] = useState(false);
4599
- const listId = `${useId()}-lista`;
4600
- const [query, setQuery] = useState("");
4601
- const [remote, setRemote] = useState(null);
4602
- const [remoteLoading, setRemoteLoading] = useState(false);
4603
- const [created, setCreated] = useState([]);
4604
- const [innerSingle, setInnerSingle] = useState(null);
4605
- const [innerMulti, setInnerMulti] = useState([]);
4606
- const [draft, setDraft] = useState([]);
4607
- const selectedSingle = multiple ? null : props.value ?? innerSingle;
4608
- const selectedMulti = multiple ? props.value ?? innerMulti : [];
4609
- const seq = useRef(0);
4610
- useEffect(() => {
4611
- if (!loadOptions || !open) return;
4612
- const n = ++seq.current;
4613
- setRemoteLoading(true);
4614
- const t = window.setTimeout(async () => {
4615
- const res = await loadOptions(query);
4616
- if (n === seq.current) {
4617
- setRemote(res);
4618
- setRemoteLoading(false);
4619
- }
4620
- }, 250);
4621
- return () => window.clearTimeout(t);
4622
- }, [
4623
- loadOptions,
4624
- query,
4625
- open
4626
- ]);
4627
- const all = useMemo(() => {
4628
- const base = remote ?? staticOptions;
4629
- const map = /* @__PURE__ */ new Map();
4630
- [...base, ...created].forEach((o) => map.set(o.value, o));
4631
- return [...map.values()];
4632
- }, [
4633
- remote,
4634
- staticOptions,
4635
- created
4636
- ]);
4637
- const byValue = (v) => all.find((o) => o.value === v);
4638
- const isSearchable = searchable || creatable || Boolean(loadOptions);
4639
- const busy = loading || remoteLoading;
4640
- const multiValue = isMobile && multiple ? draft : selectedMulti;
4641
- const commitSingle = (v) => {
4642
- setInnerSingle(v);
4643
- props.onChange?.(v);
4644
- };
4645
- const commitMulti = (v) => {
4646
- setInnerMulti(v);
4647
- props.onChange?.(v);
4648
- };
4649
- const toggle = (v) => {
4650
- if (!multiple) {
4651
- commitSingle(v);
4652
- setOpen(false);
4653
- return;
4654
- }
4655
- const next = multiValue.includes(v) ? multiValue.filter((x) => x !== v) : [...multiValue, v];
4656
- if (isMobile) setDraft(next);
4657
- else commitMulti(next);
4658
- };
4659
- const enabled = all.filter((o) => !o.disabled);
4660
- const allSelected = enabled.length > 0 && enabled.every((o) => multiValue.includes(o.value));
4661
- const showSelectAll = multiple && (props.selectAll ?? all.length > SELECT_ALL_AUTO_MIN);
4662
- const toggleAll = () => {
4663
- const next = allSelected ? [] : enabled.map((o) => o.value);
4664
- if (isMobile) setDraft(next);
4665
- else commitMulti(next);
4666
- };
4667
- const create = async () => {
4668
- const text = query.trim();
4669
- if (!text) return;
4670
- const opt = onCreate ? await onCreate(text) : {
4671
- value: text,
4672
- label: text
4673
- };
4674
- setCreated((c) => [...c, opt]);
4675
- setQuery("");
4676
- toggle(opt.value);
4677
- };
4678
- const onOpenChange = (o) => {
4679
- setOpen(o);
4680
- if (o) setDraft(selectedMulti);
4681
- else setQuery("");
4682
- };
4683
- const hasValue = multiple ? selectedMulti.length > 0 : selectedSingle != null;
4684
- const maxChips = props.maxChips ?? 2;
4685
- let display = /* @__PURE__ */ jsx("span", {
4686
- className: "truncate text-muted-foreground",
4687
- children: placeholder
4688
- });
4689
- if (!multiple && selectedSingle != null) {
4690
- const o = byValue(selectedSingle);
4691
- display = /* @__PURE__ */ jsxs("span", {
4692
- className: "flex min-w-0 items-center gap-2",
4693
- children: [o?.icon, /* @__PURE__ */ jsx("span", {
4694
- className: "truncate",
4695
- children: o?.label ?? selectedSingle
4696
- })]
4697
- });
4698
- } else if (multiple && selectedMulti.length > 0) display = props.showCount ? /* @__PURE__ */ jsxs("span", {
4699
- className: "truncate",
4700
- children: [
4701
- selectedMulti.length,
4702
- " ",
4703
- selectedMulti.length === 1 ? "selecionado" : "selecionados"
4704
- ]
4705
- }) : /* @__PURE__ */ jsxs("span", {
4706
- className: "flex min-w-0 items-center gap-1 overflow-hidden",
4707
- children: [selectedMulti.slice(0, maxChips).map((v) => /* @__PURE__ */ jsx("span", {
4708
- className: "max-w-3xs shrink-0 truncate rounded-item bg-primary-soft px-2 py-1 text-xs leading-none font-medium text-primary-soft-foreground",
4709
- children: byValue(v)?.label ?? v
4710
- }, v)), selectedMulti.length > maxChips && /* @__PURE__ */ jsxs("span", {
4711
- className: "shrink-0 rounded-item bg-muted px-2 py-1 text-xs leading-none font-medium",
4712
- children: ["+", selectedMulti.length - maxChips]
4713
- })]
4714
- });
4715
- const showClear = clearable && hasValue && !disabled;
4716
- const clear = showClear ? /* @__PURE__ */ jsx("button", {
4717
- type: "button",
4718
- "aria-label": "Limpar seleção",
4719
- className: cn(controlAdornmentButton, "absolute top-1/2 right-8 mr-0 -translate-y-1/2 md:mr-0"),
4720
- onClick: () => multiple ? commitMulti([]) : commitSingle(null),
4721
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
4722
- }) : void 0;
4723
- const trigger = /* @__PURE__ */ jsxs("button", {
4724
- ref,
4725
- id,
4726
- type: "button",
4727
- role: "combobox",
4728
- "aria-expanded": open,
4729
- "aria-controls": listId,
4730
- "aria-haspopup": "listbox",
4731
- "aria-invalid": invalid || void 0,
4732
- "aria-describedby": props["aria-describedby"],
4733
- "aria-label": id ? void 0 : label,
4734
- disabled,
4735
- "data-rendra": "SEL-001",
4736
- onKeyDown: (e) => e.stopPropagation(),
4737
- className: cn(controlFrame({
4738
- size,
4739
- invalid
4740
- }), "cursor-pointer text-left disabled:cursor-not-allowed disabled:opacity-60", className),
4741
- children: [
4742
- /* @__PURE__ */ jsx("span", {
4743
- className: "flex min-w-0 flex-1 items-center",
4744
- children: display
4745
- }),
4746
- showClear && /* @__PURE__ */ jsx("span", {
4747
- "aria-hidden": true,
4748
- className: cn("size-control-sm shrink-0 md:size-8", "-mr-1")
4749
- }),
4750
- /* @__PURE__ */ jsx(ChevronDown, {
4751
- "aria-hidden": true,
4752
- className: cn("size-icon-sm shrink-0 text-muted-foreground transition-transform duration-(--rendra-duration-base) ease-move", open && "rotate-180")
4753
- })
4754
- ]
4755
- });
4756
- const groups = all.reduce((acc, o) => {
4757
- (acc[o.group ?? ""] ??= []).push(o);
4758
- return acc;
4759
- }, {});
4760
- const q = query.trim().toLowerCase();
4761
- const showCreate = creatable && q && !all.some((o) => o.label.toLowerCase() === q);
4762
- const itemClass = "relative flex min-h-touch cursor-pointer items-center gap-3 rounded-item px-3 text-sm outline-none select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-[selected=true]:bg-accent md:min-h-0 md:py-2";
4763
- const checkbox = (checked) => /* @__PURE__ */ jsx("span", {
4764
- "aria-hidden": true,
4765
- className: cn("flex size-icon-sm shrink-0 items-center justify-center rounded-item border transition-colors", checked ? "border-primary bg-primary text-primary-foreground" : "border-input bg-card"),
4766
- children: checked && /* @__PURE__ */ jsx(Check, {
4767
- className: "size-3",
4768
- strokeWidth: 3
4769
- })
4770
- });
4771
- const list = /* @__PURE__ */ jsxs(Command.List, {
4772
- id: listId,
4773
- "aria-label": label ?? placeholder,
4774
- children: [
4775
- busy && /* @__PURE__ */ jsx(Command.Loading, { children: /* @__PURE__ */ jsxs("div", {
4776
- className: "flex items-center gap-2 px-3 py-4 text-sm text-muted-foreground",
4777
- children: [/* @__PURE__ */ jsx(Spinner, { size: "sm" }), "Carregando opções..."]
4778
- }) }),
4779
- !busy && /* @__PURE__ */ jsx(Command.Empty, {
4780
- className: "px-3 py-6 text-center text-sm text-muted-foreground",
4781
- children: emptyText
4782
- }),
4783
- showCreate && /* @__PURE__ */ jsxs(Command.Item, {
4784
- value: `__criar__${query}`,
4785
- onSelect: create,
4786
- className: itemClass,
4787
- children: [/* @__PURE__ */ jsx(Plus, {
4788
- className: "size-icon-sm shrink-0 text-primary-text",
4789
- "aria-hidden": true
4790
- }), /* @__PURE__ */ jsxs("span", {
4791
- className: "truncate",
4792
- children: ["Criar ", /* @__PURE__ */ jsxs("strong", {
4793
- className: "font-semibold",
4794
- children: [
4795
- "“",
4796
- query.trim(),
4797
- "”"
4798
- ]
4799
- })]
4800
- })]
4801
- }),
4802
- showSelectAll && !q && enabled.length > 0 && /* @__PURE__ */ jsxs(Command.Item, {
4803
- value: "__todos__",
4804
- onSelect: toggleAll,
4805
- className: cn(itemClass, "font-medium"),
4806
- children: [checkbox(allSelected), allSelected ? "Desmarcar todos" : "Selecionar todos"]
4807
- }),
4808
- Object.entries(groups).map(([group, opts]) => /* @__PURE__ */ jsx(Command.Group, {
4809
- heading: group || void 0,
4810
- className: "[&_[cmdk-group-heading]]:px-3 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
4811
- children: opts.map((o) => {
4812
- const checked = multiple ? multiValue.includes(o.value) : selectedSingle === o.value;
4813
- return /* @__PURE__ */ jsxs(Command.Item, {
4814
- value: `${o.label} ${o.description ?? ""} ${o.value}`,
4815
- disabled: o.disabled,
4816
- onSelect: () => toggle(o.value),
4817
- "aria-selected": checked,
4818
- className: itemClass,
4819
- children: [
4820
- multiple && checkbox(checked),
4821
- o.icon && /* @__PURE__ */ jsx("span", {
4822
- className: "flex shrink-0 [&_svg]:size-icon-sm",
4823
- children: o.icon
4824
- }),
4825
- /* @__PURE__ */ jsxs("span", {
4826
- className: "flex min-w-0 flex-1 flex-col",
4827
- children: [/* @__PURE__ */ jsx("span", {
4828
- className: cn("truncate", checked && !multiple && "font-medium"),
4829
- children: o.label
4830
- }), o.description && /* @__PURE__ */ jsx("span", {
4831
- className: "truncate text-xs text-muted-foreground",
4832
- children: o.description
4833
- })]
4834
- }),
4835
- !multiple && checked && /* @__PURE__ */ jsx(Check, {
4836
- className: "size-icon-sm shrink-0 text-primary-text",
4837
- "aria-hidden": true
4838
- })
4839
- ]
4840
- }, o.value);
4841
- })
4842
- }, group || "sem-grupo"))
4843
- ]
4844
- });
4845
- const search = isSearchable ? /* @__PURE__ */ jsxs("div", {
4846
- className: cn(controlFrame({ size: "md" }), "bg-background"),
4847
- children: [/* @__PURE__ */ jsx(Search, {
4848
- className: "size-icon-sm shrink-0 text-muted-foreground",
4849
- "aria-hidden": true
4850
- }), /* @__PURE__ */ jsx(Command.Input, {
4851
- value: query,
4852
- onValueChange: setQuery,
4853
- placeholder: creatable ? "Buscar ou criar..." : "Buscar...",
4854
- className: "h-full min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground"
4855
- })]
4856
- }) : null;
4857
- const count = multiValue.length;
4858
- const footer = multiple && (isMobile || count > 0) ? /* @__PURE__ */ jsxs("div", {
4859
- className: "grid grid-actions-2 gap-3",
4860
- children: [/* @__PURE__ */ jsx(Button, {
4861
- variant: "outline",
4862
- onClick: () => isMobile ? setDraft([]) : commitMulti([]),
4863
- disabled: count === 0,
4864
- children: "Limpar"
4865
- }), isMobile ? /* @__PURE__ */ jsxs(Button, {
4866
- onClick: () => {
4867
- commitMulti(draft);
4868
- setOpen(false);
4869
- },
4870
- children: ["Aplicar", count ? ` (${count})` : ""]
4871
- }) : /* @__PURE__ */ jsxs(Button, {
4872
- variant: "secondary",
4873
- onClick: () => setOpen(false),
4874
- children: [
4875
- "Pronto (",
4876
- count,
4877
- ")"
4878
- ]
4879
- })]
4880
- }) : void 0;
4881
- return /* @__PURE__ */ jsx(Command, {
4882
- shouldFilter: !loadOptions,
4883
- loop: true,
4884
- className: "contents",
4885
- label: label ?? placeholder,
4886
- children: /* @__PURE__ */ jsx(PickerPanel, {
4887
- open,
4888
- onOpenChange,
4889
- trigger,
4890
- adornment: clear,
4891
- title: label ?? placeholder,
4892
- header: search,
4893
- footer,
4894
- children: list
4895
- })
4896
- });
4897
- });
4898
- //#endregion
4899
3241
  //#region src/components/ui/tabs.tsx
4900
3242
  /** Contador de instâncias, só para o `layoutId` do indicador (ver `indicatorId`). */
4901
3243
  var tabsCreated = 0;
@@ -5458,33 +3800,6 @@ function ImageViewer({ images, index, onIndexChange }) {
5458
3800
  });
5459
3801
  }
5460
3802
  //#endregion
5461
- //#region src/hooks/use-fill-height.ts
5462
- /**
5463
- * Mede o espaço que sobra na área de conteúdo (o <main>) abaixo do elemento e grava em
5464
- * --board-h. Com a classe h-board, o elemento ocupa exatamente essa altura: a página não
5465
- * rola e a rolagem acontece dentro dele (kanban, chat).
5466
- */
5467
- function useFillHeight() {
5468
- const ref = useRef(null);
5469
- useLayoutEffect(() => {
5470
- const el = ref.current;
5471
- if (!el) return;
5472
- const main = el.closest("main");
5473
- const measure = () => {
5474
- const h = el.getBoundingClientRect().height;
5475
- const page = main?.firstElementChild;
5476
- const room = main && page ? main.clientHeight - (page.getBoundingClientRect().height - h) : window.innerHeight - el.getBoundingClientRect().top;
5477
- el.style.setProperty("--board-h", `${Math.max(320, Math.floor(room))}px`);
5478
- };
5479
- measure();
5480
- const ro = new ResizeObserver(measure);
5481
- ro.observe(document.documentElement);
5482
- if (main) ro.observe(main);
5483
- return () => ro.disconnect();
5484
- }, []);
5485
- return ref;
5486
- }
5487
- //#endregion
5488
3803
  //#region src/components/ui/kanban.tsx
5489
3804
  /** Marca o fim da lista: ao aparecer na rolagem da etapa, pede mais cards. */
5490
3805
  function LoadMore({ onVisible }) {
@@ -5527,10 +3842,239 @@ var targetIconTone = {
5527
3842
  info: "text-info",
5528
3843
  outline: "text-foreground"
5529
3844
  };
5530
- function CardBody({ card, valueFields = [], formatValue, withHandle }) {
5531
- const shown = valueFields.slice(0, 2);
3845
+ /** Fundo claro de cada destino do rodapé flutuante, pela cor da ação. */
3846
+ var targetSoft = {
3847
+ neutral: "bg-muted",
3848
+ primary: "bg-primary-soft",
3849
+ success: "bg-success-soft",
3850
+ warning: "bg-warning-soft",
3851
+ error: "bg-destructive-soft",
3852
+ info: "bg-info-soft",
3853
+ outline: "bg-card"
3854
+ };
3855
+ /** Destino realçado (card por cima): fundo forte com o `-foreground` dele em tudo. */
3856
+ var targetStrong = {
3857
+ neutral: "bg-muted text-foreground",
3858
+ primary: "bg-primary text-primary-foreground",
3859
+ success: "bg-success text-success-foreground",
3860
+ warning: "bg-warning text-warning-foreground",
3861
+ error: "bg-destructive text-destructive-foreground",
3862
+ info: "bg-info text-info-foreground",
3863
+ outline: "bg-muted text-foreground"
3864
+ };
3865
+ /**
3866
+ * Situação do card a partir da próxima atividade e da cadência da etapa. Compara por dia de
3867
+ * calendário; `referenceDate` é o "hoje" (injetável para teste).
3868
+ */
3869
+ function getKanbanCardStatus(card, column, referenceDate) {
3870
+ const daysInColumn = card.enteredColumnAt ? differenceInCalendarDays(referenceDate, card.enteredColumnAt) : null;
3871
+ const cadenceOverdue = column?.cadenceDays !== void 0 && daysInColumn !== null && daysInColumn > column.cadenceDays;
3872
+ const ahead = card.activity ? differenceInCalendarDays(card.activity.at, referenceDate) : null;
3873
+ const activity = ahead === null ? "none" : ahead < 0 ? "late" : ahead === 0 ? "today" : "onTime";
3874
+ return {
3875
+ background: cadenceOverdue && (activity === "late" || activity === "none") ? "critical" : cadenceOverdue ? "cadenceOverdue" : activity === "none" ? "noActivity" : "normal",
3876
+ activity,
3877
+ daysInColumn,
3878
+ daysLate: ahead !== null && ahead < 0 ? -ahead : 0
3879
+ };
3880
+ }
3881
+ /** Classes literais completas (nada de classe montada por string). */
3882
+ var statusBackground = {
3883
+ critical: "bg-critical-soft",
3884
+ cadenceOverdue: "bg-destructive-soft",
3885
+ noActivity: "bg-warning-soft",
3886
+ normal: "bg-card"
3887
+ };
3888
+ var activityIcon = {
3889
+ onTime: {
3890
+ icon: CalendarDays,
3891
+ circle: "bg-info-soft text-info-soft-foreground"
3892
+ },
3893
+ today: {
3894
+ icon: CalendarClock,
3895
+ circle: "bg-warning-mid text-warning-mid-foreground"
3896
+ },
3897
+ late: {
3898
+ icon: CalendarX2,
3899
+ circle: "bg-destructive text-destructive-foreground"
3900
+ },
3901
+ none: {
3902
+ icon: CalendarDays,
3903
+ circle: "border border-dashed border-muted-foreground text-muted-foreground"
3904
+ }
3905
+ };
3906
+ function plural(n, one, many) {
3907
+ return `${n} ${n === 1 ? one : many}`;
3908
+ }
3909
+ function situationText(status) {
3910
+ if (status.activity === "onTime") return "Em dia";
3911
+ if (status.activity === "today") return "Vence hoje";
3912
+ if (status.activity === "late") return `Atrasada há ${plural(status.daysLate, "dia", "dias")}`;
3913
+ return "Sem atividade agendada";
3914
+ }
3915
+ /** Texto para o leitor de tela: a cor do fundo nunca é a única informação. */
3916
+ function backgroundLabel(status) {
3917
+ if (status.background === "critical") return `Situação: crítico, cadência vencida e ${status.activity === "late" ? "atividade atrasada" : "sem próxima atividade"}`;
3918
+ if (status.background === "cadenceOverdue") return "Situação: cadência vencida";
3919
+ if (status.background === "noActivity") return "Situação: sem atividade";
3920
+ return null;
3921
+ }
3922
+ function whenText(at, status) {
3923
+ return status.activity === "today" ? `Hoje às ${format(at, "HH:mm")}` : `${format(at, "dd/MM/yyyy")} às ${format(at, "HH:mm")}`;
3924
+ }
3925
+ var baseFields = [
3926
+ {
3927
+ id: "tags",
3928
+ label: "Tags"
3929
+ },
3930
+ {
3931
+ id: "document",
3932
+ label: "CNPJ ou CPF"
3933
+ },
3934
+ {
3935
+ id: "description",
3936
+ label: "Descrição"
3937
+ },
3938
+ {
3939
+ id: "contact",
3940
+ label: "Contato"
3941
+ },
3942
+ {
3943
+ id: "phone",
3944
+ label: "Telefone"
3945
+ },
3946
+ {
3947
+ id: "email",
3948
+ label: "E-mail"
3949
+ },
3950
+ {
3951
+ id: "activity",
3952
+ label: "Atividade"
3953
+ },
3954
+ {
3955
+ id: "assignee",
3956
+ label: "Responsável"
3957
+ }
3958
+ ];
3959
+ /** Lê os campos escondidos; qualquer falha do armazenamento cai no padrão (tudo visível). */
3960
+ function readHidden(storageKey) {
3961
+ if (!storageKey) return {};
3962
+ try {
3963
+ const raw = localStorage.getItem(storageKey);
3964
+ const list = raw ? JSON.parse(raw) : [];
3965
+ return Array.isArray(list) ? Object.fromEntries(list.filter((f) => typeof f === "string").map((f) => [f, true])) : {};
3966
+ } catch {
3967
+ return {};
3968
+ }
3969
+ }
3970
+ function writeHidden(storageKey, hidden) {
3971
+ if (!storageKey) return;
3972
+ try {
3973
+ localStorage.setItem(storageKey, JSON.stringify(Object.keys(hidden).filter((k) => hidden[k])));
3974
+ } catch {}
3975
+ }
3976
+ var HINT_CLOSE_DELAY = 150;
3977
+ /**
3978
+ * Calendário do card: ícone de situação, data ao lado e hint (tipo, quando, com quem,
3979
+ * responsável e situação). Abre por clique, toque, foco por teclado e mouse; o gatilho é um
3980
+ * botão fora do botão de clique do card.
3981
+ */
3982
+ function ActivityHint({ card, status }) {
3983
+ const [open, setOpen] = useState(false);
3984
+ const pressed = useRef(false);
3985
+ const holding = useRef(false);
3986
+ const triggerRef = useRef(null);
3987
+ const lastPointer = useRef("");
3988
+ const timer = useRef(void 0);
3989
+ const { icon: Icon, circle } = activityIcon[status.activity];
3990
+ const activity = card.activity;
3991
+ const visible = activity ? status.activity === "today" ? `Hoje ${format(activity.at, "HH:mm")}` : format(activity.at, "dd/MM/yyyy") : "Sem atividade";
3992
+ const cancelClose = () => clearTimeout(timer.current);
3993
+ const scheduleClose = () => {
3994
+ cancelClose();
3995
+ timer.current = setTimeout(() => {
3996
+ if (!holding.current) setOpen(false);
3997
+ }, HINT_CLOSE_DELAY);
3998
+ };
3999
+ useEffect(() => cancelClose, []);
4000
+ return /* @__PURE__ */ jsxs(Popover, {
4001
+ open,
4002
+ onOpenChange: (next) => !next && !holding.current && setOpen(false),
4003
+ children: [/* @__PURE__ */ jsx(PopoverAnchor, {
4004
+ asChild: true,
4005
+ children: /* @__PURE__ */ jsxs("button", {
4006
+ ref: triggerRef,
4007
+ type: "button",
4008
+ "aria-haspopup": "dialog",
4009
+ "aria-expanded": open,
4010
+ "aria-label": `${visible}. ${situationText(status)}`,
4011
+ className: "relative z-10 -ml-1 inline-flex cursor-pointer items-center gap-1 rounded-item px-1 tabular-nums outline-none focus-visible:ring-2 focus-visible:ring-ring max-md:min-h-touch",
4012
+ onPointerDown: (e) => {
4013
+ cancelClose();
4014
+ pressed.current = true;
4015
+ holding.current = true;
4016
+ window.addEventListener("pointerup", () => holding.current = false, { once: true });
4017
+ window.addEventListener("pointercancel", () => holding.current = false, { once: true });
4018
+ lastPointer.current = e.pointerType;
4019
+ },
4020
+ onPointerEnter: (e) => {
4021
+ if (e.pointerType !== "mouse") return;
4022
+ cancelClose();
4023
+ setOpen(true);
4024
+ },
4025
+ onPointerLeave: (e) => e.pointerType === "mouse" && scheduleClose(),
4026
+ onFocus: () => {
4027
+ if (!pressed.current) setOpen(true);
4028
+ },
4029
+ onBlur: () => {
4030
+ pressed.current = false;
4031
+ if (!holding.current) setOpen(false);
4032
+ },
4033
+ onClick: (e) => {
4034
+ e.preventDefault();
4035
+ e.stopPropagation();
4036
+ pressed.current = false;
4037
+ const byMouse = lastPointer.current === "mouse";
4038
+ lastPointer.current = "";
4039
+ setOpen((o) => byMouse || !o);
4040
+ },
4041
+ children: [/* @__PURE__ */ jsx("span", {
4042
+ "aria-hidden": true,
4043
+ className: cn("flex size-6 shrink-0 items-center justify-center rounded-full", circle),
4044
+ children: /* @__PURE__ */ jsx(Icon, { className: "size-3" })
4045
+ }), visible]
4046
+ })
4047
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
4048
+ width: "sm",
4049
+ align: "start",
4050
+ className: "flex flex-col gap-1 p-3 text-xs",
4051
+ onOpenAutoFocus: (e) => e.preventDefault(),
4052
+ onInteractOutside: (e) => {
4053
+ if (triggerRef.current?.contains(e.target)) e.preventDefault();
4054
+ },
4055
+ onPointerEnter: cancelClose,
4056
+ onPointerLeave: scheduleClose,
4057
+ children: [
4058
+ activity ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("p", {
4059
+ className: "text-sm font-medium",
4060
+ children: [
4061
+ activity.type,
4062
+ " · ",
4063
+ whenText(activity.at, status)
4064
+ ]
4065
+ }), activity.with && /* @__PURE__ */ jsxs("p", { children: ["Com: ", activity.with] })] }) : null,
4066
+ card.assignee && /* @__PURE__ */ jsxs("p", { children: ["Responsável: ", card.assignee] }),
4067
+ /* @__PURE__ */ jsx("p", {
4068
+ className: "font-medium",
4069
+ children: situationText(status)
4070
+ })
4071
+ ]
4072
+ })]
4073
+ });
4074
+ }
4075
+ function CardBody({ card, show, withHandle }) {
5532
4076
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
5533
- card.tags && card.tags.length > 0 && /* @__PURE__ */ jsx("span", {
4077
+ show("tags") && card.tags && card.tags.length > 0 && /* @__PURE__ */ jsx("span", {
5534
4078
  className: "flex flex-wrap gap-1",
5535
4079
  children: card.tags.map((t) => /* @__PURE__ */ jsx(Badge, {
5536
4080
  tone: t.tone ?? "neutral",
@@ -5542,19 +4086,19 @@ function CardBody({ card, valueFields = [], formatValue, withHandle }) {
5542
4086
  children: [/* @__PURE__ */ jsx("span", {
5543
4087
  className: "line-clamp-2 text-sm font-medium",
5544
4088
  children: card.title
5545
- }), card.subtitle && /* @__PURE__ */ jsx("span", {
4089
+ }), show("document") && card.subtitle && /* @__PURE__ */ jsx("span", {
5546
4090
  className: "text-xs text-muted-foreground tabular-nums",
5547
4091
  children: card.subtitle
5548
4092
  })]
5549
4093
  }),
5550
- card.description && /* @__PURE__ */ jsx("span", {
4094
+ show("description") && card.description && /* @__PURE__ */ jsx("span", {
5551
4095
  className: "line-clamp-2 text-xs text-muted-foreground",
5552
4096
  children: card.description
5553
4097
  }),
5554
4098
  card.contact && /* @__PURE__ */ jsxs("span", {
5555
4099
  className: "flex flex-col gap-1 text-xs text-muted-foreground",
5556
4100
  children: [
5557
- card.contact.name && /* @__PURE__ */ jsxs("span", {
4101
+ show("contact") && card.contact.name && /* @__PURE__ */ jsxs("span", {
5558
4102
  className: "flex min-w-0 items-center gap-1 text-foreground",
5559
4103
  children: [/* @__PURE__ */ jsx(User, {
5560
4104
  className: "size-3 shrink-0",
@@ -5564,7 +4108,7 @@ function CardBody({ card, valueFields = [], formatValue, withHandle }) {
5564
4108
  children: card.contact.name
5565
4109
  })]
5566
4110
  }),
5567
- card.contact.phone && /* @__PURE__ */ jsxs("span", {
4111
+ show("phone") && card.contact.phone && /* @__PURE__ */ jsxs("span", {
5568
4112
  className: "flex min-w-0 items-center gap-1 tabular-nums",
5569
4113
  children: [/* @__PURE__ */ jsx(Phone, {
5570
4114
  className: "size-3 shrink-0",
@@ -5574,7 +4118,7 @@ function CardBody({ card, valueFields = [], formatValue, withHandle }) {
5574
4118
  children: card.contact.phone
5575
4119
  })]
5576
4120
  }),
5577
- card.contact.email && /* @__PURE__ */ jsxs("span", {
4121
+ show("email") && card.contact.email && /* @__PURE__ */ jsxs("span", {
5578
4122
  className: "flex min-w-0 items-center gap-1",
5579
4123
  children: [/* @__PURE__ */ jsx(Mail, {
5580
4124
  className: "size-3 shrink-0",
@@ -5585,50 +4129,96 @@ function CardBody({ card, valueFields = [], formatValue, withHandle }) {
5585
4129
  })]
5586
4130
  })
5587
4131
  ]
5588
- }),
5589
- shown.length > 0 && /* @__PURE__ */ jsx("span", {
5590
- className: "grid grid-cols-2 divide-x rounded-block bg-muted text-xs",
5591
- children: shown.map((f) => /* @__PURE__ */ jsxs("span", {
5592
- className: "flex min-w-0 flex-col px-2 py-1",
5593
- children: [/* @__PURE__ */ jsxs("span", {
5594
- className: "text-muted-foreground",
5595
- children: [f.label, ":"]
5596
- }), /* @__PURE__ */ jsx("span", {
5597
- className: "truncate font-semibold text-foreground tabular-nums",
5598
- children: formatValue(card.values?.[f.key] ?? 0)
5599
- })]
5600
- }, f.key))
5601
- }),
5602
- (card.assignee || card.dueDate || card.meta) && /* @__PURE__ */ jsxs("span", {
5603
- className: "flex items-center gap-2 border-t pt-2 text-xs text-muted-foreground",
5604
- children: [
5605
- card.dueDate && /* @__PURE__ */ jsxs("span", {
5606
- className: "inline-flex items-center gap-1 tabular-nums",
5607
- children: [/* @__PURE__ */ jsx(CalendarDays, {
5608
- className: "size-3",
5609
- "aria-hidden": true
5610
- }), format(card.dueDate, "dd/MM/yyyy")]
5611
- }),
5612
- card.meta && /* @__PURE__ */ jsx("span", {
5613
- className: "font-medium text-foreground tabular-nums",
5614
- children: card.meta
5615
- }),
5616
- card.assignee && /* @__PURE__ */ jsx("span", {
5617
- className: "ml-auto",
5618
- title: card.assignee,
5619
- children: /* @__PURE__ */ jsx(Avatar, {
5620
- name: card.assignee,
5621
- size: "sm"
5622
- })
5623
- })
5624
- ]
5625
4132
  })
5626
4133
  ] });
5627
4134
  }
4135
+ /**
4136
+ * Rodapé do card, fora do botão de clique: linha de calendário, meta e responsável e, depois
4137
+ * de uma divisória, os valores empilhados. Sem `renderCard` apenas.
4138
+ */
4139
+ function cardFooter({ card, status, show, valueFields, formatValue }) {
4140
+ const calendar = show("activity") ? status ? /* @__PURE__ */ jsx(ActivityHint, {
4141
+ card,
4142
+ status
4143
+ }) : card.dueDate && /* @__PURE__ */ jsxs("span", {
4144
+ className: "inline-flex items-center gap-1 tabular-nums",
4145
+ children: [/* @__PURE__ */ jsx(CalendarDays, {
4146
+ className: "size-3",
4147
+ "aria-hidden": true
4148
+ }), format(card.dueDate, "dd/MM/yyyy")]
4149
+ }) : null;
4150
+ const assignee = show("assignee") ? card.assignee : void 0;
4151
+ const values = valueFields.slice(0, 2).filter((f) => show(`value:${f.key}`));
4152
+ const hasRow = Boolean(calendar || assignee || card.meta);
4153
+ if (!hasRow && values.length === 0) return null;
4154
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [hasRow && /* @__PURE__ */ jsxs("span", {
4155
+ className: "flex items-center gap-2 border-t pt-2 text-xs text-muted-foreground",
4156
+ children: [
4157
+ calendar,
4158
+ card.meta && /* @__PURE__ */ jsx("span", {
4159
+ className: "font-medium text-foreground tabular-nums",
4160
+ children: card.meta
4161
+ }),
4162
+ assignee && /* @__PURE__ */ jsx("span", {
4163
+ className: "ml-auto",
4164
+ title: assignee,
4165
+ children: /* @__PURE__ */ jsx(Avatar, {
4166
+ name: assignee,
4167
+ size: "sm"
4168
+ })
4169
+ })
4170
+ ]
4171
+ }), values.length > 0 && /* @__PURE__ */ jsx("span", {
4172
+ className: "flex flex-col gap-1 border-t pt-2 text-xs",
4173
+ children: values.map((f) => /* @__PURE__ */ jsxs("span", {
4174
+ className: "flex items-baseline justify-between gap-2",
4175
+ children: [/* @__PURE__ */ jsxs("span", {
4176
+ className: "text-muted-foreground",
4177
+ children: [f.label, ":"]
4178
+ }), /* @__PURE__ */ jsx("span", {
4179
+ className: "truncate font-semibold text-foreground tabular-nums",
4180
+ children: formatValue(card.values?.[f.key] ?? 0)
4181
+ })]
4182
+ }, f.key))
4183
+ })] });
4184
+ }
4185
+ /** Sem acento e sem diferença de maiúscula, para a busca. */
4186
+ function normalize(text) {
4187
+ const marks = new RegExp(`[${String.fromCharCode(768)}-${String.fromCharCode(879)}]`, "g");
4188
+ return text.normalize("NFD").replace(marks, "").toLowerCase();
4189
+ }
4190
+ /**
4191
+ * Posição, na lista completa da coluna, de um índice da lista desenhada (que pode ser só uma
4192
+ * parte dela, por busca, filtro ou paginação). Fim da lista desenhada = logo depois do último
4193
+ * card desenhado; sem card desenhado = fim da lista completa.
4194
+ */
4195
+ function toFullIndex(full, drawn, drawnIndex) {
4196
+ if (drawn.length === 0) return full.length;
4197
+ if (drawnIndex < drawn.length) return full.indexOf(drawn[drawnIndex]);
4198
+ return full.indexOf(drawn[drawn.length - 1]) + 1;
4199
+ }
4200
+ var situationOptions = [
4201
+ {
4202
+ value: "critical",
4203
+ label: "Crítico"
4204
+ },
4205
+ {
4206
+ value: "cadenceOverdue",
4207
+ label: "Cadência vencida"
4208
+ },
4209
+ {
4210
+ value: "noActivity",
4211
+ label: "Sem atividade"
4212
+ },
4213
+ {
4214
+ value: "late",
4215
+ label: "Atividade atrasada"
4216
+ }
4217
+ ];
5628
4218
  function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard, valueFields, formatValue = (n) => n.toLocaleString("pt-BR", {
5629
4219
  style: "currency",
5630
4220
  currency: "BRL"
5631
- }), pageSize = 20, visibleColumns = 5, onLoadMore, hasMore, dropTargets, onDropTarget, className, ...aria }) {
4221
+ }), pageSize = 20, visibleColumns = 5, onLoadMore, hasMore, dropTargets, onDropTarget, referenceDate, toolbar, onNewCard, storageKey, className, ...aria }) {
5632
4222
  const code = resolveCatalogCode("Kanban", { hasDropTargets: Boolean(dropTargets?.length) });
5633
4223
  const { isMobile } = useBreakpoint();
5634
4224
  const motion = useMotionMode();
@@ -5653,18 +4243,66 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5653
4243
  const [active, setActive] = useState(columns[0]?.id ?? "");
5654
4244
  const [dragging, setDragging] = useState(null);
5655
4245
  const [over, setOver] = useState(null);
4246
+ const [overTarget, setOverTarget] = useState(null);
5656
4247
  const [touchDrag, setTouchDrag] = useState(null);
4248
+ const [pendingDrop, setPendingDrop] = useState(null);
4249
+ const [reason, setReason] = useState("");
4250
+ const [query, setQuery] = useState("");
4251
+ const [assigneeFilter, setAssigneeFilter] = useState(null);
4252
+ const [situationFilter, setSituationFilter] = useState(null);
4253
+ const [customizing, setCustomizing] = useState(false);
4254
+ const [hidden, setHidden] = useState(() => readHidden(storageKey));
4255
+ const changeHidden = (next) => {
4256
+ setHidden(next);
4257
+ writeHidden(storageKey, next);
4258
+ };
4259
+ const show = (field) => !hidden[field];
4260
+ const today = referenceDate ?? /* @__PURE__ */ new Date();
4261
+ const statusEnabled = columns.some((c) => c.cadenceDays !== void 0) || cards.some((c) => c.activity);
4262
+ const statusOf = (card) => statusEnabled ? getKanbanCardStatus(card, columns.find((c) => c.id === card.columnId), today) : null;
4263
+ const filtering = toolbar && (query.trim() !== "" || assigneeFilter || situationFilter);
4264
+ const matches = (card) => {
4265
+ const q = normalize(query.trim());
4266
+ if (q && ![
4267
+ card.title,
4268
+ card.subtitle,
4269
+ card.contact?.name
4270
+ ].some((t) => t && normalize(t).includes(q))) return false;
4271
+ if (assigneeFilter && card.assignee !== assigneeFilter) return false;
4272
+ if (situationFilter) {
4273
+ const status = statusOf(card);
4274
+ if (!status) return false;
4275
+ if (situationFilter === "late" ? status.activity !== "late" : status.background !== situationFilter) return false;
4276
+ }
4277
+ return true;
4278
+ };
4279
+ const visibleCards = filtering ? cards.filter(matches) : cards;
4280
+ /** Lista completa da coluna: a única fonte do índice que sai em `onCardMove`. */
5657
4281
  const inColumn = (id) => cards.filter((c) => c.columnId === id);
4282
+ /** Lista visível (busca e filtros): cards desenhados, contagem, totais, paginação e abas. */
4283
+ const visibleInColumn = (id) => visibleCards.filter((c) => c.columnId === id);
5658
4284
  const drop = (columnId, index) => {
5659
- if (dragging) onCardMove?.(dragging, columnId, index);
4285
+ if (dragging) {
4286
+ const drawn = visibleInColumn(columnId).slice(0, limitOf(columnId));
4287
+ onCardMove?.(dragging, columnId, toFullIndex(inColumn(columnId), drawn, index));
4288
+ }
5660
4289
  setDragging(null);
5661
4290
  setOver(null);
4291
+ setOverTarget(null);
5662
4292
  };
5663
4293
  const draggingCardId = dragging ?? touchDrag?.cardId ?? null;
5664
4294
  const showTargetBar = Boolean(dropTargets?.length) && draggingCardId !== null;
5665
4295
  const dropOnTarget = (cardId, targetId) => {
5666
4296
  const target = dropTargets?.find((t) => t.id === targetId);
5667
4297
  if (!target || target.disabled) return;
4298
+ if (target.confirm) {
4299
+ setReason(target.confirm.reasons?.[0]?.value ?? "");
4300
+ setPendingDrop({
4301
+ cardId,
4302
+ targetId
4303
+ });
4304
+ return;
4305
+ }
5668
4306
  onDropTarget?.(cardId, targetId);
5669
4307
  };
5670
4308
  const startTouchDrag = (card, e) => {
@@ -5742,43 +4380,60 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5742
4380
  ]
5743
4381
  })] });
5744
4382
  };
5745
- /** Barra de destinos, mostrada durante o arraste (mouse ou toque) quando há dropTargets. */
4383
+ /** Rodapé flutuante de destinos, mostrado durante o arraste (mouse ou toque). */
4384
+ const highlightedTarget = overTarget ?? touchDrag?.overTargetId ?? null;
5746
4385
  const targetBar = showTargetBar && /* @__PURE__ */ jsx("div", {
5747
4386
  "aria-label": "Soltar em",
5748
- className: "fixed inset-x-4 bottom-4 z-30 flex justify-center gap-2 overflow-x-auto rounded-surface border bg-card p-2 shadow-lg",
5749
- children: dropTargets.map((t) => /* @__PURE__ */ jsxs("div", {
5750
- "data-kanban-target": t.id,
5751
- onDragOver: (e) => {
5752
- if (dragging && !t.disabled) e.preventDefault();
5753
- },
5754
- onDrop: (e) => {
5755
- e.preventDefault();
5756
- if (dragging) {
5757
- dropOnTarget(dragging, t.id);
5758
- setDragging(null);
5759
- }
5760
- },
5761
- className: cn("flex min-w-24 flex-col items-center gap-1 rounded-block border p-2 text-center text-xs transition-colors", t.disabled ? "cursor-not-allowed opacity-50" : touchDrag?.overTargetId === t.id && "bg-primary-soft/60 ring-2 ring-ring"),
5762
- children: [
5763
- t.icon && /* @__PURE__ */ jsx("span", {
5764
- "aria-hidden": true,
5765
- className: targetIconTone[t.tone ?? "neutral"],
5766
- children: t.icon
5767
- }),
5768
- /* @__PURE__ */ jsx("span", {
5769
- className: "font-medium",
5770
- children: t.label
5771
- }),
5772
- t.hint && !t.disabled && /* @__PURE__ */ jsx("span", {
5773
- className: "text-muted-foreground",
5774
- children: t.hint
5775
- }),
5776
- t.disabled && t.disabledReason && /* @__PURE__ */ jsx("span", {
5777
- className: "text-muted-foreground",
5778
- children: t.disabledReason
5779
- })
5780
- ]
5781
- }, t.id))
4387
+ className: "fixed inset-x-4 bottom-4 z-30 grid grid-cols-2 gap-2 rounded-surface border bg-card p-2 shadow-lg md:flex md:justify-center md:overflow-x-auto",
4388
+ children: dropTargets.map((t) => {
4389
+ const tone = t.tone ?? "neutral";
4390
+ const highlighted = !t.disabled && highlightedTarget === t.id;
4391
+ return /* @__PURE__ */ jsxs("div", {
4392
+ "data-kanban-target": t.id,
4393
+ "data-over": highlighted ? "true" : void 0,
4394
+ onDragEnter: () => {
4395
+ if (dragging && !t.disabled) setOverTarget(t.id);
4396
+ },
4397
+ onDragOver: (e) => {
4398
+ if (dragging && !t.disabled) {
4399
+ e.preventDefault();
4400
+ setOverTarget(t.id);
4401
+ }
4402
+ },
4403
+ onDragLeave: (e) => {
4404
+ if (e.relatedTarget instanceof Node && e.currentTarget.contains(e.relatedTarget)) return;
4405
+ setOverTarget((id) => id === t.id ? null : id);
4406
+ },
4407
+ onDrop: (e) => {
4408
+ e.preventDefault();
4409
+ setOverTarget(null);
4410
+ if (dragging) {
4411
+ dropOnTarget(dragging, t.id);
4412
+ setDragging(null);
4413
+ }
4414
+ },
4415
+ className: cn("flex min-w-24 flex-col items-center gap-1 rounded-block border p-2 text-center text-xs transition-colors", t.disabled ? "cursor-not-allowed bg-muted opacity-50" : highlighted ? cn(targetStrong[tone], "ring-2 ring-ring") : targetSoft[tone]),
4416
+ children: [
4417
+ t.icon && /* @__PURE__ */ jsx("span", {
4418
+ "aria-hidden": true,
4419
+ className: highlighted ? void 0 : targetIconTone[tone],
4420
+ children: t.icon
4421
+ }),
4422
+ /* @__PURE__ */ jsx("span", {
4423
+ className: "font-medium",
4424
+ children: t.label
4425
+ }),
4426
+ t.hint && !t.disabled && /* @__PURE__ */ jsx("span", {
4427
+ className: highlighted ? void 0 : "text-muted-foreground",
4428
+ children: t.hint
4429
+ }),
4430
+ t.disabled && t.disabledReason && /* @__PURE__ */ jsx("span", {
4431
+ className: "text-muted-foreground",
4432
+ children: t.disabledReason
4433
+ })
4434
+ ]
4435
+ }, t.id);
4436
+ })
5782
4437
  });
5783
4438
  const canTouchDrag = Boolean(onCardMove) || Boolean(dropTargets?.length && onDropTarget);
5784
4439
  const showHandle = isMobile && canTouchDrag;
@@ -5792,7 +4447,10 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5792
4447
  const dragEnd = () => {
5793
4448
  setDragging(null);
5794
4449
  setOver(null);
4450
+ setOverTarget(null);
5795
4451
  };
4452
+ const status = statusOf(card);
4453
+ const label = status && backgroundLabel(status);
5796
4454
  const common = {
5797
4455
  "data-kanban-card": card.id,
5798
4456
  draggable: !isMobile && Boolean(onCardMove),
@@ -5812,27 +4470,37 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5812
4470
  e.stopPropagation();
5813
4471
  if (over) drop(over.column, over.index);
5814
4472
  },
5815
- className: cn("relative flex rounded-block border bg-card p-3 shadow-sm transition-[box-shadow,opacity]", !isMobile && onCardMove && "cursor-grab active:cursor-grabbing", (dragging === card.id || touchDrag?.cardId === card.id) && "opacity-50", over?.column === card.columnId && over.index === index && dragging !== card.id && "ring-2 ring-ring ring-offset-2 ring-offset-muted")
4473
+ className: cn("relative flex rounded-block border", status ? statusBackground[status.background] : "bg-card", "p-3 shadow-sm transition-[box-shadow,opacity]", !isMobile && onCardMove && "cursor-grab active:cursor-grabbing", (dragging === card.id || touchDrag?.cardId === card.id) && "opacity-50", over?.column === card.columnId && over.index === index && dragging !== card.id && "ring-2 ring-ring ring-offset-2 ring-offset-muted")
5816
4474
  };
4475
+ const content = renderCard ? renderCard(card) : /* @__PURE__ */ jsx(CardBody, {
4476
+ card,
4477
+ show,
4478
+ withHandle: showHandle
4479
+ });
4480
+ const footer = renderCard ? null : cardFooter({
4481
+ card,
4482
+ status,
4483
+ show,
4484
+ valueFields: valueFields ?? [],
4485
+ formatValue
4486
+ });
4487
+ const main = onCardClick ? /* @__PURE__ */ jsx("button", {
4488
+ type: "button",
4489
+ onClick: () => onCardClick(card),
4490
+ className: footer ? "flex min-w-0 cursor-pointer flex-col gap-2 rounded-block text-left outline-none after:absolute after:inset-0 focus-visible:ring-2 focus-visible:ring-ring" : "flex min-w-0 flex-1 cursor-pointer flex-col gap-2 rounded-block text-left outline-none after:absolute after:inset-0 focus-visible:ring-2 focus-visible:ring-ring",
4491
+ children: content
4492
+ }) : /* @__PURE__ */ jsx("div", {
4493
+ className: footer ? "flex min-w-0 flex-col gap-2" : "flex min-w-0 flex-1 flex-col gap-2",
4494
+ children: content
4495
+ });
5817
4496
  const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
5818
- onCardClick ? /* @__PURE__ */ jsx("button", {
5819
- type: "button",
5820
- onClick: () => onCardClick(card),
5821
- className: "flex min-w-0 flex-1 cursor-pointer flex-col gap-2 rounded-block text-left outline-none after:absolute after:inset-0 focus-visible:ring-2 focus-visible:ring-ring",
5822
- children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(CardBody, {
5823
- card,
5824
- valueFields,
5825
- formatValue,
5826
- withHandle: showHandle
5827
- })
5828
- }) : /* @__PURE__ */ jsx("div", {
4497
+ footer ? /* @__PURE__ */ jsxs("div", {
5829
4498
  className: "flex min-w-0 flex-1 flex-col gap-2",
5830
- children: renderCard ? renderCard(card) : /* @__PURE__ */ jsx(CardBody, {
5831
- card,
5832
- valueFields,
5833
- formatValue,
5834
- withHandle: showHandle
5835
- })
4499
+ children: [main, footer]
4500
+ }) : main,
4501
+ label && /* @__PURE__ */ jsx("span", {
4502
+ className: "sr-only",
4503
+ children: label
5836
4504
  }),
5837
4505
  showHandle && /* @__PURE__ */ jsx("button", {
5838
4506
  type: "button",
@@ -5880,53 +4548,79 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5880
4548
  children: body
5881
4549
  }, card.id);
5882
4550
  };
5883
- const totals = (col) => (valueFields ?? []).slice(0, 2).map((f) => ({
5884
- ...f,
5885
- total: inColumn(col.id).reduce((sum, c) => sum + (c.values?.[f.key] ?? 0), 0)
5886
- }));
4551
+ const totalFields = (valueFields ?? []).slice(0, 2);
4552
+ const sumOf = (list, key) => list.reduce((sum, c) => sum + (c.values?.[key] ?? 0), 0);
4553
+ const boardCards = visibleCards.filter((c) => columns.some((col) => col.id === c.columnId));
4554
+ const totals = (col) => totalFields.map((f) => {
4555
+ const total = sumOf(visibleInColumn(col.id), f.key);
4556
+ const board = sumOf(boardCards, f.key);
4557
+ return {
4558
+ ...f,
4559
+ total,
4560
+ percent: board === 0 ? 0 : Math.round(total / board * 100)
4561
+ };
4562
+ });
5887
4563
  const columnHeader = (col, count) => /* @__PURE__ */ jsxs("div", {
5888
4564
  className: "flex flex-col gap-2 px-1",
5889
- children: [/* @__PURE__ */ jsxs("div", {
5890
- className: "flex items-center gap-2",
5891
- children: [
5892
- /* @__PURE__ */ jsx("span", {
5893
- "aria-hidden": true,
5894
- className: cn("size-2 rounded-full", dotTone[col.tone ?? "neutral"])
5895
- }),
5896
- /* @__PURE__ */ jsx("h3", {
5897
- className: "min-w-0 flex-1 truncate text-sm font-semibold",
5898
- children: col.title
5899
- }),
5900
- /* @__PURE__ */ jsx(Badge, {
5901
- tone: col.limit && count > col.limit ? "warning" : "neutral",
5902
- children: col.limit ? `${count}/${col.limit}` : count
5903
- }),
5904
- onAddCard && /* @__PURE__ */ jsx(Button, {
5905
- variant: "ghost",
5906
- size: "sm",
5907
- iconOnly: true,
5908
- "aria-label": `Adicionar card em ${col.title}`,
5909
- onClick: () => onAddCard(col.id),
5910
- className: "-my-1 -mr-1 rounded-full",
5911
- children: /* @__PURE__ */ jsx(Plus, {})
5912
- })
5913
- ]
5914
- }), valueFields && valueFields.length > 0 && /* @__PURE__ */ jsx("div", {
5915
- className: "grid grid-cols-2 divide-x rounded-block bg-card text-xs",
5916
- children: totals(col).map((t) => /* @__PURE__ */ jsxs("span", {
5917
- className: "flex min-w-0 flex-col px-2 py-1",
5918
- children: [/* @__PURE__ */ jsxs("span", {
5919
- className: "text-muted-foreground",
5920
- children: ["Total ", t.label]
5921
- }), /* @__PURE__ */ jsx("span", {
5922
- className: "truncate font-semibold tabular-nums",
5923
- children: formatValue(t.total)
5924
- })]
5925
- }, t.key))
5926
- })]
4565
+ children: [
4566
+ /* @__PURE__ */ jsxs("div", {
4567
+ className: "flex items-center gap-2",
4568
+ children: [
4569
+ /* @__PURE__ */ jsx("span", {
4570
+ "aria-hidden": true,
4571
+ className: cn("size-2 rounded-full", dotTone[col.tone ?? "neutral"])
4572
+ }),
4573
+ /* @__PURE__ */ jsx("h3", {
4574
+ className: "min-w-0 flex-1 truncate text-sm font-semibold",
4575
+ children: col.title
4576
+ }),
4577
+ /* @__PURE__ */ jsx(Badge, {
4578
+ tone: col.limit && count > col.limit ? "warning" : "neutral",
4579
+ children: col.limit ? `${count}/${col.limit}` : count
4580
+ }),
4581
+ onAddCard && /* @__PURE__ */ jsx(Button, {
4582
+ variant: "ghost",
4583
+ size: "sm",
4584
+ iconOnly: true,
4585
+ "aria-label": `Adicionar card em ${col.title}`,
4586
+ onClick: () => onAddCard(col.id),
4587
+ className: "-my-1 -mr-1 rounded-full",
4588
+ children: /* @__PURE__ */ jsx(Plus, {})
4589
+ })
4590
+ ]
4591
+ }),
4592
+ col.cadenceDays !== void 0 && /* @__PURE__ */ jsxs("p", {
4593
+ className: "text-xs text-muted-foreground",
4594
+ children: ["Cadência: ", plural(col.cadenceDays, "dia", "dias")]
4595
+ }),
4596
+ valueFields && valueFields.length > 0 && /* @__PURE__ */ jsx("div", {
4597
+ className: "grid grid-cols-2 divide-x rounded-block bg-card text-xs",
4598
+ children: totals(col).map((t) => /* @__PURE__ */ jsxs("span", {
4599
+ className: "flex min-w-0 flex-col px-2 py-1",
4600
+ children: [
4601
+ /* @__PURE__ */ jsxs("span", {
4602
+ className: "text-muted-foreground",
4603
+ children: ["Total ", t.label]
4604
+ }),
4605
+ /* @__PURE__ */ jsx("span", {
4606
+ className: "truncate font-semibold tabular-nums",
4607
+ children: formatValue(t.total)
4608
+ }),
4609
+ /* @__PURE__ */ jsxs("span", {
4610
+ className: "text-muted-foreground tabular-nums",
4611
+ children: [
4612
+ "(",
4613
+ t.percent,
4614
+ "%)"
4615
+ ]
4616
+ })
4617
+ ]
4618
+ }, t.key))
4619
+ })
4620
+ ]
5927
4621
  });
5928
4622
  const columnList = (col) => {
5929
- const all = inColumn(col.id);
4623
+ const all = visibleInColumn(col.id);
5930
4624
  const list = all.slice(0, limitOf(col.id));
5931
4625
  const more = all.length > list.length || Boolean(hasMore?.(col.id));
5932
4626
  return /* @__PURE__ */ jsx("div", {
@@ -5964,32 +4658,213 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
5964
4658
  }, String(ready))
5965
4659
  });
5966
4660
  };
5967
- if (isMobile) return /* @__PURE__ */ jsxs("div", {
5968
- ref: boardRef,
5969
- className: cn("flex h-board min-w-0 flex-col gap-3", className),
5970
- "aria-label": aria["aria-label"],
5971
- "data-rendra": code,
5972
- children: [
5973
- targetBar,
5974
- /* @__PURE__ */ jsx(Tabs, {
5975
- variant: "pill",
5976
- "aria-label": "Colunas",
5977
- value: active,
5978
- onChange: setActive,
5979
- items: columns.map((col) => ({
5980
- value: col.id,
5981
- label: col.title,
5982
- count: inColumn(col.id).length,
5983
- content: null
5984
- }))
5985
- }),
5986
- columns.filter((col) => col.id === active).map((col) => /* @__PURE__ */ jsxs("div", {
5987
- className: "flex min-h-0 flex-1 flex-col gap-2 rounded-surface bg-muted p-2",
5988
- children: [columnHeader(col, inColumn(col.id).length), columnList(col)]
5989
- }, col.id))
5990
- ]
4661
+ const assignees = [...new Set(cards.map((c) => c.assignee).filter((a) => !!a))].sort((a, b) => a.localeCompare(b, "pt-BR"));
4662
+ const filterCount = (assigneeFilter ? 1 : 0) + (situationFilter ? 1 : 0);
4663
+ const hasFilters = assignees.length > 0 || statusEnabled;
4664
+ const toolbarNode = toolbar && /* @__PURE__ */ jsx(DataToolbar, {
4665
+ search: {
4666
+ value: query,
4667
+ onChange: setQuery,
4668
+ placeholder: "Buscar card"
4669
+ },
4670
+ filters: hasFilters ? /* @__PURE__ */ jsxs("div", {
4671
+ className: "flex flex-col gap-4",
4672
+ children: [assignees.length > 0 && /* @__PURE__ */ jsx(Field, {
4673
+ label: "Responsável",
4674
+ span: "full",
4675
+ children: /* @__PURE__ */ jsx(Select, {
4676
+ placeholder: "Todos",
4677
+ clearable: true,
4678
+ options: assignees.map((a) => ({
4679
+ value: a,
4680
+ label: a
4681
+ })),
4682
+ value: assigneeFilter,
4683
+ onChange: setAssigneeFilter
4684
+ })
4685
+ }), statusEnabled && /* @__PURE__ */ jsx(Field, {
4686
+ label: "Situação",
4687
+ span: "full",
4688
+ children: /* @__PURE__ */ jsx(Select, {
4689
+ placeholder: "Todas",
4690
+ clearable: true,
4691
+ options: situationOptions,
4692
+ value: situationFilter,
4693
+ onChange: setSituationFilter
4694
+ })
4695
+ })]
4696
+ }) : void 0,
4697
+ filterCount,
4698
+ onClearFilters: () => {
4699
+ setAssigneeFilter(null);
4700
+ setSituationFilter(null);
4701
+ },
4702
+ summary: totalFields.length > 0 ? totalFields.map((f) => /* @__PURE__ */ jsxs("span", {
4703
+ className: "flex items-baseline gap-1",
4704
+ children: [/* @__PURE__ */ jsxs("span", {
4705
+ className: "text-muted-foreground",
4706
+ children: [
4707
+ "Total ",
4708
+ f.label,
4709
+ ":"
4710
+ ]
4711
+ }), /* @__PURE__ */ jsx("span", {
4712
+ className: "font-semibold tabular-nums",
4713
+ children: formatValue(sumOf(boardCards, f.key))
4714
+ })]
4715
+ }, f.key)) : void 0,
4716
+ actions: /* @__PURE__ */ jsx(Button, {
4717
+ variant: "outline",
4718
+ icon: /* @__PURE__ */ jsx(Settings2, {}),
4719
+ onClick: () => setCustomizing(true),
4720
+ children: "Customizar"
4721
+ }),
4722
+ primaryAction: onNewCard ? /* @__PURE__ */ jsx(Button, {
4723
+ icon: /* @__PURE__ */ jsx(Plus, {}),
4724
+ onClick: () => onNewCard(),
4725
+ children: "Novo card"
4726
+ }) : void 0
4727
+ });
4728
+ const customizeFields = [...baseFields.map((f) => ({
4729
+ id: f.id,
4730
+ label: f.label
4731
+ })), ...totalFields.map((f) => ({
4732
+ id: `value:${f.key}`,
4733
+ label: f.label
4734
+ }))];
4735
+ const customizeDrawer = toolbar && /* @__PURE__ */ jsx(Drawer, {
4736
+ open: customizing,
4737
+ onOpenChange: setCustomizing,
4738
+ title: "Customizar",
4739
+ icon: /* @__PURE__ */ jsx(Settings2, {}),
4740
+ footer: /* @__PURE__ */ jsx(ActionBar, {
4741
+ cancel: {
4742
+ label: "Restaurar",
4743
+ onClick: () => changeHidden({})
4744
+ },
4745
+ primary: {
4746
+ label: "Pronto",
4747
+ onClick: () => setCustomizing(false)
4748
+ }
4749
+ }),
4750
+ children: /* @__PURE__ */ jsx("div", {
4751
+ className: "flex flex-col gap-1",
4752
+ children: customizeFields.map((f) => /* @__PURE__ */ jsx(Checkbox, {
4753
+ label: f.label,
4754
+ checked: show(f.id),
4755
+ onCheckedChange: (checked) => changeHidden({
4756
+ ...hidden,
4757
+ [f.id]: !checked
4758
+ })
4759
+ }, f.id))
4760
+ })
5991
4761
  });
5992
- return /* @__PURE__ */ jsxs("section", {
4762
+ const pendingTarget = pendingDrop && dropTargets?.find((t) => t.id === pendingDrop.targetId);
4763
+ const pendingCard = pendingDrop && cards.find((c) => c.id === pendingDrop.cardId);
4764
+ useEffect(() => {
4765
+ if (pendingDrop && !pendingCard) setPendingDrop(null);
4766
+ }, [pendingDrop, pendingCard]);
4767
+ const confirmation = pendingDrop && pendingTarget?.confirm && pendingCard ? /* @__PURE__ */ jsxs(Modal, {
4768
+ open: true,
4769
+ onOpenChange: (next) => !next && setPendingDrop(null),
4770
+ type: pendingTarget.confirm.destructive ? "destructive" : "confirm",
4771
+ title: pendingTarget.confirm.title,
4772
+ description: pendingTarget.confirm.description,
4773
+ confirmLabel: pendingTarget.confirm.confirmLabel ?? pendingTarget.label,
4774
+ onConfirm: () => onDropTarget?.(pendingDrop.cardId, pendingDrop.targetId, reason || void 0),
4775
+ children: [/* @__PURE__ */ jsxs("dl", {
4776
+ className: "flex w-full flex-col gap-1 rounded-block bg-muted p-3 text-left text-sm",
4777
+ children: [
4778
+ /* @__PURE__ */ jsxs("div", {
4779
+ className: "flex justify-between gap-2",
4780
+ children: [/* @__PURE__ */ jsx("dt", {
4781
+ className: "text-muted-foreground",
4782
+ children: "Empresa"
4783
+ }), /* @__PURE__ */ jsx("dd", {
4784
+ className: "font-medium",
4785
+ children: pendingCard.title
4786
+ })]
4787
+ }),
4788
+ pendingCard.subtitle && /* @__PURE__ */ jsxs("div", {
4789
+ className: "flex justify-between gap-2",
4790
+ children: [/* @__PURE__ */ jsx("dt", {
4791
+ className: "text-muted-foreground",
4792
+ children: "Documento"
4793
+ }), /* @__PURE__ */ jsx("dd", {
4794
+ className: "tabular-nums",
4795
+ children: pendingCard.subtitle
4796
+ })]
4797
+ }),
4798
+ /* @__PURE__ */ jsxs("div", {
4799
+ className: "flex justify-between gap-2",
4800
+ children: [/* @__PURE__ */ jsx("dt", {
4801
+ className: "text-muted-foreground",
4802
+ children: "Etapa"
4803
+ }), /* @__PURE__ */ jsx("dd", { children: columns.find((c) => c.id === pendingCard.columnId)?.title })]
4804
+ }),
4805
+ totalFields.map((f) => /* @__PURE__ */ jsxs("div", {
4806
+ className: "flex justify-between gap-2",
4807
+ children: [/* @__PURE__ */ jsxs("dt", {
4808
+ className: "text-muted-foreground",
4809
+ children: [f.label, ":"]
4810
+ }), /* @__PURE__ */ jsx("dd", {
4811
+ className: "font-semibold tabular-nums",
4812
+ children: formatValue(pendingCard.values?.[f.key] ?? 0)
4813
+ })]
4814
+ }, f.key))
4815
+ ]
4816
+ }), pendingTarget.confirm.reasons && /* @__PURE__ */ jsxs("div", {
4817
+ className: "flex w-full flex-col gap-1 text-left",
4818
+ children: [/* @__PURE__ */ jsx("span", {
4819
+ className: "text-sm font-medium",
4820
+ children: "Motivo"
4821
+ }), /* @__PURE__ */ jsx(Select, {
4822
+ label: "Motivo",
4823
+ options: pendingTarget.confirm.reasons,
4824
+ value: reason,
4825
+ onChange: (value) => setReason(value ?? "")
4826
+ })]
4827
+ })]
4828
+ }) : null;
4829
+ if (isMobile) {
4830
+ const board = /* @__PURE__ */ jsxs("div", {
4831
+ ref: boardRef,
4832
+ className: cn("flex h-board min-w-0 flex-col gap-3", className),
4833
+ "aria-label": aria["aria-label"],
4834
+ "data-rendra": code,
4835
+ children: [
4836
+ targetBar,
4837
+ /* @__PURE__ */ jsx(Tabs, {
4838
+ variant: "pill",
4839
+ "aria-label": "Colunas",
4840
+ value: active,
4841
+ onChange: setActive,
4842
+ items: columns.map((col) => ({
4843
+ value: col.id,
4844
+ label: col.title,
4845
+ count: visibleInColumn(col.id).length,
4846
+ content: null
4847
+ }))
4848
+ }),
4849
+ columns.filter((col) => col.id === active).map((col) => /* @__PURE__ */ jsxs("div", {
4850
+ className: "flex min-h-0 flex-1 flex-col gap-2 rounded-surface bg-muted p-2",
4851
+ children: [columnHeader(col, visibleInColumn(col.id).length), columnList(col)]
4852
+ }, col.id))
4853
+ ]
4854
+ });
4855
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4856
+ toolbar ? /* @__PURE__ */ jsxs("div", {
4857
+ className: "flex min-w-0 flex-col gap-3",
4858
+ children: [/* @__PURE__ */ jsx("div", {
4859
+ className: "rounded-surface border bg-card",
4860
+ children: toolbarNode
4861
+ }), board]
4862
+ }) : board,
4863
+ customizeDrawer,
4864
+ confirmation
4865
+ ] });
4866
+ }
4867
+ const board = /* @__PURE__ */ jsxs("section", {
5993
4868
  ref: boardRef,
5994
4869
  "aria-label": aria["aria-label"] ?? "Quadro kanban",
5995
4870
  "data-rendra": code,
@@ -6000,10 +4875,21 @@ function Kanban({ columns, cards, onCardMove, onCardClick, onAddCard, renderCard
6000
4875
  className: "flex min-h-0 w-kanban-col flex-col gap-2 rounded-surface bg-muted p-2",
6001
4876
  children: [/* @__PURE__ */ jsx("div", {
6002
4877
  className: "pt-1",
6003
- children: columnHeader(col, inColumn(col.id).length)
4878
+ children: columnHeader(col, visibleInColumn(col.id).length)
6004
4879
  }), columnList(col)]
6005
4880
  }, col.id))]
6006
4881
  });
4882
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
4883
+ toolbar ? /* @__PURE__ */ jsxs("div", {
4884
+ className: "flex min-w-0 flex-col gap-4",
4885
+ children: [/* @__PURE__ */ jsx("div", {
4886
+ className: "rounded-surface border bg-card",
4887
+ children: toolbarNode
4888
+ }), board]
4889
+ }) : board,
4890
+ customizeDrawer,
4891
+ confirmation
4892
+ ] });
6007
4893
  }
6008
4894
  /** Move um card na lista (para usar no onCardMove): devolve a lista nova. */
6009
4895
  function moveKanbanCard(cards, cardId, toColumnId, toIndex) {
@@ -8397,95 +7283,6 @@ var Textarea = forwardRef(function Textarea({ invalid, counter, value, defaultVa
8397
7283
  });
8398
7284
  });
8399
7285
  //#endregion
8400
- //#region src/components/ui/timeline.tsx
8401
- var dot = {
8402
- neutral: "bg-muted text-muted-foreground",
8403
- info: "bg-info-soft text-info-soft-foreground",
8404
- success: "bg-success-soft text-success-soft-foreground",
8405
- warning: "bg-warning-soft text-warning-soft-foreground",
8406
- error: "bg-destructive-soft text-destructive-soft-foreground"
8407
- };
8408
- var statusTone = {
8409
- succeeded: "success",
8410
- failed: "error",
8411
- skipped: "neutral"
8412
- };
8413
- var statusIcon = {
8414
- succeeded: CheckCircle2,
8415
- failed: XCircle,
8416
- skipped: SkipForward
8417
- };
8418
- var statusLabel$1 = {
8419
- succeeded: "Concluído",
8420
- failed: "Falhou",
8421
- skipped: "Ignorado"
8422
- };
8423
- var statusTextTone = {
8424
- succeeded: "text-success-soft-foreground",
8425
- failed: "text-destructive-soft-foreground",
8426
- skipped: "text-muted-foreground"
8427
- };
8428
- /** Linha do tempo única, com eventos em ordem e tom semântico por evento. */
8429
- function Timeline({ events, className }) {
8430
- const lib = useMotionLib();
8431
- const on = lib !== null;
8432
- const presets = useMotionPresets();
8433
- const Item = on ? lib.m.li : "li";
8434
- const enter = presets.slide("bottom", "short");
8435
- return /* @__PURE__ */ jsx("ol", {
8436
- "data-rendra": "TLN-001",
8437
- className: cn("@container flex flex-col", className),
8438
- children: events.map((e, i) => {
8439
- const StatusIcon = e.status ? statusIcon[e.status] : null;
8440
- const tone = e.tone ?? (e.status ? statusTone[e.status] : "neutral");
8441
- return /* @__PURE__ */ jsxs(Item, {
8442
- className: "relative flex gap-3 pb-6 last:pb-0",
8443
- ...on ? {
8444
- ...enter,
8445
- transition: {
8446
- ...enter.transition,
8447
- ...presets.stagger(i)
8448
- }
8449
- } : {},
8450
- children: [
8451
- i < events.length - 1 && /* @__PURE__ */ jsx("span", {
8452
- "aria-hidden": true,
8453
- className: "absolute top-8 bottom-0 left-4 w-px -translate-x-1/2 bg-border"
8454
- }),
8455
- /* @__PURE__ */ jsx("span", {
8456
- "aria-hidden": true,
8457
- className: cn("relative flex size-8 shrink-0 items-center justify-center rounded-full [&_svg]:size-icon-sm", dot[tone]),
8458
- children: e.icon ?? (StatusIcon && /* @__PURE__ */ jsx(StatusIcon, {})) ?? /* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-current" })
8459
- }),
8460
- /* @__PURE__ */ jsxs("div", {
8461
- className: "flex min-w-0 flex-1 flex-col gap-1 pt-1",
8462
- children: [
8463
- /* @__PURE__ */ jsxs("div", {
8464
- className: "flex min-w-0 flex-col gap-1 @sm:flex-row @sm:items-baseline @sm:justify-between @sm:gap-3",
8465
- children: [/* @__PURE__ */ jsx("span", {
8466
- className: "text-sm font-medium wrap-break-word",
8467
- children: e.title
8468
- }), /* @__PURE__ */ jsx("time", {
8469
- className: "shrink-0 text-xs text-muted-foreground tabular-nums",
8470
- children: e.date
8471
- })]
8472
- }),
8473
- e.description && /* @__PURE__ */ jsx("p", {
8474
- className: "text-sm wrap-break-word text-muted-foreground",
8475
- children: e.description
8476
- }),
8477
- e.status && StatusIcon && /* @__PURE__ */ jsxs("span", {
8478
- className: cn("inline-flex w-fit items-center gap-1 text-xs font-medium [&_svg]:size-icon-sm", statusTextTone[e.status]),
8479
- children: [/* @__PURE__ */ jsx(StatusIcon, {}), statusLabel$1[e.status]]
8480
- })
8481
- ]
8482
- })
8483
- ]
8484
- }, e.id);
8485
- })
8486
- });
8487
- }
8488
- //#endregion
8489
7286
  //#region src/components/ui/toast.tsx
8490
7287
  var tone = {
8491
7288
  success: "border-success/25",
@@ -11339,6 +10136,6 @@ function createTheme(input) {
11339
10136
  };
11340
10137
  }
11341
10138
  //#endregion
11342
- export { Accordion, ActionBar, Alert, AppShell, Avatar, AvatarGroup, Badge, BrandFeedbackIcon, BrandLogo, BrandProvider, Breadcrumb, Button, ButtonGroup, CATALOG, CATALOG_EXCLUDED_FILES, COMPONENT_CODE_PATTERN, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChannelBadge, ChatComposer, ChatThread, Checkbox, CheckboxGroup, Checklist, ColorPicker, Container, ConversationList, DEFAULT_DDI, DataToolbar, DatePicker, Drawer, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorPage, Field, Form, FormField, FormSection, Grid, ImageCropper, ImageViewer, InfoHint, Inline, Input, Kanban, Label, List, Modal, OtpInput, PageHeader, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QR_MAX_VERSION, QrCode, QrEncodingError, RENDRA_CREDIT_HREF, RENDRA_CREDIT_TEXT, RadioGroup, Rating, RendraCredit, RendraProvider, RepeatableField, Section, Select, Separator, Skeleton, Slider, Spinner, Stack, StatCard, Stepper, Switch, TABLE_PAGE_SIZE, TOKEN_KEYS, Table, Tabs, Textarea, Timeline, Toaster, Tooltip, TooltipProvider, Upload, Wizard, applyPalette, applyTheme, assertCatalogIntegrity, bindPointerDrag, breakpoints, buildDataCodewords, buttonVariants, catalogByComponent, chatChannels, cn, computeCropGeometry, computeFormatBits, computeMaskPenalty, computeVersionBits, contrast, contrastRatio, controlAdornmentButton, controlAdornmentSpace, controlFrame, controlInput, createPalette, createTheme, defaultShellLayout, encodeQrMatrix, feedbackLabels, filesMissingCatalogEntry, findComponentsWithMultipleDefaults, findComponentsWithoutDefault, findDragTarget, findDuplicateCodes, findInvalidFormatCodes, findPresetCollisions, formatCents, formatCurrency, getCatalogEntry, getReducedMotion, internationalPhoneMask, isValidCnpj, isValidCpf, isValidDateBR, layoutOptions, lookupCep, lookupCnpj, masks, moveKanbanCard, parseLocaleNumber, percentMask, phoneCountries, qrMatrixToSvgPath, readCodewordsForTest, readVar, reedSolomonComputeDivisor, reedSolomonComputeRemainder, reedSolomonEncode, reedSolomonMultiply, resolveCatalogCode, resolveDropIndex, shapeLabels, subscribeReducedMotion, toCents, toE164, toast, useBrand, useBreakpoint, useCurrentPath, useDebouncedValue, useFieldControl, useFillHeight, useLookup, usePrefersReducedMotion, useRendraBreadcrumbs, useRendraLink, useRendraNavigate, useSortable, zBR };
10139
+ export { Accordion, ActionBar, Alert, AppShell, Avatar, AvatarGroup, Badge, BrandFeedbackIcon, BrandLogo, BrandProvider, Breadcrumb, Button, ButtonGroup, CATALOG, CATALOG_EXCLUDED_FILES, COMPONENT_CODE_PATTERN, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChannelBadge, ChatComposer, ChatThread, Checkbox, CheckboxGroup, Checklist, ColorPicker, Container, ConversationList, DEFAULT_DDI, DataToolbar, DatePicker, Drawer, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ErrorPage, Field, Form, FormField, FormSection, Grid, ImageCropper, ImageViewer, InfoHint, Inline, Input, Kanban, Label, List, Modal, OtpInput, PageHeader, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QR_MAX_VERSION, QrCode, QrEncodingError, RENDRA_CREDIT_HREF, RENDRA_CREDIT_TEXT, RadioGroup, Rating, RendraCredit, RendraProvider, RepeatableField, Section, Select, Separator, Skeleton, Slider, Spinner, Stack, StatCard, Stepper, Switch, TABLE_PAGE_SIZE, TOKEN_KEYS, Table, Tabs, Textarea, Timeline, Toaster, Tooltip, TooltipProvider, Upload, Wizard, applyPalette, applyTheme, assertCatalogIntegrity, bindPointerDrag, breakpoints, buildDataCodewords, buttonVariants, catalogByComponent, chatChannels, cn, computeCropGeometry, computeFormatBits, computeMaskPenalty, computeVersionBits, contrast, contrastRatio, controlAdornmentButton, controlAdornmentSpace, controlFrame, controlInput, createPalette, createTheme, defaultShellLayout, encodeQrMatrix, feedbackLabels, filesMissingCatalogEntry, findComponentsWithMultipleDefaults, findComponentsWithoutDefault, findDragTarget, findDuplicateCodes, findInvalidFormatCodes, findPresetCollisions, formatCents, formatCurrency, getCatalogEntry, getKanbanCardStatus, getReducedMotion, internationalPhoneMask, isValidCnpj, isValidCpf, isValidDateBR, layoutOptions, lookupCep, lookupCnpj, masks, moveKanbanCard, parseLocaleNumber, percentMask, phoneCountries, qrMatrixToSvgPath, readCodewordsForTest, readVar, reedSolomonComputeDivisor, reedSolomonComputeRemainder, reedSolomonEncode, reedSolomonMultiply, resolveCatalogCode, resolveDropIndex, shapeLabels, subscribeReducedMotion, toCents, toE164, toast, useBrand, useBreakpoint, useCurrentPath, useDebouncedValue, useFieldControl, useFillHeight, useLookup, usePrefersReducedMotion, useRendraBreadcrumbs, useRendraLink, useRendraNavigate, useSortable, zBR };
11343
10140
 
11344
10141
  //# sourceMappingURL=index.js.map