@snksergio/design-system 0.67.0 → 0.68.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 (123) hide show
  1. package/dist-lib/ai/componentes/AppShell.md +1 -1
  2. package/dist-lib/ai/componentes/Chip.md +11 -0
  3. package/dist-lib/ai/componentes/Combobox.md +23 -0
  4. package/dist-lib/ai/componentes/DatePicker.md +15 -0
  5. package/dist-lib/ai/componentes/FileUploadField.md +23 -0
  6. package/dist-lib/ai/componentes/Header.md +9 -0
  7. package/dist-lib/ai/componentes/Kpi.md +14 -1
  8. package/dist-lib/ai/componentes/MenuSidebar.md +3 -3
  9. package/dist-lib/ai/componentes/PageHeader.md +6 -0
  10. package/dist-lib/ai/componentes/_primitivos.md +9 -2
  11. package/dist-lib/ai/global/componentes.md +20 -2
  12. package/dist-lib/ai/manifest.json +2 -2
  13. package/dist-lib/shadcn.cjs +1 -0
  14. package/dist-lib/shadcn.cjs.map +1 -1
  15. package/dist-lib/shadcn.mjs +2 -1
  16. package/dist-lib/src/components/shadcn/alert-dialog.cjs +13 -2
  17. package/dist-lib/src/components/shadcn/alert-dialog.cjs.map +1 -1
  18. package/dist-lib/src/components/shadcn/alert-dialog.d.ts +21 -1
  19. package/dist-lib/src/components/shadcn/alert-dialog.d.ts.map +1 -1
  20. package/dist-lib/src/components/shadcn/alert-dialog.mjs +13 -2
  21. package/dist-lib/src/components/shadcn/alert-dialog.mjs.map +1 -1
  22. package/dist-lib/src/components/shadcn/card.cjs +22 -2
  23. package/dist-lib/src/components/shadcn/card.cjs.map +1 -1
  24. package/dist-lib/src/components/shadcn/card.d.ts +10 -1
  25. package/dist-lib/src/components/shadcn/card.d.ts.map +1 -1
  26. package/dist-lib/src/components/shadcn/card.mjs +22 -2
  27. package/dist-lib/src/components/shadcn/card.mjs.map +1 -1
  28. package/dist-lib/src/components/shadcn/clickable-surface.cjs +28 -4
  29. package/dist-lib/src/components/shadcn/clickable-surface.cjs.map +1 -1
  30. package/dist-lib/src/components/shadcn/clickable-surface.d.ts +26 -2
  31. package/dist-lib/src/components/shadcn/clickable-surface.d.ts.map +1 -1
  32. package/dist-lib/src/components/shadcn/clickable-surface.mjs +28 -4
  33. package/dist-lib/src/components/shadcn/clickable-surface.mjs.map +1 -1
  34. package/dist-lib/src/components/shadcn/command.cjs +26 -5
  35. package/dist-lib/src/components/shadcn/command.cjs.map +1 -1
  36. package/dist-lib/src/components/shadcn/command.d.ts +49 -2
  37. package/dist-lib/src/components/shadcn/command.d.ts.map +1 -1
  38. package/dist-lib/src/components/shadcn/command.mjs +26 -5
  39. package/dist-lib/src/components/shadcn/command.mjs.map +1 -1
  40. package/dist-lib/src/components/shadcn/input-group.d.ts +1 -1
  41. package/dist-lib/src/components/shadcn/input-otp.d.ts +1 -1
  42. package/dist-lib/src/components/shadcn/input.d.ts +1 -1
  43. package/dist-lib/src/components/shadcn/sheet.d.ts +1 -1
  44. package/dist-lib/src/components/ui/AppShell/app-shell.types.d.ts +1 -1
  45. package/dist-lib/src/components/ui/Chip/chip.cjs +7 -0
  46. package/dist-lib/src/components/ui/Chip/chip.cjs.map +1 -1
  47. package/dist-lib/src/components/ui/Chip/chip.d.ts.map +1 -1
  48. package/dist-lib/src/components/ui/Chip/chip.mjs +7 -0
  49. package/dist-lib/src/components/ui/Chip/chip.mjs.map +1 -1
  50. package/dist-lib/src/components/ui/Chip/chip.styles.cjs.map +1 -1
  51. package/dist-lib/src/components/ui/Chip/chip.styles.mjs.map +1 -1
  52. package/dist-lib/src/components/ui/Combobox/combobox.cjs +60 -22
  53. package/dist-lib/src/components/ui/Combobox/combobox.cjs.map +1 -1
  54. package/dist-lib/src/components/ui/Combobox/combobox.d.ts +2 -1
  55. package/dist-lib/src/components/ui/Combobox/combobox.d.ts.map +1 -1
  56. package/dist-lib/src/components/ui/Combobox/combobox.mjs +44 -22
  57. package/dist-lib/src/components/ui/Combobox/combobox.mjs.map +1 -1
  58. package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs +5 -1
  59. package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs.map +1 -1
  60. package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts +18 -0
  61. package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts.map +1 -1
  62. package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs +5 -1
  63. package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs.map +1 -1
  64. package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts +21 -0
  65. package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts.map +1 -1
  66. package/dist-lib/src/components/ui/DatePicker/datepicker.cjs +21 -3
  67. package/dist-lib/src/components/ui/DatePicker/datepicker.cjs.map +1 -1
  68. package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts +10 -0
  69. package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts.map +1 -1
  70. package/dist-lib/src/components/ui/DatePicker/datepicker.mjs +21 -3
  71. package/dist-lib/src/components/ui/DatePicker/datepicker.mjs.map +1 -1
  72. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs +1 -4
  73. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs.map +1 -1
  74. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.d.ts.map +1 -1
  75. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs +1 -4
  76. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs.map +1 -1
  77. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts +6 -0
  78. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts.map +1 -1
  79. package/dist-lib/src/components/ui/Header/header-search.cjs +6 -4
  80. package/dist-lib/src/components/ui/Header/header-search.cjs.map +1 -1
  81. package/dist-lib/src/components/ui/Header/header-search.d.ts +14 -3
  82. package/dist-lib/src/components/ui/Header/header-search.d.ts.map +1 -1
  83. package/dist-lib/src/components/ui/Header/header-search.mjs +6 -4
  84. package/dist-lib/src/components/ui/Header/header-search.mjs.map +1 -1
  85. package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs +5 -0
  86. package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs.map +1 -1
  87. package/dist-lib/src/components/ui/Kpi/kpi-delta.d.ts.map +1 -1
  88. package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs +5 -0
  89. package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs.map +1 -1
  90. package/dist-lib/src/components/ui/Kpi/kpi-group.cjs.map +1 -1
  91. package/dist-lib/src/components/ui/Kpi/kpi-group.mjs.map +1 -1
  92. package/dist-lib/src/components/ui/Kpi/kpi.cjs +8 -3
  93. package/dist-lib/src/components/ui/Kpi/kpi.cjs.map +1 -1
  94. package/dist-lib/src/components/ui/Kpi/kpi.d.ts +1 -1
  95. package/dist-lib/src/components/ui/Kpi/kpi.d.ts.map +1 -1
  96. package/dist-lib/src/components/ui/Kpi/kpi.mjs +8 -3
  97. package/dist-lib/src/components/ui/Kpi/kpi.mjs.map +1 -1
  98. package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts +9 -0
  99. package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts.map +1 -1
  100. package/dist-lib/src/components/ui/MenuSidebar/sidebar.types.d.ts +1 -1
  101. package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs +4 -1
  102. package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs.map +1 -1
  103. package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts +9 -0
  104. package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts.map +1 -1
  105. package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs +4 -1
  106. package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs.map +1 -1
  107. package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts +11 -4
  108. package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts.map +1 -1
  109. package/dist-lib/src/components/ui/Table/table-span-row.cjs.map +1 -1
  110. package/dist-lib/src/components/ui/Table/table-span-row.mjs.map +1 -1
  111. package/dist-lib/src/lib/utils.cjs.map +1 -1
  112. package/dist-lib/src/lib/utils.mjs.map +1 -1
  113. package/dist-lib/src/utils/tv.cjs.map +1 -1
  114. package/dist-lib/src/utils/tv.mjs.map +1 -1
  115. package/dist-lib/src/utils/tw-merge-config.cjs.map +1 -1
  116. package/dist-lib/src/utils/tw-merge-config.mjs.map +1 -1
  117. package/dist-lib/src/utils/use-title-se-truncado.cjs +34 -0
  118. package/dist-lib/src/utils/use-title-se-truncado.cjs.map +1 -0
  119. package/dist-lib/src/utils/use-title-se-truncado.d.ts +30 -0
  120. package/dist-lib/src/utils/use-title-se-truncado.d.ts.map +1 -0
  121. package/dist-lib/src/utils/use-title-se-truncado.mjs +34 -0
  122. package/dist-lib/src/utils/use-title-se-truncado.mjs.map +1 -0
  123. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"datepicker.cjs","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { Calendar as CalendarIcon } from \"lucide-react\";\r\nimport type { DateRange, Matcher } from \"react-day-picker\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\nimport { X } from \"lucide-react\";\r\n\r\nimport { Calendar } from \"@/components/shadcn/calendar\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\n\r\nexport type { DateRange };\r\n\r\ninterface BaseDatePickerProps {\r\n /** Texto do trigger quando nada está selecionado. */\r\n placeholder?: string;\r\n /** Desabilita o trigger. */\r\n disabled?: boolean;\r\n /** Alinhamento do popover. Default `\"start\"`. */\r\n align?: \"start\" | \"center\" | \"end\";\r\n /** Nº de meses no calendário. Default: 1 (single/multiple), 2 (range). */\r\n numberOfMonths?: number;\r\n /**\r\n * Data mínima selecionável. Dias anteriores ficam desabilitados no calendário.\r\n *\r\n * O DS não oferecia limite nenhum, então \"não deixar escolher data futura\" ou\r\n * \"não passar do prazo do projeto\" virava validação DEPOIS do clique — o usuário\r\n * escolhia, e só então recebia o erro.\r\n */\r\n minValue?: Date;\r\n /** Data máxima selecionável. Dias posteriores ficam desabilitados. */\r\n maxValue?: Date;\r\n /**\r\n * Mostra um \"×\" no trigger pra limpar a seleção. Default `false`.\r\n * Com `clearable`, limpar emite `onValueChange(undefined)`.\r\n */\r\n clearable?: boolean;\r\n /** Nome acessível do botão de limpar. Default: \"Limpar data\". */\r\n clearLabel?: string;\r\n /** className do trigger (mesmos overrides de um input/SelectTrigger). */\r\n className?: string;\r\n}\r\n\r\ninterface SingleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção única (default). */\r\n mode?: \"single\";\r\n value?: Date;\r\n onValueChange?: (value: Date | undefined) => void;\r\n}\r\n\r\ninterface RangeDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de intervalo (início–fim), com o realce de range do DS. */\r\n mode: \"range\";\r\n value?: DateRange;\r\n onValueChange?: (value: DateRange | undefined) => void;\r\n}\r\n\r\ninterface MultipleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de múltiplas datas. */\r\n mode: \"multiple\";\r\n value?: Date[];\r\n onValueChange?: (value: Date[] | undefined) => void;\r\n}\r\n\r\nexport type DatePickerProps =\r\n | SingleDatePickerProps\r\n | RangeDatePickerProps\r\n | MultipleDatePickerProps;\r\n\r\nconst formatFull = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"long\",\r\n year: \"numeric\",\r\n });\r\n\r\nconst formatShort = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n });\r\n\r\n/**\r\n * DatePicker — seletor de data(s) num trigger no estilo input do DS + Popover.\r\n * Suporta os modos do Calendar via `mode` (default `\"single\"`):\r\n * - `\"single\"`: uma data. `value: Date`.\r\n * - `\"range\"`: intervalo início–fim (com o realce de range do DS). `value: DateRange`.\r\n * - `\"multiple\"`: várias datas. `value: Date[]`.\r\n * Controlado via `value` / `onValueChange`. Fecha ao completar a seleção\r\n * (single: no clique; range: quando início E fim escolhidos).\r\n */\r\nexport const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\r\n (props, ref) => {\r\n const {\r\n placeholder,\r\n disabled,\r\n align = \"start\",\r\n minValue,\r\n maxValue,\r\n clearable,\r\n clearLabel,\r\n className,\r\n } = props;\r\n const [open, setOpen] = React.useState(false);\r\n\r\n // O react-day-picker recebe os limites como MATCHERS de dia desabilitado; passar\r\n // `fromDate`/`toDate` só moveria a navegação de mês e continuaria deixando clicar.\r\n const desabilitados = React.useMemo(() => {\r\n const m: Matcher[] = [];\r\n if (minValue) m.push({ before: minValue });\r\n if (maxValue) m.push({ after: maxValue });\r\n return m.length ? m : undefined;\r\n }, [minValue, maxValue]);\r\n\r\n let label: string | null = null;\r\n let calendar: React.ReactNode;\r\n\r\n if (props.mode === \"range\") {\r\n const v = props.value;\r\n label = v?.from\r\n ? v.to\r\n ? `${formatShort(v.from)} – ${formatShort(v.to)}`\r\n : formatShort(v.from)\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"range\"\r\r\n disabled={desabilitados}\r\n // min=1: sem isso o RDP completa o range no PRIMEIRO clique\r\n // ({from,to} iguais), fechando o popover e impedindo escolher o fim.\r\n min={1}\r\n numberOfMonths={props.numberOfMonths ?? 2}\r\n selected={v}\r\n onSelect={(range) => {\r\n props.onValueChange?.(range);\r\n if (range?.from && range?.to) setOpen(false);\r\n }}\r\n autoFocus\r\n />\r\n );\r\n } else if (props.mode === \"multiple\") {\r\n const v = props.value;\r\n // Plural condicional: com 1 selecionada o texto fixo dizia \"1 datas\r\n // selecionadas\". Aparece sempre que o usuário escolhe a primeira data.\r\n label =\r\n v && v.length > 0\r\n ? v.length === 1\r\n ? \"1 data selecionada\"\r\n : `${v.length} datas selecionadas`\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"multiple\"\r\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(dates) => props.onValueChange?.(dates)}\r\n autoFocus\r\n />\r\n );\r\n } else {\r\n const v = props.value;\r\n label = v ? formatFull(v) : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"single\"\r\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(date) => {\r\n props.onValueChange?.(date);\r\n setOpen(false);\r\n }}\r\n />\r\n );\r\n }\r\n\r\n const limpar = () => {\r\n // O union de props impede chamar `onValueChange` sem estreitar o mode antes.\r\n if (props.mode === \"range\") props.onValueChange?.(undefined);\r\n else if (props.mode === \"multiple\") props.onValueChange?.(undefined);\r\n else props.onValueChange?.(undefined);\r\n };\r\n\r\n const text =\r\n label ??\r\n placeholder ??\r\n (props.mode === \"range\" ? \"Selecione o período\" : \"Selecione a data\");\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n disabled={disabled}\r\n data-placeholder={label ? undefined : \"\"}\r\n className={cn(\r\n // Trigger espelha o SelectTrigger (`shadcn/select.tsx`), que é o\r\n // precedente do DS pra \"botão que tem que parecer input\": mesmos\r\n // tokens de bg/border/radius/padding/gap e o mesmo tratamento de\r\n // foco (borda + shadow-sh-ring), em vez de `ring-4` de botão.\r\n // Antes divergia em 6 pontos — bg-surface (opaco) no lugar de\r\n // bg-input (translúcido), border-default, radius-md, pad-lg,\r\n // gp-sm e sem hover — e por isso não lia como campo de formulário.\r\n \"flex min-h-form-lg w-full items-center gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default\",\r\n \"transition-[border-color,box-shadow,background-color] outline-none\",\r\n \"focus-visible:border-border-brand focus-visible:shadow-sh-ring\",\r\n \"data-[state=open]:border-border-brand data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n \"data-[placeholder]:text-fg-muted\",\r\n className,\r\n )}\r\n >\r\n <CalendarIcon className=\"size-icon-sm shrink-0 text-fg-muted\" strokeWidth={1.8} />\r\n <span className=\"truncate\">{text}</span>\r\n {clearable && label && !disabled && (\r\n // `<span role=\"button\">` e não `<button>`: o trigger do Popover JÁ é um\r\n // button, e button dentro de button é HTML inválido — o navegador\r\n // desaninha e o × passa a abrir o calendário junto (mesmo motivo da\r\n // anatomia do Chip com onRemove).\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label={clearLabel ?? \"Limpar data\"}\r\n className=\"-mr-[2px] ml-auto grid size-icon-md shrink-0 place-items-center rounded-radius-full text-fg-muted opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand\"\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key !== \"Enter\" && e.key !== \" \") return;\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n >\r\n <X className=\"size-icon-xs\" aria-hidden />\r\n </span>\r\n )}\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent align={align} className=\"w-auto p-pad-xl\">\r\n {calendar}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\n\r\nDatePicker.displayName = \"DatePicker\";\r\n"],"names":["React","calendar","jsx","Calendar","jsxs","Popover","PopoverTrigger","cn","CalendarIcon","X","PopoverContent"],"mappings":"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"}
1
+ {"version":3,"file":"datepicker.cjs","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { Calendar as CalendarIcon } from \"lucide-react\";\r\nimport type { DateRange, Matcher } from \"react-day-picker\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\nimport { ptBR } from \"date-fns/locale\";\r\nimport { X } from \"lucide-react\";\r\n\r\nimport { Calendar } from \"@/components/shadcn/calendar\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\n\r\nexport type { DateRange };\r\n\r\ninterface BaseDatePickerProps {\r\n /** Texto do trigger quando nada está selecionado. */\r\n placeholder?: string;\r\n /** Desabilita o trigger. */\r\n disabled?: boolean;\r\n /** Alinhamento do popover. Default `\"start\"`. */\r\n align?: \"start\" | \"center\" | \"end\";\r\n /** Nº de meses no calendário. Default: 1 (single/multiple), 2 (range). */\r\n numberOfMonths?: number;\r\n /**\r\n * Locale do CALENDÁRIO (meses, dias da semana). Default `ptBR`.\r\n *\r\n * Era o único texto em inglês do DS: o trigger já formatava em pt-BR\r\n * (\"12 de março de 2026\") enquanto o calendário abria com \"March\" e \"Mo Tu We\".\r\n * Não era escolha — o `Calendar` nunca recebeu `locale`. O `date-fns` já é\r\n * dependência, então o default custa zero.\r\n */\r\n locale?: typeof ptBR;\r\n /**\r\n * Data mínima selecionável. Dias anteriores ficam desabilitados no calendário.\r\n *\r\n * O DS não oferecia limite nenhum, então \"não deixar escolher data futura\" ou\r\n * \"não passar do prazo do projeto\" virava validação DEPOIS do clique — o usuário\r\n * escolhia, e só então recebia o erro.\r\n */\r\n minValue?: Date;\r\n /** Data máxima selecionável. Dias posteriores ficam desabilitados. */\r\n maxValue?: Date;\r\n /**\r\n * Mostra um \"×\" no trigger pra limpar a seleção. Default `false`.\r\n * Com `clearable`, limpar emite `onValueChange(undefined)`.\r\n */\r\n clearable?: boolean;\r\n /** Nome acessível do botão de limpar. Default: \"Limpar data\". */\r\n clearLabel?: string;\r\n /** className do trigger (mesmos overrides de um input/SelectTrigger). */\r\n className?: string;\r\n}\r\n\r\ninterface SingleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção única (default). */\r\n mode?: \"single\";\r\n value?: Date;\r\n onValueChange?: (value: Date | undefined) => void;\r\n}\r\n\r\ninterface RangeDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de intervalo (início–fim), com o realce de range do DS. */\r\n mode: \"range\";\r\n value?: DateRange;\r\n onValueChange?: (value: DateRange | undefined) => void;\r\n}\r\n\r\ninterface MultipleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de múltiplas datas. */\r\n mode: \"multiple\";\r\n value?: Date[];\r\n onValueChange?: (value: Date[] | undefined) => void;\r\n}\r\n\r\nexport type DatePickerProps =\r\n | SingleDatePickerProps\r\n | RangeDatePickerProps\r\n | MultipleDatePickerProps;\r\n\r\nconst formatFull = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"long\",\r\n year: \"numeric\",\r\n });\r\n\r\nconst formatShort = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n });\r\n\r\n/**\r\n * DatePicker — seletor de data(s) num trigger no estilo input do DS + Popover.\r\n * Suporta os modos do Calendar via `mode` (default `\"single\"`):\r\n * - `\"single\"`: uma data. `value: Date`.\r\n * - `\"range\"`: intervalo início–fim (com o realce de range do DS). `value: DateRange`.\r\n * - `\"multiple\"`: várias datas. `value: Date[]`.\r\n * Controlado via `value` / `onValueChange`. Fecha ao completar a seleção\r\n * (single: no clique; range: quando início E fim escolhidos).\r\n */\r\nexport const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\r\n (props, ref) => {\r\n const {\r\n placeholder,\r\n disabled,\r\n align = \"start\",\r\n locale = ptBR,\r\n minValue,\r\n maxValue,\r\n clearable,\r\n clearLabel,\r\n className,\r\n } = props;\r\n const [open, setOpen] = React.useState(false);\r\n /**\r\n * O range fecha no SEGUNDO clique, não no primeiro.\r\n *\r\n * Antes havia `min={1}` no Calendar, justamente pra o RDP não completar o range no\r\n * primeiro clique e fechar o popover antes de escolher o fim. O efeito colateral era\r\n * tornar impossível um período de UM DIA (`from === to`) — reportado pelo consumidor,\r\n * que manteve o date-range-picker local por causa disso.\r\n *\r\n * Sem o `min`, o primeiro clique já devolve `{ from, to }` iguais; quem segura o\r\n * popover aberto passa a ser este estado, e não o RDP.\r\n */\r\n const [aguardandoFim, setAguardandoFim] = React.useState(false);\r\n\r\n // O react-day-picker recebe os limites como MATCHERS de dia desabilitado; passar\r\n // `fromDate`/`toDate` só moveria a navegação de mês e continuaria deixando clicar.\r\n const desabilitados = React.useMemo(() => {\r\n const m: Matcher[] = [];\r\n if (minValue) m.push({ before: minValue });\r\n if (maxValue) m.push({ after: maxValue });\r\n return m.length ? m : undefined;\r\n }, [minValue, maxValue]);\r\n\r\n let label: string | null = null;\r\n let calendar: React.ReactNode;\r\n\r\n if (props.mode === \"range\") {\r\n const v = props.value;\r\n label = v?.from\r\n ? v.to\r\n ? `${formatShort(v.from)} – ${formatShort(v.to)}`\r\n : formatShort(v.from)\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"range\"\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths ?? 2}\r\n selected={v}\r\n onSelect={(range) => {\r\n props.onValueChange?.(range);\r\n if (!range?.from) {\r\n // Seleção limpa pelo próprio RDP — recomeça a contagem.\r\n setAguardandoFim(false);\r\n return;\r\n }\r\n if (!aguardandoFim) {\r\n setAguardandoFim(true); // 1º clique: segura o popover aberto\r\n return;\r\n }\r\n setAguardandoFim(false);\r\n setOpen(false); // 2º clique fecha, mesmo com from === to\r\n }}\r\n autoFocus\r\n />\r\n );\r\n } else if (props.mode === \"multiple\") {\r\n const v = props.value;\r\n // Plural condicional: com 1 selecionada o texto fixo dizia \"1 datas\r\n // selecionadas\". Aparece sempre que o usuário escolhe a primeira data.\r\n label =\r\n v && v.length > 0\r\n ? v.length === 1\r\n ? \"1 data selecionada\"\r\n : `${v.length} datas selecionadas`\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"multiple\"\r\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(dates) => props.onValueChange?.(dates)}\r\n autoFocus\r\n />\r\n );\r\n } else {\r\n const v = props.value;\r\n label = v ? formatFull(v) : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"single\"\r\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(date) => {\r\n props.onValueChange?.(date);\r\n setOpen(false);\r\n }}\r\n />\r\n );\r\n }\r\n\r\n const aoAbrirFechar = (proximo: boolean) => {\r\n setOpen(proximo);\r\n if (!proximo) setAguardandoFim(false);\r\n };\r\n\r\n const limpar = () => {\r\n // O union de props impede chamar `onValueChange` sem estreitar o mode antes.\r\n if (props.mode === \"range\") props.onValueChange?.(undefined);\r\n else if (props.mode === \"multiple\") props.onValueChange?.(undefined);\r\n else props.onValueChange?.(undefined);\r\n };\r\n\r\n const text =\r\n label ??\r\n placeholder ??\r\n (props.mode === \"range\" ? \"Selecione o período\" : \"Selecione a data\");\r\n\r\n return (\r\n <Popover open={open} onOpenChange={aoAbrirFechar}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n disabled={disabled}\r\n data-placeholder={label ? undefined : \"\"}\r\n className={cn(\r\n // Trigger espelha o SelectTrigger (`shadcn/select.tsx`), que é o\r\n // precedente do DS pra \"botão que tem que parecer input\": mesmos\r\n // tokens de bg/border/radius/padding/gap e o mesmo tratamento de\r\n // foco (borda + shadow-sh-ring), em vez de `ring-4` de botão.\r\n // Antes divergia em 6 pontos — bg-surface (opaco) no lugar de\r\n // bg-input (translúcido), border-default, radius-md, pad-lg,\r\n // gp-sm e sem hover — e por isso não lia como campo de formulário.\r\n \"flex min-h-form-lg w-full items-center gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default\",\r\n \"transition-[border-color,box-shadow,background-color] outline-none\",\r\n \"focus-visible:border-border-brand focus-visible:shadow-sh-ring\",\r\n \"data-[state=open]:border-border-brand data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n \"data-[placeholder]:text-fg-muted\",\r\n className,\r\n )}\r\n >\r\n <CalendarIcon className=\"size-icon-sm shrink-0 text-fg-muted\" strokeWidth={1.8} />\r\n <span className=\"truncate\">{text}</span>\r\n {clearable && label && !disabled && (\r\n // `<span role=\"button\">` e não `<button>`: o trigger do Popover JÁ é um\r\n // button, e button dentro de button é HTML inválido — o navegador\r\n // desaninha e o × passa a abrir o calendário junto (mesmo motivo da\r\n // anatomia do Chip com onRemove).\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label={clearLabel ?? \"Limpar data\"}\r\n className=\"-mr-[2px] ml-auto grid size-icon-md shrink-0 place-items-center rounded-radius-full text-fg-muted opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand\"\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key !== \"Enter\" && e.key !== \" \") return;\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n >\r\n <X className=\"size-icon-xs\" aria-hidden />\r\n </span>\r\n )}\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent align={align} className=\"w-auto p-pad-xl\">\r\n {calendar}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\n\r\nDatePicker.displayName = \"DatePicker\";\r\n"],"names":["React","locale","ptBR","calendar","jsx","Calendar","jsxs","Popover","PopoverTrigger","cn","CalendarIcon","X","PopoverContent"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { DateRange } from 'react-day-picker';
2
+ import { ptBR } from 'date-fns/locale';
2
3
  import * as React from "react";
3
4
  export type { DateRange };
4
5
  interface BaseDatePickerProps {
@@ -10,6 +11,15 @@ interface BaseDatePickerProps {
10
11
  align?: "start" | "center" | "end";
11
12
  /** Nº de meses no calendário. Default: 1 (single/multiple), 2 (range). */
12
13
  numberOfMonths?: number;
14
+ /**
15
+ * Locale do CALENDÁRIO (meses, dias da semana). Default `ptBR`.
16
+ *
17
+ * Era o único texto em inglês do DS: o trigger já formatava em pt-BR
18
+ * ("12 de março de 2026") enquanto o calendário abria com "March" e "Mo Tu We".
19
+ * Não era escolha — o `Calendar` nunca recebeu `locale`. O `date-fns` já é
20
+ * dependência, então o default custa zero.
21
+ */
22
+ locale?: typeof ptBR;
13
23
  /**
14
24
  * Data mínima selecionável. Dias anteriores ficam desabilitados no calendário.
15
25
  *
@@ -1 +1 @@
1
- {"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,kBAAkB,CAAC;AAY3D,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,UAAU,mBAAmB;IAC3B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,qBAAsB,SAAQ,mBAAmB;IACzD,+BAA+B;IAC/B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,KAAK,IAAI,CAAC;CACnD;AAED,UAAU,oBAAqB,SAAQ,mBAAmB;IACxD,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;CACxD;AAED,UAAU,uBAAwB,SAAQ,mBAAmB;IAC3D,kCAAkC;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,CAAC;AAgB5B;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,2FAsKtB,CAAC"}
1
+ {"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,kBAAkB,CAAC;AAG3D,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAUvC,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,UAAU,mBAAmB;IAC3B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACnC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,IAAI,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,qBAAsB,SAAQ,mBAAmB;IACzD,+BAA+B;IAC/B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,KAAK,IAAI,CAAC;CACnD;AAED,UAAU,oBAAqB,SAAQ,mBAAmB;IACxD,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;CACxD;AAED,UAAU,uBAAwB,SAAQ,mBAAmB;IAC3D,kCAAkC;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,SAAS,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,oBAAoB,GACpB,uBAAuB,CAAC;AAgB5B;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,2FAiMtB,CAAC"}
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Calendar as Calendar$1, X } from "lucide-react";
4
4
  import { cn } from "../../../lib/utils.mjs";
5
+ import { ptBR } from "date-fns/locale";
5
6
  import { Calendar } from "../../shadcn/calendar.mjs";
6
7
  import { Popover, PopoverTrigger, PopoverContent } from "../../shadcn/popover.mjs";
7
8
  const formatFull = (date) => date.toLocaleDateString("pt-BR", {
@@ -20,6 +21,7 @@ const DatePicker = React.forwardRef(
20
21
  placeholder,
21
22
  disabled,
22
23
  align = "start",
24
+ locale = ptBR,
23
25
  minValue,
24
26
  maxValue,
25
27
  clearable,
@@ -27,6 +29,7 @@ const DatePicker = React.forwardRef(
27
29
  className
28
30
  } = props;
29
31
  const [open, setOpen] = React.useState(false);
32
+ const [aguardandoFim, setAguardandoFim] = React.useState(false);
30
33
  const desabilitados = React.useMemo(() => {
31
34
  const m = [];
32
35
  if (minValue) m.push({ before: minValue });
@@ -42,13 +45,22 @@ const DatePicker = React.forwardRef(
42
45
  Calendar,
43
46
  {
44
47
  mode: "range",
48
+ locale,
45
49
  disabled: desabilitados,
46
- min: 1,
47
50
  numberOfMonths: props.numberOfMonths ?? 2,
48
51
  selected: v,
49
52
  onSelect: (range) => {
50
53
  props.onValueChange?.(range);
51
- if (range?.from && range?.to) setOpen(false);
54
+ if (!range?.from) {
55
+ setAguardandoFim(false);
56
+ return;
57
+ }
58
+ if (!aguardandoFim) {
59
+ setAguardandoFim(true);
60
+ return;
61
+ }
62
+ setAguardandoFim(false);
63
+ setOpen(false);
52
64
  },
53
65
  autoFocus: true
54
66
  }
@@ -60,6 +72,7 @@ const DatePicker = React.forwardRef(
60
72
  Calendar,
61
73
  {
62
74
  mode: "multiple",
75
+ locale,
63
76
  disabled: desabilitados,
64
77
  numberOfMonths: props.numberOfMonths,
65
78
  selected: v,
@@ -74,6 +87,7 @@ const DatePicker = React.forwardRef(
74
87
  Calendar,
75
88
  {
76
89
  mode: "single",
90
+ locale,
77
91
  disabled: desabilitados,
78
92
  numberOfMonths: props.numberOfMonths,
79
93
  selected: v,
@@ -84,13 +98,17 @@ const DatePicker = React.forwardRef(
84
98
  }
85
99
  );
86
100
  }
101
+ const aoAbrirFechar = (proximo) => {
102
+ setOpen(proximo);
103
+ if (!proximo) setAguardandoFim(false);
104
+ };
87
105
  const limpar = () => {
88
106
  if (props.mode === "range") props.onValueChange?.(void 0);
89
107
  else if (props.mode === "multiple") props.onValueChange?.(void 0);
90
108
  else props.onValueChange?.(void 0);
91
109
  };
92
110
  const text = label ?? placeholder ?? (props.mode === "range" ? "Selecione o período" : "Selecione a data");
93
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
111
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: aoAbrirFechar, children: [
94
112
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
95
113
  "button",
96
114
  {
@@ -1 +1 @@
1
- {"version":3,"file":"datepicker.mjs","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { Calendar as CalendarIcon } from \"lucide-react\";\r\nimport type { DateRange, Matcher } from \"react-day-picker\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\nimport { X } from \"lucide-react\";\r\n\r\nimport { Calendar } from \"@/components/shadcn/calendar\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\n\r\nexport type { DateRange };\r\n\r\ninterface BaseDatePickerProps {\r\n /** Texto do trigger quando nada está selecionado. */\r\n placeholder?: string;\r\n /** Desabilita o trigger. */\r\n disabled?: boolean;\r\n /** Alinhamento do popover. Default `\"start\"`. */\r\n align?: \"start\" | \"center\" | \"end\";\r\n /** Nº de meses no calendário. Default: 1 (single/multiple), 2 (range). */\r\n numberOfMonths?: number;\r\n /**\r\n * Data mínima selecionável. Dias anteriores ficam desabilitados no calendário.\r\n *\r\n * O DS não oferecia limite nenhum, então \"não deixar escolher data futura\" ou\r\n * \"não passar do prazo do projeto\" virava validação DEPOIS do clique — o usuário\r\n * escolhia, e só então recebia o erro.\r\n */\r\n minValue?: Date;\r\n /** Data máxima selecionável. Dias posteriores ficam desabilitados. */\r\n maxValue?: Date;\r\n /**\r\n * Mostra um \"×\" no trigger pra limpar a seleção. Default `false`.\r\n * Com `clearable`, limpar emite `onValueChange(undefined)`.\r\n */\r\n clearable?: boolean;\r\n /** Nome acessível do botão de limpar. Default: \"Limpar data\". */\r\n clearLabel?: string;\r\n /** className do trigger (mesmos overrides de um input/SelectTrigger). */\r\n className?: string;\r\n}\r\n\r\ninterface SingleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção única (default). */\r\n mode?: \"single\";\r\n value?: Date;\r\n onValueChange?: (value: Date | undefined) => void;\r\n}\r\n\r\ninterface RangeDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de intervalo (início–fim), com o realce de range do DS. */\r\n mode: \"range\";\r\n value?: DateRange;\r\n onValueChange?: (value: DateRange | undefined) => void;\r\n}\r\n\r\ninterface MultipleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de múltiplas datas. */\r\n mode: \"multiple\";\r\n value?: Date[];\r\n onValueChange?: (value: Date[] | undefined) => void;\r\n}\r\n\r\nexport type DatePickerProps =\r\n | SingleDatePickerProps\r\n | RangeDatePickerProps\r\n | MultipleDatePickerProps;\r\n\r\nconst formatFull = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"long\",\r\n year: \"numeric\",\r\n });\r\n\r\nconst formatShort = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n });\r\n\r\n/**\r\n * DatePicker — seletor de data(s) num trigger no estilo input do DS + Popover.\r\n * Suporta os modos do Calendar via `mode` (default `\"single\"`):\r\n * - `\"single\"`: uma data. `value: Date`.\r\n * - `\"range\"`: intervalo início–fim (com o realce de range do DS). `value: DateRange`.\r\n * - `\"multiple\"`: várias datas. `value: Date[]`.\r\n * Controlado via `value` / `onValueChange`. Fecha ao completar a seleção\r\n * (single: no clique; range: quando início E fim escolhidos).\r\n */\r\nexport const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\r\n (props, ref) => {\r\n const {\r\n placeholder,\r\n disabled,\r\n align = \"start\",\r\n minValue,\r\n maxValue,\r\n clearable,\r\n clearLabel,\r\n className,\r\n } = props;\r\n const [open, setOpen] = React.useState(false);\r\n\r\n // O react-day-picker recebe os limites como MATCHERS de dia desabilitado; passar\r\n // `fromDate`/`toDate` só moveria a navegação de mês e continuaria deixando clicar.\r\n const desabilitados = React.useMemo(() => {\r\n const m: Matcher[] = [];\r\n if (minValue) m.push({ before: minValue });\r\n if (maxValue) m.push({ after: maxValue });\r\n return m.length ? m : undefined;\r\n }, [minValue, maxValue]);\r\n\r\n let label: string | null = null;\r\n let calendar: React.ReactNode;\r\n\r\n if (props.mode === \"range\") {\r\n const v = props.value;\r\n label = v?.from\r\n ? v.to\r\n ? `${formatShort(v.from)} – ${formatShort(v.to)}`\r\n : formatShort(v.from)\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"range\"\r\r\n disabled={desabilitados}\r\n // min=1: sem isso o RDP completa o range no PRIMEIRO clique\r\n // ({from,to} iguais), fechando o popover e impedindo escolher o fim.\r\n min={1}\r\n numberOfMonths={props.numberOfMonths ?? 2}\r\n selected={v}\r\n onSelect={(range) => {\r\n props.onValueChange?.(range);\r\n if (range?.from && range?.to) setOpen(false);\r\n }}\r\n autoFocus\r\n />\r\n );\r\n } else if (props.mode === \"multiple\") {\r\n const v = props.value;\r\n // Plural condicional: com 1 selecionada o texto fixo dizia \"1 datas\r\n // selecionadas\". Aparece sempre que o usuário escolhe a primeira data.\r\n label =\r\n v && v.length > 0\r\n ? v.length === 1\r\n ? \"1 data selecionada\"\r\n : `${v.length} datas selecionadas`\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"multiple\"\r\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(dates) => props.onValueChange?.(dates)}\r\n autoFocus\r\n />\r\n );\r\n } else {\r\n const v = props.value;\r\n label = v ? formatFull(v) : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"single\"\r\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(date) => {\r\n props.onValueChange?.(date);\r\n setOpen(false);\r\n }}\r\n />\r\n );\r\n }\r\n\r\n const limpar = () => {\r\n // O union de props impede chamar `onValueChange` sem estreitar o mode antes.\r\n if (props.mode === \"range\") props.onValueChange?.(undefined);\r\n else if (props.mode === \"multiple\") props.onValueChange?.(undefined);\r\n else props.onValueChange?.(undefined);\r\n };\r\n\r\n const text =\r\n label ??\r\n placeholder ??\r\n (props.mode === \"range\" ? \"Selecione o período\" : \"Selecione a data\");\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n disabled={disabled}\r\n data-placeholder={label ? undefined : \"\"}\r\n className={cn(\r\n // Trigger espelha o SelectTrigger (`shadcn/select.tsx`), que é o\r\n // precedente do DS pra \"botão que tem que parecer input\": mesmos\r\n // tokens de bg/border/radius/padding/gap e o mesmo tratamento de\r\n // foco (borda + shadow-sh-ring), em vez de `ring-4` de botão.\r\n // Antes divergia em 6 pontos — bg-surface (opaco) no lugar de\r\n // bg-input (translúcido), border-default, radius-md, pad-lg,\r\n // gp-sm e sem hover — e por isso não lia como campo de formulário.\r\n \"flex min-h-form-lg w-full items-center gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default\",\r\n \"transition-[border-color,box-shadow,background-color] outline-none\",\r\n \"focus-visible:border-border-brand focus-visible:shadow-sh-ring\",\r\n \"data-[state=open]:border-border-brand data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n \"data-[placeholder]:text-fg-muted\",\r\n className,\r\n )}\r\n >\r\n <CalendarIcon className=\"size-icon-sm shrink-0 text-fg-muted\" strokeWidth={1.8} />\r\n <span className=\"truncate\">{text}</span>\r\n {clearable && label && !disabled && (\r\n // `<span role=\"button\">` e não `<button>`: o trigger do Popover JÁ é um\r\n // button, e button dentro de button é HTML inválido — o navegador\r\n // desaninha e o × passa a abrir o calendário junto (mesmo motivo da\r\n // anatomia do Chip com onRemove).\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label={clearLabel ?? \"Limpar data\"}\r\n className=\"-mr-[2px] ml-auto grid size-icon-md shrink-0 place-items-center rounded-radius-full text-fg-muted opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand\"\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key !== \"Enter\" && e.key !== \" \") return;\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n >\r\n <X className=\"size-icon-xs\" aria-hidden />\r\n </span>\r\n )}\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent align={align} className=\"w-auto p-pad-xl\">\r\n {calendar}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\n\r\nDatePicker.displayName = \"DatePicker\";\r\n"],"names":["CalendarIcon"],"mappings":"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"}
1
+ {"version":3,"file":"datepicker.mjs","sources":["../../../../../src/components/ui/DatePicker/datepicker.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport { Calendar as CalendarIcon } from \"lucide-react\";\r\nimport type { DateRange, Matcher } from \"react-day-picker\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\nimport { ptBR } from \"date-fns/locale\";\r\nimport { X } from \"lucide-react\";\r\n\r\nimport { Calendar } from \"@/components/shadcn/calendar\";\r\nimport {\r\n Popover,\r\n PopoverContent,\r\n PopoverTrigger,\r\n} from \"@/components/shadcn/popover\";\r\n\r\nexport type { DateRange };\r\n\r\ninterface BaseDatePickerProps {\r\n /** Texto do trigger quando nada está selecionado. */\r\n placeholder?: string;\r\n /** Desabilita o trigger. */\r\n disabled?: boolean;\r\n /** Alinhamento do popover. Default `\"start\"`. */\r\n align?: \"start\" | \"center\" | \"end\";\r\n /** Nº de meses no calendário. Default: 1 (single/multiple), 2 (range). */\r\n numberOfMonths?: number;\r\n /**\r\n * Locale do CALENDÁRIO (meses, dias da semana). Default `ptBR`.\r\n *\r\n * Era o único texto em inglês do DS: o trigger já formatava em pt-BR\r\n * (\"12 de março de 2026\") enquanto o calendário abria com \"March\" e \"Mo Tu We\".\r\n * Não era escolha — o `Calendar` nunca recebeu `locale`. O `date-fns` já é\r\n * dependência, então o default custa zero.\r\n */\r\n locale?: typeof ptBR;\r\n /**\r\n * Data mínima selecionável. Dias anteriores ficam desabilitados no calendário.\r\n *\r\n * O DS não oferecia limite nenhum, então \"não deixar escolher data futura\" ou\r\n * \"não passar do prazo do projeto\" virava validação DEPOIS do clique — o usuário\r\n * escolhia, e só então recebia o erro.\r\n */\r\n minValue?: Date;\r\n /** Data máxima selecionável. Dias posteriores ficam desabilitados. */\r\n maxValue?: Date;\r\n /**\r\n * Mostra um \"×\" no trigger pra limpar a seleção. Default `false`.\r\n * Com `clearable`, limpar emite `onValueChange(undefined)`.\r\n */\r\n clearable?: boolean;\r\n /** Nome acessível do botão de limpar. Default: \"Limpar data\". */\r\n clearLabel?: string;\r\n /** className do trigger (mesmos overrides de um input/SelectTrigger). */\r\n className?: string;\r\n}\r\n\r\ninterface SingleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção única (default). */\r\n mode?: \"single\";\r\n value?: Date;\r\n onValueChange?: (value: Date | undefined) => void;\r\n}\r\n\r\ninterface RangeDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de intervalo (início–fim), com o realce de range do DS. */\r\n mode: \"range\";\r\n value?: DateRange;\r\n onValueChange?: (value: DateRange | undefined) => void;\r\n}\r\n\r\ninterface MultipleDatePickerProps extends BaseDatePickerProps {\r\n /** Seleção de múltiplas datas. */\r\n mode: \"multiple\";\r\n value?: Date[];\r\n onValueChange?: (value: Date[] | undefined) => void;\r\n}\r\n\r\nexport type DatePickerProps =\r\n | SingleDatePickerProps\r\n | RangeDatePickerProps\r\n | MultipleDatePickerProps;\r\n\r\nconst formatFull = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"long\",\r\n year: \"numeric\",\r\n });\r\n\r\nconst formatShort = (date: Date) =>\r\n date.toLocaleDateString(\"pt-BR\", {\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n });\r\n\r\n/**\r\n * DatePicker — seletor de data(s) num trigger no estilo input do DS + Popover.\r\n * Suporta os modos do Calendar via `mode` (default `\"single\"`):\r\n * - `\"single\"`: uma data. `value: Date`.\r\n * - `\"range\"`: intervalo início–fim (com o realce de range do DS). `value: DateRange`.\r\n * - `\"multiple\"`: várias datas. `value: Date[]`.\r\n * Controlado via `value` / `onValueChange`. Fecha ao completar a seleção\r\n * (single: no clique; range: quando início E fim escolhidos).\r\n */\r\nexport const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(\r\n (props, ref) => {\r\n const {\r\n placeholder,\r\n disabled,\r\n align = \"start\",\r\n locale = ptBR,\r\n minValue,\r\n maxValue,\r\n clearable,\r\n clearLabel,\r\n className,\r\n } = props;\r\n const [open, setOpen] = React.useState(false);\r\n /**\r\n * O range fecha no SEGUNDO clique, não no primeiro.\r\n *\r\n * Antes havia `min={1}` no Calendar, justamente pra o RDP não completar o range no\r\n * primeiro clique e fechar o popover antes de escolher o fim. O efeito colateral era\r\n * tornar impossível um período de UM DIA (`from === to`) — reportado pelo consumidor,\r\n * que manteve o date-range-picker local por causa disso.\r\n *\r\n * Sem o `min`, o primeiro clique já devolve `{ from, to }` iguais; quem segura o\r\n * popover aberto passa a ser este estado, e não o RDP.\r\n */\r\n const [aguardandoFim, setAguardandoFim] = React.useState(false);\r\n\r\n // O react-day-picker recebe os limites como MATCHERS de dia desabilitado; passar\r\n // `fromDate`/`toDate` só moveria a navegação de mês e continuaria deixando clicar.\r\n const desabilitados = React.useMemo(() => {\r\n const m: Matcher[] = [];\r\n if (minValue) m.push({ before: minValue });\r\n if (maxValue) m.push({ after: maxValue });\r\n return m.length ? m : undefined;\r\n }, [minValue, maxValue]);\r\n\r\n let label: string | null = null;\r\n let calendar: React.ReactNode;\r\n\r\n if (props.mode === \"range\") {\r\n const v = props.value;\r\n label = v?.from\r\n ? v.to\r\n ? `${formatShort(v.from)} – ${formatShort(v.to)}`\r\n : formatShort(v.from)\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"range\"\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths ?? 2}\r\n selected={v}\r\n onSelect={(range) => {\r\n props.onValueChange?.(range);\r\n if (!range?.from) {\r\n // Seleção limpa pelo próprio RDP — recomeça a contagem.\r\n setAguardandoFim(false);\r\n return;\r\n }\r\n if (!aguardandoFim) {\r\n setAguardandoFim(true); // 1º clique: segura o popover aberto\r\n return;\r\n }\r\n setAguardandoFim(false);\r\n setOpen(false); // 2º clique fecha, mesmo com from === to\r\n }}\r\n autoFocus\r\n />\r\n );\r\n } else if (props.mode === \"multiple\") {\r\n const v = props.value;\r\n // Plural condicional: com 1 selecionada o texto fixo dizia \"1 datas\r\n // selecionadas\". Aparece sempre que o usuário escolhe a primeira data.\r\n label =\r\n v && v.length > 0\r\n ? v.length === 1\r\n ? \"1 data selecionada\"\r\n : `${v.length} datas selecionadas`\r\n : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"multiple\"\r\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(dates) => props.onValueChange?.(dates)}\r\n autoFocus\r\n />\r\n );\r\n } else {\r\n const v = props.value;\r\n label = v ? formatFull(v) : null;\r\n calendar = (\r\n <Calendar\r\n mode=\"single\"\r\r\n locale={locale}\r\n disabled={desabilitados}\r\n numberOfMonths={props.numberOfMonths}\r\n selected={v}\r\n onSelect={(date) => {\r\n props.onValueChange?.(date);\r\n setOpen(false);\r\n }}\r\n />\r\n );\r\n }\r\n\r\n const aoAbrirFechar = (proximo: boolean) => {\r\n setOpen(proximo);\r\n if (!proximo) setAguardandoFim(false);\r\n };\r\n\r\n const limpar = () => {\r\n // O union de props impede chamar `onValueChange` sem estreitar o mode antes.\r\n if (props.mode === \"range\") props.onValueChange?.(undefined);\r\n else if (props.mode === \"multiple\") props.onValueChange?.(undefined);\r\n else props.onValueChange?.(undefined);\r\n };\r\n\r\n const text =\r\n label ??\r\n placeholder ??\r\n (props.mode === \"range\" ? \"Selecione o período\" : \"Selecione a data\");\r\n\r\n return (\r\n <Popover open={open} onOpenChange={aoAbrirFechar}>\r\n <PopoverTrigger asChild>\r\n <button\r\n ref={ref}\r\n type=\"button\"\r\n disabled={disabled}\r\n data-placeholder={label ? undefined : \"\"}\r\n className={cn(\r\n // Trigger espelha o SelectTrigger (`shadcn/select.tsx`), que é o\r\n // precedente do DS pra \"botão que tem que parecer input\": mesmos\r\n // tokens de bg/border/radius/padding/gap e o mesmo tratamento de\r\n // foco (borda + shadow-sh-ring), em vez de `ring-4` de botão.\r\n // Antes divergia em 6 pontos — bg-surface (opaco) no lugar de\r\n // bg-input (translúcido), border-default, radius-md, pad-lg,\r\n // gp-sm e sem hover — e por isso não lia como campo de formulário.\r\n \"flex min-h-form-lg w-full items-center gap-gp-md\",\r\n \"rounded-radius-lg px-pad-xl\",\r\n \"bg-bg-input dark:bg-bg-muted\",\r\n \"hover:bg-bg-input-hover dark:hover:bg-bg-muted-hover\",\r\n \"border border-border-input\",\r\n \"text-body-sm font-normal text-fg-default\",\r\n \"transition-[border-color,box-shadow,background-color] outline-none\",\r\n \"focus-visible:border-border-brand focus-visible:shadow-sh-ring\",\r\n \"data-[state=open]:border-border-brand data-[state=open]:shadow-sh-ring\",\r\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-bg-input dark:disabled:hover:bg-bg-muted\",\r\n \"data-[placeholder]:text-fg-muted\",\r\n className,\r\n )}\r\n >\r\n <CalendarIcon className=\"size-icon-sm shrink-0 text-fg-muted\" strokeWidth={1.8} />\r\n <span className=\"truncate\">{text}</span>\r\n {clearable && label && !disabled && (\r\n // `<span role=\"button\">` e não `<button>`: o trigger do Popover JÁ é um\r\n // button, e button dentro de button é HTML inválido — o navegador\r\n // desaninha e o × passa a abrir o calendário junto (mesmo motivo da\r\n // anatomia do Chip com onRemove).\r\n <span\r\n role=\"button\"\r\n tabIndex={0}\r\n aria-label={clearLabel ?? \"Limpar data\"}\r\n className=\"-mr-[2px] ml-auto grid size-icon-md shrink-0 place-items-center rounded-radius-full text-fg-muted opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand\"\r\n onClick={(e) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n onKeyDown={(e) => {\r\n if (e.key !== \"Enter\" && e.key !== \" \") return;\r\n e.preventDefault();\r\n e.stopPropagation();\r\n limpar();\r\n }}\r\n >\r\n <X className=\"size-icon-xs\" aria-hidden />\r\n </span>\r\n )}\r\n </button>\r\n </PopoverTrigger>\r\n <PopoverContent align={align} className=\"w-auto p-pad-xl\">\r\n {calendar}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n },\r\n);\r\n\r\nDatePicker.displayName = \"DatePicker\";\r\n"],"names":["CalendarIcon"],"mappings":";;;;;;;AAkFA,MAAM,aAAa,CAAC,SAClB,KAAK,mBAAmB,SAAS;AAAA,EAC/B,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AACR,CAAC;AAEH,MAAM,cAAc,CAAC,SACnB,KAAK,mBAAmB,SAAS;AAAA,EAC/B,KAAK;AAAA,EACL,OAAO;AAAA,EACP,MAAM;AACR,CAAC;AAWI,MAAM,aAAa,MAAM;AAAA,EAC9B,CAAC,OAAO,QAAQ;AACd,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,IACE;AACJ,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAY5C,UAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAS,KAAK;AAI9D,UAAM,gBAAgB,MAAM,QAAQ,MAAM;AACxC,YAAM,IAAe,CAAA;AACrB,UAAI,SAAU,GAAE,KAAK,EAAE,QAAQ,UAAU;AACzC,UAAI,SAAU,GAAE,KAAK,EAAE,OAAO,UAAU;AACxC,aAAO,EAAE,SAAS,IAAI;AAAA,IACxB,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,QAAI,QAAuB;AAC3B,QAAI;AAEJ,QAAI,MAAM,SAAS,SAAS;AAC1B,YAAM,IAAI,MAAM;AAChB,cAAQ,GAAG,OACP,EAAE,KACA,GAAG,YAAY,EAAE,IAAI,CAAC,MAAM,YAAY,EAAE,EAAE,CAAC,KAC7C,YAAY,EAAE,IAAI,IACpB;AACJ,iBACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA,UAAU;AAAA,UACV,gBAAgB,MAAM,kBAAkB;AAAA,UACxC,UAAU;AAAA,UACV,UAAU,CAAC,UAAU;AACnB,kBAAM,gBAAgB,KAAK;AAC3B,gBAAI,CAAC,OAAO,MAAM;AAEhB,+BAAiB,KAAK;AACtB;AAAA,YACF;AACA,gBAAI,CAAC,eAAe;AAClB,+BAAiB,IAAI;AACrB;AAAA,YACF;AACA,6BAAiB,KAAK;AACtB,oBAAQ,KAAK;AAAA,UACf;AAAA,UACA,WAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAGf,WAAW,MAAM,SAAS,YAAY;AACpC,YAAM,IAAI,MAAM;AAGhB,cACE,KAAK,EAAE,SAAS,IACZ,EAAE,WAAW,IACX,uBACA,GAAG,EAAE,MAAM,wBACb;AACN,iBACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UAEL;AAAA,UACA,UAAU;AAAA,UACV,gBAAgB,MAAM;AAAA,UACtB,UAAU;AAAA,UACV,UAAU,CAAC,UAAU,MAAM,gBAAgB,KAAK;AAAA,UAChD,WAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAGf,OAAO;AACL,YAAM,IAAI,MAAM;AAChB,cAAQ,IAAI,WAAW,CAAC,IAAI;AAC5B,iBACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UAEL;AAAA,UACA,UAAU;AAAA,UACV,gBAAgB,MAAM;AAAA,UACtB,UAAU;AAAA,UACV,UAAU,CAAC,SAAS;AAClB,kBAAM,gBAAgB,IAAI;AAC1B,oBAAQ,KAAK;AAAA,UACf;AAAA,QAAA;AAAA,MAAA;AAAA,IAGN;AAEA,UAAM,gBAAgB,CAAC,YAAqB;AAC1C,cAAQ,OAAO;AACf,UAAI,CAAC,QAAS,kBAAiB,KAAK;AAAA,IACtC;AAEA,UAAM,SAAS,MAAM;AAEnB,UAAI,MAAM,SAAS,QAAS,OAAM,gBAAgB,MAAS;AAAA,eAClD,MAAM,SAAS,WAAY,OAAM,gBAAgB,MAAS;AAAA,UAC9D,OAAM,gBAAgB,MAAS;AAAA,IACtC;AAEA,UAAM,OACJ,SACA,gBACC,MAAM,SAAS,UAAU,wBAAwB;AAEpD,WACE,qBAAC,SAAA,EAAQ,MAAY,cAAc,eACjC,UAAA;AAAA,MAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,MAAK;AAAA,UACL;AAAA,UACA,oBAAkB,QAAQ,SAAY;AAAA,UACtC,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAQT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAAA,UAGF,UAAA;AAAA,YAAA,oBAACA,YAAA,EAAa,WAAU,uCAAsC,aAAa,KAAK;AAAA,YAChF,oBAAC,QAAA,EAAK,WAAU,YAAY,UAAA,MAAK;AAAA,YAChC,aAAa,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,YAKtB;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,cAAY,cAAc;AAAA,gBAC1B,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM;AACd,oBAAE,eAAA;AACF,oBAAE,gBAAA;AACF,yBAAA;AAAA,gBACF;AAAA,gBACA,WAAW,CAAC,MAAM;AAChB,sBAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK;AACxC,oBAAE,eAAA;AACF,oBAAE,gBAAA;AACF,yBAAA;AAAA,gBACF;AAAA,gBAEA,UAAA,oBAAC,GAAA,EAAE,WAAU,gBAAe,eAAW,KAAA,CAAC;AAAA,cAAA;AAAA,YAAA;AAAA,UAC1C;AAAA,QAAA;AAAA,MAAA,GAGN;AAAA,MACA,oBAAC,gBAAA,EAAe,OAAc,WAAU,mBACrC,UAAA,SAAA,CACH;AAAA,IAAA,GACF;AAAA,EAEJ;AACF;AAEA,WAAW,cAAc;"}
@@ -90,10 +90,7 @@ function FileUploadField({
90
90
  onChange(null);
91
91
  resetInput();
92
92
  };
93
- const hint = [
94
- accept ? accept : null,
95
- maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null
96
- ].filter(Boolean).join(" · ");
93
+ const hint = texts?.hint ?? [accept ? accept : null, maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null].filter(Boolean).join(" · ");
97
94
  const chamada = texts?.drop ?? "Clique para anexar";
98
95
  const displayName = value instanceof File ? value.name : fileName ?? texts?.fallbackFileName ?? "Arquivo";
99
96
  const isImage = value != null && resolveIsImage(value, preview, accept);
@@ -1 +1 @@
1
- {"version":3,"file":"file-upload-field.cjs","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { FormField } from \"@/components/ui/FormField\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { IconSvg } from \"@/components/ui/Icon/icon-svg\";\r\nimport { lineBin, lineCloudUpload, lineFile } from \"@/components/ui/Icon/icon-glyphs\";\r\nimport { fileUploadFieldStyles } from \"./file-upload-field.styles\";\r\nimport type {\r\n FileUploadFieldProps,\r\n FileUploadPreview,\r\n} from \"./file-upload-field.types\";\r\n\r\n/** Decide se o value deve ser exibido como imagem (thumbnail) ou como chip de arquivo. */\r\nfunction resolveIsImage(\r\n value: File | string,\r\n preview: FileUploadPreview,\r\n accept?: string,\r\n): boolean {\r\n if (preview === \"image\") return true;\r\n if (preview === \"file\") return false;\r\n // auto — infere por MIME (File) ou por accept (URL string)\r\n if (value instanceof File) return value.type.startsWith(\"image/\");\r\n if (accept) return /image\\//.test(accept) || /\\.(png|jpe?g|gif|webp|svg|avif)/i.test(accept);\r\n return /\\.(png|jpe?g|gif|webp|svg|avif)(\\?|$)/i.test(value);\r\n}\r\n\r\n/** Valida tipo contra `accept` (suporta `image/*`, `image/png`, `.pdf`). */\r\nfunction matchesAccept(file: File, accept?: string): boolean {\r\n if (!accept) return true;\r\n const tokens = accept.split(\",\").map((t) => t.trim().toLowerCase());\r\n const name = file.name.toLowerCase();\r\n const mime = file.type.toLowerCase();\r\n return tokens.some((token) => {\r\n if (!token) return false;\r\n if (token.startsWith(\".\")) return name.endsWith(token);\r\n if (token.endsWith(\"/*\")) return mime.startsWith(token.slice(0, token.length - 1));\r\n return mime === token;\r\n });\r\n}\r\n\r\n/**\r\n * FileUploadField — captura de UM arquivo (dumb: só captura File + preview).\r\n * O consumidor é responsável pelo upload. Composto de FormField + Button + Chip + Icon.\r\n *\r\n * - Estado VAZIO (`value == null`): dropzone `<button>` que abre o seletor nativo.\r\n * - Estado COM-ARQUIVO: preview (thumbnail de imagem ou Chip de arquivo) + remover.\r\n * - Rejeição por tipo/tamanho: arquivo NÃO vira value; dispara `onError(\"type\"|\"size\")`.\r\n *\r\n * A11y: dropzone é `<button>` (Enter/Space abrem o seletor); input file hidden\r\n * com id linkado ao FormField; thumbnail com `alt`; remover icon-only com aria-label.\r\n * Drag & drop é fase 2 (não implementado — API preservada).\r\n */\r\nexport function FileUploadField({\r\n value,\r\n onChange,\r\n accept,\r\n maxSizeMB,\r\n preview = \"auto\",\r\r\n fileName,\r\n texts,\r\n label,\r\n required,\r\n state,\r\n errorMessage,\r\n helperText,\r\n onError,\r\n disabled,\r\n id,\r\n className,\r\n}: FileUploadFieldProps) {\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const styles = fileUploadFieldStyles({ disabled });\r\n\r\n // Object URL pra preview de File (revogado ao trocar/desmontar).\r\n const [objectUrl, setObjectUrl] = useState<string | null>(null);\r\n useEffect(() => {\r\n if (value instanceof File) {\r\n const url = URL.createObjectURL(value);\r\n setObjectUrl(url);\r\n return () => URL.revokeObjectURL(url);\r\n }\r\n setObjectUrl(null);\r\n return undefined;\r\n }, [value]);\r\n\r\n const openPicker = () => {\r\n if (disabled) return;\r\n inputRef.current?.click();\r\n };\r\n\r\n const handleFiles = (file: File | undefined) => {\r\n if (!file) return;\r\n if (!matchesAccept(file, accept)) {\r\n onError?.(\"type\");\r\n resetInput();\r\n return;\r\n }\r\n if (maxSizeMB != null && file.size > maxSizeMB * 1024 * 1024) {\r\n onError?.(\"size\");\r\n resetInput();\r\n return;\r\n }\r\n onChange(file);\r\n };\r\n\r\n const resetInput = () => {\r\n if (inputRef.current) inputRef.current.value = \"\";\r\n };\r\n\r\n const handleRemove = () => {\r\n onChange(null);\r\n resetInput();\r\n };\r\n\r\n const hint = [\r\n accept ? accept : null,\r\n maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null,\r\n ]\r\n .filter(Boolean)\r\n .join(\" · \");\r\n\r\n /** Chamada da área de drop. Literal PT-BR era cravado aqui até 2026-09-23. */\r\n const chamada = texts?.drop ?? \"Clique para anexar\";\r\n\r\n const displayName =\r\n value instanceof File\r\n ? value.name\r\n : (fileName ?? texts?.fallbackFileName ?? \"Arquivo\");\r\n const isImage = value != null && resolveIsImage(value, preview, accept);\r\n const imageSrc = value instanceof File ? objectUrl : (value as string | null);\r\n\r\n return (\r\n <FormField\r\n label={label}\r\n required={required}\r\n state={state}\r\n errorMessage={errorMessage}\r\n helperText={helperText}\r\n className={className}\r\n id={id}\r\n disabled={disabled}\r\n >\r\n {({ id: fieldId }) => (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n id={fieldId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={disabled}\r\n className=\"hidden\"\r\n onChange={(e) => handleFiles(e.target.files?.[0])}\r\n />\r\n\r\n {value == null ? (\r\n <button\r\n type=\"button\"\r\n onClick={openPicker}\r\n disabled={disabled}\r\n aria-label={\r\n label ? `${label}: ${chamada.toLowerCase()}` : chamada\r\n }\r\n className={styles.dropzone()}\r\n >\r\n <IconSvg glyph={lineCloudUpload} size=\"lg\" className={styles.dropIcon()} />\r\n <span className={styles.dropTexts()}>\r\n <span className={styles.dropText()}>{chamada}</span>\r\n {hint && <span className={styles.dropHint()}>{hint}</span>}\r\n </span>\r\n </button>\r\n ) : (\r\n <div className={styles.fileRow()}>\r\n {isImage && imageSrc ? (\r\n <img src={imageSrc} alt={displayName} className={styles.thumb()} />\r\n ) : (\r\n <div className={styles.fileChipWrap()}>\r\n <Chip color=\"neutral\" variant=\"soft\" size=\"lg\" className=\"max-w-full\">\r\n <IconSvg glyph={lineFile} size=\"sm\" />\r\n <span className={styles.fileName()}>{displayName}</span>\r\n </Chip>\r\n </div>\r\n )}\r\n <div className={styles.removeWrap()}>\r\n <Button\r\n type=\"button\"\r\n color=\"critical\"\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n disabled={disabled}\r\n aria-label={texts?.remove ?? \"Remover arquivo\"}\r\n onClick={handleRemove}\r\n >\r\n <IconSvg glyph={lineBin} size=\"sm\" />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </FormField>\r\n );\r\n}\r\n"],"names":["useRef","fileUploadFieldStyles","useState","useEffect","jsx","FormField","jsxs","IconSvg","lineCloudUpload","Chip","lineFile","Button","lineBin"],"mappings":"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"}
1
+ {"version":3,"file":"file-upload-field.cjs","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { FormField } from \"@/components/ui/FormField\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { IconSvg } from \"@/components/ui/Icon/icon-svg\";\r\nimport { lineBin, lineCloudUpload, lineFile } from \"@/components/ui/Icon/icon-glyphs\";\r\nimport { fileUploadFieldStyles } from \"./file-upload-field.styles\";\r\nimport type {\r\n FileUploadFieldProps,\r\n FileUploadPreview,\r\n} from \"./file-upload-field.types\";\r\n\r\n/** Decide se o value deve ser exibido como imagem (thumbnail) ou como chip de arquivo. */\r\nfunction resolveIsImage(\r\n value: File | string,\r\n preview: FileUploadPreview,\r\n accept?: string,\r\n): boolean {\r\n if (preview === \"image\") return true;\r\n if (preview === \"file\") return false;\r\n // auto — infere por MIME (File) ou por accept (URL string)\r\n if (value instanceof File) return value.type.startsWith(\"image/\");\r\n if (accept) return /image\\//.test(accept) || /\\.(png|jpe?g|gif|webp|svg|avif)/i.test(accept);\r\n return /\\.(png|jpe?g|gif|webp|svg|avif)(\\?|$)/i.test(value);\r\n}\r\n\r\n/** Valida tipo contra `accept` (suporta `image/*`, `image/png`, `.pdf`). */\r\nfunction matchesAccept(file: File, accept?: string): boolean {\r\n if (!accept) return true;\r\n const tokens = accept.split(\",\").map((t) => t.trim().toLowerCase());\r\n const name = file.name.toLowerCase();\r\n const mime = file.type.toLowerCase();\r\n return tokens.some((token) => {\r\n if (!token) return false;\r\n if (token.startsWith(\".\")) return name.endsWith(token);\r\n if (token.endsWith(\"/*\")) return mime.startsWith(token.slice(0, token.length - 1));\r\n return mime === token;\r\n });\r\n}\r\n\r\n/**\r\n * FileUploadField — captura de UM arquivo (dumb: só captura File + preview).\r\n * O consumidor é responsável pelo upload. Composto de FormField + Button + Chip + Icon.\r\n *\r\n * - Estado VAZIO (`value == null`): dropzone `<button>` que abre o seletor nativo.\r\n * - Estado COM-ARQUIVO: preview (thumbnail de imagem ou Chip de arquivo) + remover.\r\n * - Rejeição por tipo/tamanho: arquivo NÃO vira value; dispara `onError(\"type\"|\"size\")`.\r\n *\r\n * A11y: dropzone é `<button>` (Enter/Space abrem o seletor); input file hidden\r\n * com id linkado ao FormField; thumbnail com `alt`; remover icon-only com aria-label.\r\n * Drag & drop é fase 2 (não implementado — API preservada).\r\n */\r\nexport function FileUploadField({\r\n value,\r\n onChange,\r\n accept,\r\n maxSizeMB,\r\n preview = \"auto\",\r\n fileName,\r\n texts,\r\n label,\r\n required,\r\n state,\r\n errorMessage,\r\n helperText,\r\n onError,\r\n disabled,\r\n id,\r\n className,\r\n}: FileUploadFieldProps) {\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const styles = fileUploadFieldStyles({ disabled });\r\n\r\n // Object URL pra preview de File (revogado ao trocar/desmontar).\r\n const [objectUrl, setObjectUrl] = useState<string | null>(null);\r\n useEffect(() => {\r\n if (value instanceof File) {\r\n const url = URL.createObjectURL(value);\r\n setObjectUrl(url);\r\n return () => URL.revokeObjectURL(url);\r\n }\r\n setObjectUrl(null);\r\n return undefined;\r\n }, [value]);\r\n\r\n const openPicker = () => {\r\n if (disabled) return;\r\n inputRef.current?.click();\r\n };\r\n\r\n const handleFiles = (file: File | undefined) => {\r\n if (!file) return;\r\n if (!matchesAccept(file, accept)) {\r\n onError?.(\"type\");\r\n resetInput();\r\n return;\r\n }\r\n if (maxSizeMB != null && file.size > maxSizeMB * 1024 * 1024) {\r\n onError?.(\"size\");\r\n resetInput();\r\n return;\r\n }\r\n onChange(file);\r\n };\r\n\r\n const resetInput = () => {\r\n if (inputRef.current) inputRef.current.value = \"\";\r\n };\r\n\r\n const handleRemove = () => {\r\n onChange(null);\r\n resetInput();\r\n };\r\n\r\n /**\r\n * A dica derivada é `accept` cru — ou seja, a lista de MIME types. Serve pra\r\n * `accept=\".pdf\"`, e não serve pra importação em lote, onde `accept` tem 3 MIME\r\n * longos. `texts.hint` sobrescreve; `\"\"` esconde a linha.\r\n */\r\n const hint =\r\n texts?.hint ??\r\n [accept ? accept : null, maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null]\r\n .filter(Boolean)\r\n .join(\" · \");\r\n\r\n /** Chamada da área de drop. Literal PT-BR era cravado aqui até 2026-09-23. */\r\n const chamada = texts?.drop ?? \"Clique para anexar\";\r\n\r\n const displayName =\r\n value instanceof File\r\n ? value.name\r\n : (fileName ?? texts?.fallbackFileName ?? \"Arquivo\");\r\n const isImage = value != null && resolveIsImage(value, preview, accept);\r\n const imageSrc = value instanceof File ? objectUrl : (value as string | null);\r\n\r\n return (\r\n <FormField\r\n label={label}\r\n required={required}\r\n state={state}\r\n errorMessage={errorMessage}\r\n helperText={helperText}\r\n className={className}\r\n id={id}\r\n disabled={disabled}\r\n >\r\n {({ id: fieldId }) => (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n id={fieldId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={disabled}\r\n className=\"hidden\"\r\n onChange={(e) => handleFiles(e.target.files?.[0])}\r\n />\r\n\r\n {value == null ? (\r\n <button\r\n type=\"button\"\r\n onClick={openPicker}\r\n disabled={disabled}\r\n aria-label={\r\n label ? `${label}: ${chamada.toLowerCase()}` : chamada\r\n }\r\n className={styles.dropzone()}\r\n >\r\n <IconSvg glyph={lineCloudUpload} size=\"lg\" className={styles.dropIcon()} />\r\n <span className={styles.dropTexts()}>\r\n <span className={styles.dropText()}>{chamada}</span>\r\n {hint && <span className={styles.dropHint()}>{hint}</span>}\r\n </span>\r\n </button>\r\n ) : (\r\n <div className={styles.fileRow()}>\r\n {isImage && imageSrc ? (\r\n <img src={imageSrc} alt={displayName} className={styles.thumb()} />\r\n ) : (\r\n <div className={styles.fileChipWrap()}>\r\n <Chip color=\"neutral\" variant=\"soft\" size=\"lg\" className=\"max-w-full\">\r\n <IconSvg glyph={lineFile} size=\"sm\" />\r\n <span className={styles.fileName()}>{displayName}</span>\r\n </Chip>\r\n </div>\r\n )}\r\n <div className={styles.removeWrap()}>\r\n <Button\r\n type=\"button\"\r\n color=\"critical\"\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n disabled={disabled}\r\n aria-label={texts?.remove ?? \"Remover arquivo\"}\r\n onClick={handleRemove}\r\n >\r\n <IconSvg glyph={lineBin} size=\"sm\" />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </FormField>\r\n );\r\n}\r\n"],"names":["useRef","fileUploadFieldStyles","useState","useEffect","jsx","FormField","jsxs","IconSvg","lineCloudUpload","Chip","lineFile","Button","lineBin"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-upload-field.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,2BAA2B,CAAC;AA8BnC;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,OAAgB,EAEhB,QAAQ,EACR,KAAK,EACL,KAAK,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,QAAQ,EACR,EAAE,EACF,SAAS,GACV,EAAE,oBAAoB,2CAoItB"}
1
+ {"version":3,"file":"file-upload-field.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,2BAA2B,CAAC;AA8BnC;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,OAAgB,EAChB,QAAQ,EACR,KAAK,EACL,KAAK,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,QAAQ,EACR,EAAE,EACF,SAAS,GACV,EAAE,oBAAoB,2CAwItB"}
@@ -88,10 +88,7 @@ function FileUploadField({
88
88
  onChange(null);
89
89
  resetInput();
90
90
  };
91
- const hint = [
92
- accept ? accept : null,
93
- maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null
94
- ].filter(Boolean).join(" · ");
91
+ const hint = texts?.hint ?? [accept ? accept : null, maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null].filter(Boolean).join(" · ");
95
92
  const chamada = texts?.drop ?? "Clique para anexar";
96
93
  const displayName = value instanceof File ? value.name : fileName ?? texts?.fallbackFileName ?? "Arquivo";
97
94
  const isImage = value != null && resolveIsImage(value, preview, accept);
@@ -1 +1 @@
1
- {"version":3,"file":"file-upload-field.mjs","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { FormField } from \"@/components/ui/FormField\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { IconSvg } from \"@/components/ui/Icon/icon-svg\";\r\nimport { lineBin, lineCloudUpload, lineFile } from \"@/components/ui/Icon/icon-glyphs\";\r\nimport { fileUploadFieldStyles } from \"./file-upload-field.styles\";\r\nimport type {\r\n FileUploadFieldProps,\r\n FileUploadPreview,\r\n} from \"./file-upload-field.types\";\r\n\r\n/** Decide se o value deve ser exibido como imagem (thumbnail) ou como chip de arquivo. */\r\nfunction resolveIsImage(\r\n value: File | string,\r\n preview: FileUploadPreview,\r\n accept?: string,\r\n): boolean {\r\n if (preview === \"image\") return true;\r\n if (preview === \"file\") return false;\r\n // auto — infere por MIME (File) ou por accept (URL string)\r\n if (value instanceof File) return value.type.startsWith(\"image/\");\r\n if (accept) return /image\\//.test(accept) || /\\.(png|jpe?g|gif|webp|svg|avif)/i.test(accept);\r\n return /\\.(png|jpe?g|gif|webp|svg|avif)(\\?|$)/i.test(value);\r\n}\r\n\r\n/** Valida tipo contra `accept` (suporta `image/*`, `image/png`, `.pdf`). */\r\nfunction matchesAccept(file: File, accept?: string): boolean {\r\n if (!accept) return true;\r\n const tokens = accept.split(\",\").map((t) => t.trim().toLowerCase());\r\n const name = file.name.toLowerCase();\r\n const mime = file.type.toLowerCase();\r\n return tokens.some((token) => {\r\n if (!token) return false;\r\n if (token.startsWith(\".\")) return name.endsWith(token);\r\n if (token.endsWith(\"/*\")) return mime.startsWith(token.slice(0, token.length - 1));\r\n return mime === token;\r\n });\r\n}\r\n\r\n/**\r\n * FileUploadField — captura de UM arquivo (dumb: só captura File + preview).\r\n * O consumidor é responsável pelo upload. Composto de FormField + Button + Chip + Icon.\r\n *\r\n * - Estado VAZIO (`value == null`): dropzone `<button>` que abre o seletor nativo.\r\n * - Estado COM-ARQUIVO: preview (thumbnail de imagem ou Chip de arquivo) + remover.\r\n * - Rejeição por tipo/tamanho: arquivo NÃO vira value; dispara `onError(\"type\"|\"size\")`.\r\n *\r\n * A11y: dropzone é `<button>` (Enter/Space abrem o seletor); input file hidden\r\n * com id linkado ao FormField; thumbnail com `alt`; remover icon-only com aria-label.\r\n * Drag & drop é fase 2 (não implementado — API preservada).\r\n */\r\nexport function FileUploadField({\r\n value,\r\n onChange,\r\n accept,\r\n maxSizeMB,\r\n preview = \"auto\",\r\r\n fileName,\r\n texts,\r\n label,\r\n required,\r\n state,\r\n errorMessage,\r\n helperText,\r\n onError,\r\n disabled,\r\n id,\r\n className,\r\n}: FileUploadFieldProps) {\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const styles = fileUploadFieldStyles({ disabled });\r\n\r\n // Object URL pra preview de File (revogado ao trocar/desmontar).\r\n const [objectUrl, setObjectUrl] = useState<string | null>(null);\r\n useEffect(() => {\r\n if (value instanceof File) {\r\n const url = URL.createObjectURL(value);\r\n setObjectUrl(url);\r\n return () => URL.revokeObjectURL(url);\r\n }\r\n setObjectUrl(null);\r\n return undefined;\r\n }, [value]);\r\n\r\n const openPicker = () => {\r\n if (disabled) return;\r\n inputRef.current?.click();\r\n };\r\n\r\n const handleFiles = (file: File | undefined) => {\r\n if (!file) return;\r\n if (!matchesAccept(file, accept)) {\r\n onError?.(\"type\");\r\n resetInput();\r\n return;\r\n }\r\n if (maxSizeMB != null && file.size > maxSizeMB * 1024 * 1024) {\r\n onError?.(\"size\");\r\n resetInput();\r\n return;\r\n }\r\n onChange(file);\r\n };\r\n\r\n const resetInput = () => {\r\n if (inputRef.current) inputRef.current.value = \"\";\r\n };\r\n\r\n const handleRemove = () => {\r\n onChange(null);\r\n resetInput();\r\n };\r\n\r\n const hint = [\r\n accept ? accept : null,\r\n maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null,\r\n ]\r\n .filter(Boolean)\r\n .join(\" · \");\r\n\r\n /** Chamada da área de drop. Literal PT-BR era cravado aqui até 2026-09-23. */\r\n const chamada = texts?.drop ?? \"Clique para anexar\";\r\n\r\n const displayName =\r\n value instanceof File\r\n ? value.name\r\n : (fileName ?? texts?.fallbackFileName ?? \"Arquivo\");\r\n const isImage = value != null && resolveIsImage(value, preview, accept);\r\n const imageSrc = value instanceof File ? objectUrl : (value as string | null);\r\n\r\n return (\r\n <FormField\r\n label={label}\r\n required={required}\r\n state={state}\r\n errorMessage={errorMessage}\r\n helperText={helperText}\r\n className={className}\r\n id={id}\r\n disabled={disabled}\r\n >\r\n {({ id: fieldId }) => (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n id={fieldId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={disabled}\r\n className=\"hidden\"\r\n onChange={(e) => handleFiles(e.target.files?.[0])}\r\n />\r\n\r\n {value == null ? (\r\n <button\r\n type=\"button\"\r\n onClick={openPicker}\r\n disabled={disabled}\r\n aria-label={\r\n label ? `${label}: ${chamada.toLowerCase()}` : chamada\r\n }\r\n className={styles.dropzone()}\r\n >\r\n <IconSvg glyph={lineCloudUpload} size=\"lg\" className={styles.dropIcon()} />\r\n <span className={styles.dropTexts()}>\r\n <span className={styles.dropText()}>{chamada}</span>\r\n {hint && <span className={styles.dropHint()}>{hint}</span>}\r\n </span>\r\n </button>\r\n ) : (\r\n <div className={styles.fileRow()}>\r\n {isImage && imageSrc ? (\r\n <img src={imageSrc} alt={displayName} className={styles.thumb()} />\r\n ) : (\r\n <div className={styles.fileChipWrap()}>\r\n <Chip color=\"neutral\" variant=\"soft\" size=\"lg\" className=\"max-w-full\">\r\n <IconSvg glyph={lineFile} size=\"sm\" />\r\n <span className={styles.fileName()}>{displayName}</span>\r\n </Chip>\r\n </div>\r\n )}\r\n <div className={styles.removeWrap()}>\r\n <Button\r\n type=\"button\"\r\n color=\"critical\"\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n disabled={disabled}\r\n aria-label={texts?.remove ?? \"Remover arquivo\"}\r\n onClick={handleRemove}\r\n >\r\n <IconSvg glyph={lineBin} size=\"sm\" />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </FormField>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-upload-field.mjs","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { FormField } from \"@/components/ui/FormField\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { Chip } from \"@/components/ui/Chip\";\r\nimport { IconSvg } from \"@/components/ui/Icon/icon-svg\";\r\nimport { lineBin, lineCloudUpload, lineFile } from \"@/components/ui/Icon/icon-glyphs\";\r\nimport { fileUploadFieldStyles } from \"./file-upload-field.styles\";\r\nimport type {\r\n FileUploadFieldProps,\r\n FileUploadPreview,\r\n} from \"./file-upload-field.types\";\r\n\r\n/** Decide se o value deve ser exibido como imagem (thumbnail) ou como chip de arquivo. */\r\nfunction resolveIsImage(\r\n value: File | string,\r\n preview: FileUploadPreview,\r\n accept?: string,\r\n): boolean {\r\n if (preview === \"image\") return true;\r\n if (preview === \"file\") return false;\r\n // auto — infere por MIME (File) ou por accept (URL string)\r\n if (value instanceof File) return value.type.startsWith(\"image/\");\r\n if (accept) return /image\\//.test(accept) || /\\.(png|jpe?g|gif|webp|svg|avif)/i.test(accept);\r\n return /\\.(png|jpe?g|gif|webp|svg|avif)(\\?|$)/i.test(value);\r\n}\r\n\r\n/** Valida tipo contra `accept` (suporta `image/*`, `image/png`, `.pdf`). */\r\nfunction matchesAccept(file: File, accept?: string): boolean {\r\n if (!accept) return true;\r\n const tokens = accept.split(\",\").map((t) => t.trim().toLowerCase());\r\n const name = file.name.toLowerCase();\r\n const mime = file.type.toLowerCase();\r\n return tokens.some((token) => {\r\n if (!token) return false;\r\n if (token.startsWith(\".\")) return name.endsWith(token);\r\n if (token.endsWith(\"/*\")) return mime.startsWith(token.slice(0, token.length - 1));\r\n return mime === token;\r\n });\r\n}\r\n\r\n/**\r\n * FileUploadField — captura de UM arquivo (dumb: só captura File + preview).\r\n * O consumidor é responsável pelo upload. Composto de FormField + Button + Chip + Icon.\r\n *\r\n * - Estado VAZIO (`value == null`): dropzone `<button>` que abre o seletor nativo.\r\n * - Estado COM-ARQUIVO: preview (thumbnail de imagem ou Chip de arquivo) + remover.\r\n * - Rejeição por tipo/tamanho: arquivo NÃO vira value; dispara `onError(\"type\"|\"size\")`.\r\n *\r\n * A11y: dropzone é `<button>` (Enter/Space abrem o seletor); input file hidden\r\n * com id linkado ao FormField; thumbnail com `alt`; remover icon-only com aria-label.\r\n * Drag & drop é fase 2 (não implementado — API preservada).\r\n */\r\nexport function FileUploadField({\r\n value,\r\n onChange,\r\n accept,\r\n maxSizeMB,\r\n preview = \"auto\",\r\n fileName,\r\n texts,\r\n label,\r\n required,\r\n state,\r\n errorMessage,\r\n helperText,\r\n onError,\r\n disabled,\r\n id,\r\n className,\r\n}: FileUploadFieldProps) {\r\n const inputRef = useRef<HTMLInputElement>(null);\r\n const styles = fileUploadFieldStyles({ disabled });\r\n\r\n // Object URL pra preview de File (revogado ao trocar/desmontar).\r\n const [objectUrl, setObjectUrl] = useState<string | null>(null);\r\n useEffect(() => {\r\n if (value instanceof File) {\r\n const url = URL.createObjectURL(value);\r\n setObjectUrl(url);\r\n return () => URL.revokeObjectURL(url);\r\n }\r\n setObjectUrl(null);\r\n return undefined;\r\n }, [value]);\r\n\r\n const openPicker = () => {\r\n if (disabled) return;\r\n inputRef.current?.click();\r\n };\r\n\r\n const handleFiles = (file: File | undefined) => {\r\n if (!file) return;\r\n if (!matchesAccept(file, accept)) {\r\n onError?.(\"type\");\r\n resetInput();\r\n return;\r\n }\r\n if (maxSizeMB != null && file.size > maxSizeMB * 1024 * 1024) {\r\n onError?.(\"size\");\r\n resetInput();\r\n return;\r\n }\r\n onChange(file);\r\n };\r\n\r\n const resetInput = () => {\r\n if (inputRef.current) inputRef.current.value = \"\";\r\n };\r\n\r\n const handleRemove = () => {\r\n onChange(null);\r\n resetInput();\r\n };\r\n\r\n /**\r\n * A dica derivada é `accept` cru — ou seja, a lista de MIME types. Serve pra\r\n * `accept=\".pdf\"`, e não serve pra importação em lote, onde `accept` tem 3 MIME\r\n * longos. `texts.hint` sobrescreve; `\"\"` esconde a linha.\r\n */\r\n const hint =\r\n texts?.hint ??\r\n [accept ? accept : null, maxSizeMB != null ? `máx. ${maxSizeMB}MB` : null]\r\n .filter(Boolean)\r\n .join(\" · \");\r\n\r\n /** Chamada da área de drop. Literal PT-BR era cravado aqui até 2026-09-23. */\r\n const chamada = texts?.drop ?? \"Clique para anexar\";\r\n\r\n const displayName =\r\n value instanceof File\r\n ? value.name\r\n : (fileName ?? texts?.fallbackFileName ?? \"Arquivo\");\r\n const isImage = value != null && resolveIsImage(value, preview, accept);\r\n const imageSrc = value instanceof File ? objectUrl : (value as string | null);\r\n\r\n return (\r\n <FormField\r\n label={label}\r\n required={required}\r\n state={state}\r\n errorMessage={errorMessage}\r\n helperText={helperText}\r\n className={className}\r\n id={id}\r\n disabled={disabled}\r\n >\r\n {({ id: fieldId }) => (\r\n <div>\r\n <input\r\n ref={inputRef}\r\n id={fieldId}\r\n type=\"file\"\r\n accept={accept}\r\n disabled={disabled}\r\n className=\"hidden\"\r\n onChange={(e) => handleFiles(e.target.files?.[0])}\r\n />\r\n\r\n {value == null ? (\r\n <button\r\n type=\"button\"\r\n onClick={openPicker}\r\n disabled={disabled}\r\n aria-label={\r\n label ? `${label}: ${chamada.toLowerCase()}` : chamada\r\n }\r\n className={styles.dropzone()}\r\n >\r\n <IconSvg glyph={lineCloudUpload} size=\"lg\" className={styles.dropIcon()} />\r\n <span className={styles.dropTexts()}>\r\n <span className={styles.dropText()}>{chamada}</span>\r\n {hint && <span className={styles.dropHint()}>{hint}</span>}\r\n </span>\r\n </button>\r\n ) : (\r\n <div className={styles.fileRow()}>\r\n {isImage && imageSrc ? (\r\n <img src={imageSrc} alt={displayName} className={styles.thumb()} />\r\n ) : (\r\n <div className={styles.fileChipWrap()}>\r\n <Chip color=\"neutral\" variant=\"soft\" size=\"lg\" className=\"max-w-full\">\r\n <IconSvg glyph={lineFile} size=\"sm\" />\r\n <span className={styles.fileName()}>{displayName}</span>\r\n </Chip>\r\n </div>\r\n )}\r\n <div className={styles.removeWrap()}>\r\n <Button\r\n type=\"button\"\r\n color=\"critical\"\r\n variant=\"ghost\"\r\n size=\"icon-sm\"\r\n disabled={disabled}\r\n aria-label={texts?.remove ?? \"Remover arquivo\"}\r\n onClick={handleRemove}\r\n >\r\n <IconSvg glyph={lineBin} size=\"sm\" />\r\n </Button>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </FormField>\r\n );\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -31,6 +31,12 @@ export interface FileUploadFieldProps {
31
31
  fallbackFileName?: string;
32
32
  /** Nome acessível do botão de remover. Default: "Remover arquivo". */
33
33
  remove?: string;
34
+ /**
35
+ * Dica abaixo da chamada. Default: derivada de `accept` + `maxSizeMB`
36
+ * — que é literalmente a lista de MIME types (`text/csv,application/vnd...`),
37
+ * ilegível quando `accept` é longo. String vazia esconde a linha.
38
+ */
39
+ hint?: string;
34
40
  };
35
41
  /** Label do campo (renderizado pelo FormField). */
36
42
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"file-upload-field.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QACN,8DAA8D;QAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,gFAAgF;QAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,sEAAsE;QACtE,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"file-upload-field.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/FileUploadField/file-upload-field.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QACN,8DAA8D;QAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,gFAAgF;QAChF,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,sEAAsE;QACtE,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB;;;;WAIG;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -15,12 +15,14 @@ function HeaderSearch({
15
15
  commandEmptyMessage = "Nenhum resultado encontrado.",
16
16
  hotkey = { key: "k", meta: true, ctrl: true },
17
17
  onOpen,
18
+ ariaLabel = "Abrir busca",
18
19
  className
19
20
  }) {
20
21
  const [open, setOpen] = React.useState(false);
21
22
  const delegado = Boolean(onOpen);
22
23
  const abrir = () => onOpen ? onOpen() : setOpen(true);
23
24
  React.useEffect(() => {
25
+ if (hotkey === false) return;
24
26
  const handler = (e) => {
25
27
  if (e.key.toLowerCase() !== hotkey.key.toLowerCase()) return;
26
28
  const meta = hotkey.meta && e.metaKey;
@@ -33,7 +35,7 @@ function HeaderSearch({
33
35
  };
34
36
  window.addEventListener("keydown", handler);
35
37
  return () => window.removeEventListener("keydown", handler);
36
- }, [hotkey.key, hotkey.meta, hotkey.ctrl, onOpen]);
38
+ }, [hotkey, onOpen]);
37
39
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
38
40
  /* @__PURE__ */ jsxRuntime.jsxs(
39
41
  "button",
@@ -41,7 +43,7 @@ function HeaderSearch({
41
43
  type: "button",
42
44
  className: utils.cn(header_styles.searchFakeInput(), "hidden md:inline-flex", className),
43
45
  onClick: abrir,
44
- "aria-label": "Abrir busca",
46
+ "aria-label": ariaLabel,
45
47
  children: [
46
48
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: header_styles.searchFakeInputIcon(), strokeWidth: 1.8 }),
47
49
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: header_styles.searchFakeInputText(), children: placeholder }),
@@ -57,8 +59,8 @@ function HeaderSearch({
57
59
  size: "icon-sm",
58
60
  className: "md:hidden rounded-radius-md",
59
61
  onClick: abrir,
60
- "aria-label": "Abrir busca",
61
- title: `Buscar (${shortcut})`,
62
+ "aria-label": ariaLabel,
63
+ title: hotkey === false ? ariaLabel : `${ariaLabel} (${shortcut})`,
62
64
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, {})
63
65
  }
64
66
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"header-search.cjs","sources":["../../../../../src/components/ui/Header/header-search.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\r\nimport { Search } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/Button/button\";\r\nimport {\r\n CommandDialog,\r\n CommandInput,\r\n CommandList,\r\n CommandEmpty,\r\n CommandGroup,\r\n CommandItem,\r\n CommandSeparator,\r\n CommandShortcut,\r\n} from \"@/components/shadcn/command\";\r\nimport {\r\n searchFakeInput,\r\n searchFakeInputIcon,\r\n searchFakeInputText,\r\n searchFakeInputKbd,\r\n} from \"./header.styles\";\r\nimport type { HeaderCommandGroup } from \"./header.types\";\r\n\r\nexport type HeaderSearchProps = {\r\n placeholder?: string;\r\n shortcut?: string;\r\n commandGroups?: HeaderCommandGroup[];\r\n commandPlaceholder?: string;\r\n commandEmptyMessage?: string;\r\n /** Atalho de teclado pra abrir (default: cmd/ctrl + k) */\r\n hotkey?: { key: string; meta?: boolean; ctrl?: boolean };\r\n /**\r\n * Delega a abertura: quando passado, o clique no campo e o atalho chamam ISTO em vez\r\n * de abrir a paleta interna — e o `CommandDialog` do componente não é montado.\r\n *\r\n * Existe porque o `open` era `useState` interno, sem saída: um app que já tem a\r\n * própria paleta global (Ctrl+K com busca assíncrona, histórico, navegação por rota)\r\n * não conseguia reaproveitá-la. Só dava pra preencher a NOSSA paleta por\r\n * `commandGroups`, que é dado estático — sem busca assíncrona nem render custom.\r\n *\r\n * Com `onOpen`, o header entrega só o campo falso (que é o que ele faz de melhor:\r\n * aparência e atalho consistentes) e a paleta é sua.\r\n */\r\n onOpen?: () => void;\r\n className?: string;\r\n};\r\n\r\n/**\r\n * Fake input no header que abre Command palette ao click.\r\n * Atalho ⌘K (Mac) / Ctrl+K (Windows) registrado globalmente.\r\n */\r\nexport function HeaderSearch({\r\n placeholder = \"Buscar...\",\r\n shortcut = \"⌘K\",\r\n commandGroups = [],\r\n commandPlaceholder = \"Digite um comando ou busque...\",\r\n commandEmptyMessage = \"Nenhum resultado encontrado.\",\r\n hotkey = { key: \"k\", meta: true, ctrl: true },\r\n onOpen,\r\n className,\r\n}: HeaderSearchProps) {\r\n const [open, setOpen] = useState(false);\r\n const delegado = Boolean(onOpen);\r\n const abrir = () => (onOpen ? onOpen() : setOpen(true));\r\n\r\n // Atalho global (⌘K / Ctrl+K)\r\n useEffect(() => {\r\n const handler = (e: KeyboardEvent) => {\r\n if (e.key.toLowerCase() !== hotkey.key.toLowerCase()) return;\r\n const meta = hotkey.meta && e.metaKey;\r\n const ctrl = hotkey.ctrl && e.ctrlKey;\r\n if (meta || ctrl) {\r\n e.preventDefault();\r\n if (onOpen) onOpen();\r\n else setOpen((o) => !o);\r\n }\r\n };\r\n window.addEventListener(\"keydown\", handler);\r\n return () => window.removeEventListener(\"keydown\", handler);\r\n }, [hotkey.key, hotkey.meta, hotkey.ctrl, onOpen]);\r\n\r\n return (\r\n <>\r\n {/* Desktop: fake input completo com placeholder + atalho */}\r\n <button\r\n type=\"button\"\r\n className={cn(searchFakeInput(), \"hidden md:inline-flex\", className)}\r\n onClick={abrir}\r\n aria-label=\"Abrir busca\"\r\n >\r\n <Search className={searchFakeInputIcon()} strokeWidth={1.8} />\r\n <span className={searchFakeInputText()}>{placeholder}</span>\r\n <kbd className={searchFakeInputKbd()}>{shortcut}</kbd>\r\n </button>\r\n\r\n {/* Mobile: icon-button (mesma altura/style dos triggers do header) */}\r\n <Button\r\n color=\"secondary\"\r\n variant=\"outline\"\r\n size=\"icon-sm\"\r\n className=\"md:hidden rounded-radius-md\"\r\n onClick={abrir}\r\n aria-label=\"Abrir busca\"\r\n title={`Buscar (${shortcut})`}\r\n >\r\n <Search />\r\n </Button>\r\n\r\n {/* Com `onOpen` a paleta é do consumidor — não montamos a nossa. */}\r\n {!delegado && (\r\n <CommandDialog open={open} onOpenChange={setOpen}>\r\n <CommandInput placeholder={commandPlaceholder} />\r\n <CommandList>\r\n <CommandEmpty>{commandEmptyMessage}</CommandEmpty>\r\n {commandGroups.map((group, gIdx) => (\r\n <CommandGroupBlock\r\n key={group.heading}\r\n group={group}\r\n isLast={gIdx === commandGroups.length - 1}\r\n onSelect={() => setOpen(false)}\r\n />\r\n ))}\r\n </CommandList>\r\n </CommandDialog>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\nfunction CommandGroupBlock({\r\n group,\r\n isLast,\r\n onSelect,\r\n}: {\r\n group: HeaderCommandGroup;\r\n isLast: boolean;\r\n onSelect: () => void;\r\n}) {\r\n return (\r\n <>\r\n <CommandGroup heading={group.heading}>\r\n {group.items.map((item) => {\r\n const Icon = item.icon;\r\n return (\r\n <CommandItem\r\n key={item.id ?? item.label}\r\n value={item.id ?? item.label}\r\n keywords={item.keywords}\r\n onSelect={() => {\r\n item.onSelect();\r\n onSelect();\r\n }}\r\n className={item.destructive ? \"text-fg-danger [&_svg]:text-fg-danger\" : undefined}\r\n >\r\n {Icon && <Icon />}\r\n <span>{item.label}</span>\r\n {item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}\r\n </CommandItem>\r\n );\r\n })}\r\n </CommandGroup>\r\n {!isLast && <CommandSeparator />}\r\n </>\r\n );\r\n}\r\n"],"names":["useState","useEffect","jsxs","Fragment","cn","searchFakeInput","jsx","Search","searchFakeInputIcon","searchFakeInputText","searchFakeInputKbd","Button","CommandDialog","CommandInput","CommandList","CommandEmpty","CommandGroup","CommandItem","CommandShortcut","CommandSeparator"],"mappings":";;;;;;;;;AAkDO,SAAS,aAAa;AAAA,EAC3B,cAAc;AAAA,EACd,WAAW;AAAA,EACX,gBAAgB,CAAA;AAAA,EAChB,qBAAqB;AAAA,EACrB,sBAAsB;AAAA,EACtB,SAAS,EAAE,KAAK,KAAK,MAAM,MAAM,MAAM,KAAA;AAAA,EACvC;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,MAAM,OAAO,IAAIA,MAAAA,SAAS,KAAK;AACtC,QAAM,WAAW,QAAQ,MAAM;AAC/B,QAAM,QAAQ,MAAO,SAAS,OAAA,IAAW,QAAQ,IAAI;AAGrDC,QAAAA,UAAU,MAAM;AACd,UAAM,UAAU,CAAC,MAAqB;AACpC,UAAI,EAAE,IAAI,YAAA,MAAkB,OAAO,IAAI,cAAe;AACtD,YAAM,OAAO,OAAO,QAAQ,EAAE;AAC9B,YAAM,OAAO,OAAO,QAAQ,EAAE;AAC9B,UAAI,QAAQ,MAAM;AAChB,UAAE,eAAA;AACF,YAAI,OAAQ,QAAA;AAAA,YACP,SAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,MACxB;AAAA,IACF;AACA,WAAO,iBAAiB,WAAW,OAAO;AAC1C,WAAO,MAAM,OAAO,oBAAoB,WAAW,OAAO;AAAA,EAC5D,GAAG,CAAC,OAAO,KAAK,OAAO,MAAM,OAAO,MAAM,MAAM,CAAC;AAEjD,SACEC,2BAAAA,KAAAC,qBAAA,EAEE,UAAA;AAAA,IAAAD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAWE,MAAAA,GAAGC,cAAAA,mBAAmB,yBAAyB,SAAS;AAAA,QACnE,SAAS;AAAA,QACT,cAAW;AAAA,QAEX,UAAA;AAAA,UAAAC,2BAAAA,IAACC,YAAAA,QAAA,EAAO,WAAWC,cAAAA,oBAAA,GAAuB,aAAa,KAAK;AAAA,UAC5DF,2BAAAA,IAAC,QAAA,EAAK,WAAWG,cAAAA,oBAAA,GAAwB,UAAA,aAAY;AAAA,UACrDH,2BAAAA,IAAC,OAAA,EAAI,WAAWI,cAAAA,mBAAA,GAAuB,UAAA,SAAA,CAAS;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAIlDJ,2BAAAA;AAAAA,MAACK,OAAAA;AAAAA,MAAA;AAAA,QACC,OAAM;AAAA,QACN,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS;AAAA,QACT,cAAW;AAAA,QACX,OAAO,WAAW,QAAQ;AAAA,QAE1B,yCAACJ,YAAAA,QAAA,CAAA,CAAO;AAAA,MAAA;AAAA,IAAA;AAAA,IAIT,CAAC,YACFL,2BAAAA,KAACU,QAAAA,eAAA,EAAc,MAAY,cAAc,SACvC,UAAA;AAAA,MAAAN,2BAAAA,IAACO,QAAAA,cAAA,EAAa,aAAa,mBAAA,CAAoB;AAAA,sCAC9CC,QAAAA,aAAA,EACC,UAAA;AAAA,QAAAR,2BAAAA,IAACS,QAAAA,gBAAc,UAAA,oBAAA,CAAoB;AAAA,QAClC,cAAc,IAAI,CAAC,OAAO,SACzBT,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YAEC;AAAA,YACA,QAAQ,SAAS,cAAc,SAAS;AAAA,YACxC,UAAU,MAAM,QAAQ,KAAK;AAAA,UAAA;AAAA,UAHxB,MAAM;AAAA,QAAA,CAKd;AAAA,MAAA,EAAA,CACH;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEF;AAEJ;AAEA,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACEJ,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAG,2BAAAA,IAACU,QAAAA,cAAA,EAAa,SAAS,MAAM,SAC1B,gBAAM,MAAM,IAAI,CAAC,SAAS;AACzB,YAAM,OAAO,KAAK;AAClB,aACEd,2BAAAA;AAAAA,QAACe,QAAAA;AAAAA,QAAA;AAAA,UAEC,OAAO,KAAK,MAAM,KAAK;AAAA,UACvB,UAAU,KAAK;AAAA,UACf,UAAU,MAAM;AACd,iBAAK,SAAA;AACL,qBAAA;AAAA,UACF;AAAA,UACA,WAAW,KAAK,cAAc,0CAA0C;AAAA,UAEvE,UAAA;AAAA,YAAA,uCAAS,MAAA,EAAK;AAAA,YACfX,2BAAAA,IAAC,QAAA,EAAM,UAAA,KAAK,MAAA,CAAM;AAAA,YACjB,KAAK,YAAYA,+BAACY,QAAAA,iBAAA,EAAiB,eAAK,SAAA,CAAS;AAAA,UAAA;AAAA,QAAA;AAAA,QAX7C,KAAK,MAAM,KAAK;AAAA,MAAA;AAAA,IAc3B,CAAC,EAAA,CACH;AAAA,IACC,CAAC,UAAUZ,+BAACa,QAAAA,kBAAA,CAAA,CAAiB;AAAA,EAAA,GAChC;AAEJ;;"}
1
+ {"version":3,"file":"header-search.cjs","sources":["../../../../../src/components/ui/Header/header-search.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\r\nimport { Search } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Button } from \"@/components/ui/Button/button\";\r\nimport {\r\n CommandDialog,\r\n CommandInput,\r\n CommandList,\r\n CommandEmpty,\r\n CommandGroup,\r\n CommandItem,\r\n CommandSeparator,\r\n CommandShortcut,\r\n} from \"@/components/shadcn/command\";\r\nimport {\r\n searchFakeInput,\r\n searchFakeInputIcon,\r\n searchFakeInputText,\r\n searchFakeInputKbd,\r\n} from \"./header.styles\";\r\nimport type { HeaderCommandGroup } from \"./header.types\";\r\n\r\nexport type HeaderSearchProps = {\r\n placeholder?: string;\r\n shortcut?: string;\r\n commandGroups?: HeaderCommandGroup[];\r\n commandPlaceholder?: string;\r\n commandEmptyMessage?: string;\r\n /**\r\n * Atalho de teclado pra abrir (default: cmd/ctrl + k).\r\n *\r\n * `false` desliga o listener global — use quando o app JÁ registra o próprio ⌘K: dois\r\n * listeners no mesmo atalho brigam, e o consumidor não tinha como desligar o nosso.\r\n */\r\n hotkey?: { key: string; meta?: boolean; ctrl?: boolean } | false;\r\n /**\r\n * Nome acessível do campo e do botão-ícone. Default \"Abrir busca\".\r\n * Era fixo em português — não dava pra traduzir nem pra dizer o que a busca busca\r\n * (\"Buscar tickets\").\r\n */\r\n ariaLabel?: string;\r\n /**\r\n * Delega a abertura: quando passado, o clique no campo e o atalho chamam ISTO em vez\r\n * de abrir a paleta interna — e o `CommandDialog` do componente não é montado.\r\n *\r\n * Existe porque o `open` era `useState` interno, sem saída: um app que já tem a\r\n * própria paleta global (Ctrl+K com busca assíncrona, histórico, navegação por rota)\r\n * não conseguia reaproveitá-la. Só dava pra preencher a NOSSA paleta por\r\n * `commandGroups`, que é dado estático — sem busca assíncrona nem render custom.\r\n *\r\n * Com `onOpen`, o header entrega só o campo falso (que é o que ele faz de melhor:\r\n * aparência e atalho consistentes) e a paleta é sua.\r\n */\r\n onOpen?: () => void;\r\n className?: string;\r\n};\r\n\r\n/**\r\n * Fake input no header que abre Command palette ao click.\r\n * Atalho ⌘K (Mac) / Ctrl+K (Windows) registrado globalmente.\r\n */\r\nexport function HeaderSearch({\r\n placeholder = \"Buscar...\",\r\n shortcut = \"⌘K\",\r\n commandGroups = [],\r\n commandPlaceholder = \"Digite um comando ou busque...\",\r\n commandEmptyMessage = \"Nenhum resultado encontrado.\",\r\n hotkey = { key: \"k\", meta: true, ctrl: true },\r\n onOpen,\r\n ariaLabel = \"Abrir busca\",\r\n className,\r\n}: HeaderSearchProps) {\r\n const [open, setOpen] = useState(false);\r\n const delegado = Boolean(onOpen);\r\n const abrir = () => (onOpen ? onOpen() : setOpen(true));\r\n\r\n // Atalho global (⌘K / Ctrl+K)\r\n useEffect(() => {\r\n if (hotkey === false) return;\r\n const handler = (e: KeyboardEvent) => {\r\n if (e.key.toLowerCase() !== hotkey.key.toLowerCase()) return;\r\n const meta = hotkey.meta && e.metaKey;\r\n const ctrl = hotkey.ctrl && e.ctrlKey;\r\n if (meta || ctrl) {\r\n e.preventDefault();\r\n if (onOpen) onOpen();\r\n else setOpen((o) => !o);\r\n }\r\n };\r\n window.addEventListener(\"keydown\", handler);\r\n return () => window.removeEventListener(\"keydown\", handler);\r\n }, [hotkey, onOpen]);\r\n\r\n return (\r\n <>\r\n {/* Desktop: fake input completo com placeholder + atalho */}\r\n <button\r\n type=\"button\"\r\n className={cn(searchFakeInput(), \"hidden md:inline-flex\", className)}\r\n onClick={abrir}\r\n aria-label={ariaLabel}\r\n >\r\n <Search className={searchFakeInputIcon()} strokeWidth={1.8} />\r\n <span className={searchFakeInputText()}>{placeholder}</span>\r\n <kbd className={searchFakeInputKbd()}>{shortcut}</kbd>\r\n </button>\r\n\r\n {/* Mobile: icon-button (mesma altura/style dos triggers do header) */}\r\n <Button\r\n color=\"secondary\"\r\n variant=\"outline\"\r\n size=\"icon-sm\"\r\n className=\"md:hidden rounded-radius-md\"\r\n onClick={abrir}\r\n aria-label={ariaLabel}\r\n title={hotkey === false ? ariaLabel : `${ariaLabel} (${shortcut})`}\r\n >\r\n <Search />\r\n </Button>\r\n\r\n {/* Com `onOpen` a paleta é do consumidor — não montamos a nossa. */}\r\n {!delegado && (\r\n <CommandDialog open={open} onOpenChange={setOpen}>\r\n <CommandInput placeholder={commandPlaceholder} />\r\n <CommandList>\r\n <CommandEmpty>{commandEmptyMessage}</CommandEmpty>\r\n {commandGroups.map((group, gIdx) => (\r\n <CommandGroupBlock\r\n key={group.heading}\r\n group={group}\r\n isLast={gIdx === commandGroups.length - 1}\r\n onSelect={() => setOpen(false)}\r\n />\r\n ))}\r\n </CommandList>\r\n </CommandDialog>\r\n )}\r\n </>\r\n );\r\n}\r\n\r\nfunction CommandGroupBlock({\r\n group,\r\n isLast,\r\n onSelect,\r\n}: {\r\n group: HeaderCommandGroup;\r\n isLast: boolean;\r\n onSelect: () => void;\r\n}) {\r\n return (\r\n <>\r\n <CommandGroup heading={group.heading}>\r\n {group.items.map((item) => {\r\n const Icon = item.icon;\r\n return (\r\n <CommandItem\r\n key={item.id ?? item.label}\r\n value={item.id ?? item.label}\r\n keywords={item.keywords}\r\n onSelect={() => {\r\n item.onSelect();\r\n onSelect();\r\n }}\r\n className={item.destructive ? \"text-fg-danger [&_svg]:text-fg-danger\" : undefined}\r\n >\r\n {Icon && <Icon />}\r\n <span>{item.label}</span>\r\n {item.shortcut && <CommandShortcut>{item.shortcut}</CommandShortcut>}\r\n </CommandItem>\r\n );\r\n })}\r\n </CommandGroup>\r\n {!isLast && <CommandSeparator />}\r\n </>\r\n );\r\n}\r\n"],"names":["useState","useEffect","jsxs","Fragment","cn","searchFakeInput","jsx","Search","searchFakeInputIcon","searchFakeInputText","searchFakeInputKbd","Button","CommandDialog","CommandInput","CommandList","CommandEmpty","CommandGroup","CommandItem","CommandShortcut","CommandSeparator"],"mappings":"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"}
@@ -5,12 +5,23 @@ export type HeaderSearchProps = {
5
5
  commandGroups?: HeaderCommandGroup[];
6
6
  commandPlaceholder?: string;
7
7
  commandEmptyMessage?: string;
8
- /** Atalho de teclado pra abrir (default: cmd/ctrl + k) */
8
+ /**
9
+ * Atalho de teclado pra abrir (default: cmd/ctrl + k).
10
+ *
11
+ * `false` desliga o listener global — use quando o app JÁ registra o próprio ⌘K: dois
12
+ * listeners no mesmo atalho brigam, e o consumidor não tinha como desligar o nosso.
13
+ */
9
14
  hotkey?: {
10
15
  key: string;
11
16
  meta?: boolean;
12
17
  ctrl?: boolean;
13
- };
18
+ } | false;
19
+ /**
20
+ * Nome acessível do campo e do botão-ícone. Default "Abrir busca".
21
+ * Era fixo em português — não dava pra traduzir nem pra dizer o que a busca busca
22
+ * ("Buscar tickets").
23
+ */
24
+ ariaLabel?: string;
14
25
  /**
15
26
  * Delega a abertura: quando passado, o clique no campo e o atalho chamam ISTO em vez
16
27
  * de abrir a paleta interna — e o `CommandDialog` do componente não é montado.
@@ -30,5 +41,5 @@ export type HeaderSearchProps = {
30
41
  * Fake input no header que abre Command palette ao click.
31
42
  * Atalho ⌘K (Mac) / Ctrl+K (Windows) registrado globalmente.
32
43
  */
33
- export declare function HeaderSearch({ placeholder, shortcut, commandGroups, commandPlaceholder, commandEmptyMessage, hotkey, onOpen, className, }: HeaderSearchProps): import("react/jsx-runtime").JSX.Element;
44
+ export declare function HeaderSearch({ placeholder, shortcut, commandGroups, commandPlaceholder, commandEmptyMessage, hotkey, onOpen, ariaLabel, className, }: HeaderSearchProps): import("react/jsx-runtime").JSX.Element;
34
45
  //# sourceMappingURL=header-search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"header-search.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Header/header-search.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACzD;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,WAAyB,EACzB,QAAe,EACf,aAAkB,EAClB,kBAAqD,EACrD,mBAAoD,EACpD,MAA6C,EAC7C,MAAM,EACN,SAAS,GACV,EAAE,iBAAiB,2CAmEnB"}
1
+ {"version":3,"file":"header-search.d.ts","sourceRoot":"","sources":["../../../../../src/components/ui/Header/header-search.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,KAAK,CAAC;IACjE;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,WAAyB,EACzB,QAAe,EACf,aAAkB,EAClB,kBAAqD,EACrD,mBAAoD,EACpD,MAA6C,EAC7C,MAAM,EACN,SAAyB,EACzB,SAAS,GACV,EAAE,iBAAiB,2CAoEnB"}