@jcdecor/ui 0.1.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 (86) hide show
  1. package/README.md +83 -0
  2. package/dist/brand/Brand.d.ts +93 -0
  3. package/dist/brand/index.d.ts +2 -0
  4. package/dist/brand/paths.d.ts +43 -0
  5. package/dist/brand-CXtAwb1A.js +248 -0
  6. package/dist/brand-CXtAwb1A.js.map +1 -0
  7. package/dist/brand.d.ts +1 -0
  8. package/dist/brand.js +3 -0
  9. package/dist/charts/ChartCard.d.ts +20 -0
  10. package/dist/charts/charts.d.ts +36 -0
  11. package/dist/charts/index.d.ts +5 -0
  12. package/dist/charts/palette.d.ts +12 -0
  13. package/dist/charts.d.ts +1 -0
  14. package/dist/charts.js +151 -0
  15. package/dist/charts.js.map +1 -0
  16. package/dist/chat/ChatComposer.d.ts +27 -0
  17. package/dist/chat/ChatLayout.d.ts +36 -0
  18. package/dist/chat/ChatMessage.d.ts +25 -0
  19. package/dist/chat/ChatThread.d.ts +25 -0
  20. package/dist/chat/ConversationList.d.ts +16 -0
  21. package/dist/chat/TypingIndicator.d.ts +10 -0
  22. package/dist/chat/index.d.ts +9 -0
  23. package/dist/chat/types.d.ts +38 -0
  24. package/dist/chat/utils.d.ts +25 -0
  25. package/dist/chat.d.ts +1 -0
  26. package/dist/chat.js +541 -0
  27. package/dist/chat.js.map +1 -0
  28. package/dist/colors-C_5SHERN.js +33 -0
  29. package/dist/colors-C_5SHERN.js.map +1 -0
  30. package/dist/components/ContentCard/ContentCard.d.ts +25 -0
  31. package/dist/components/ContentCard/index.d.ts +1 -0
  32. package/dist/components/CouponCode/CouponCode.d.ts +14 -0
  33. package/dist/components/CouponCode/index.d.ts +1 -0
  34. package/dist/components/DataTable/DataTable.d.ts +52 -0
  35. package/dist/components/DataTable/index.d.ts +1 -0
  36. package/dist/components/KpiCard/KpiCard.d.ts +48 -0
  37. package/dist/components/KpiCard/index.d.ts +1 -0
  38. package/dist/components/PageHeader/PageHeader.d.ts +22 -0
  39. package/dist/components/PageHeader/index.d.ts +1 -0
  40. package/dist/components/PriceTag/PriceTag.d.ts +22 -0
  41. package/dist/components/PriceTag/index.d.ts +1 -0
  42. package/dist/components/ProductCard/ProductCard.d.ts +39 -0
  43. package/dist/components/ProductCard/index.d.ts +1 -0
  44. package/dist/components/PromoBanner/PromoBanner.d.ts +35 -0
  45. package/dist/components/PromoBanner/index.d.ts +1 -0
  46. package/dist/components/Tag/Tag.d.ts +16 -0
  47. package/dist/components/Tag/index.d.ts +1 -0
  48. package/dist/components/ThemeToggle/ThemeToggle.d.ts +11 -0
  49. package/dist/components/ThemeToggle/index.d.ts +1 -0
  50. package/dist/components/TokenSwatch/TokenSwatch.d.ts +26 -0
  51. package/dist/components/TokenSwatch/index.d.ts +1 -0
  52. package/dist/components/TopNav/TopNav.d.ts +32 -0
  53. package/dist/components/TopNav/index.d.ts +1 -0
  54. package/dist/components/Typography/Typography.d.ts +74 -0
  55. package/dist/components/Typography/index.d.ts +1 -0
  56. package/dist/components/index.d.ts +13 -0
  57. package/dist/format-Cxmo46_A.js +97 -0
  58. package/dist/format-Cxmo46_A.js.map +1 -0
  59. package/dist/index.d.ts +13 -0
  60. package/dist/index.js +1868 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/provider/JcProvider.d.ts +18 -0
  63. package/dist/styles.css +2 -0
  64. package/dist/theme/colors.d.ts +26 -0
  65. package/dist/theme/components/buttons.d.ts +2 -0
  66. package/dist/theme/components/combobox.d.ts +2 -0
  67. package/dist/theme/components/dataDisplay.d.ts +2 -0
  68. package/dist/theme/components/feedback.d.ts +2 -0
  69. package/dist/theme/components/index.d.ts +3 -0
  70. package/dist/theme/components/inputs.d.ts +2 -0
  71. package/dist/theme/components/layout.d.ts +2 -0
  72. package/dist/theme/components/misc.d.ts +2 -0
  73. package/dist/theme/components/navigation.d.ts +2 -0
  74. package/dist/theme/components/overlays.d.ts +2 -0
  75. package/dist/theme/components/typography.d.ts +2 -0
  76. package/dist/theme/controls.d.ts +44 -0
  77. package/dist/theme/cssVariablesResolver.d.ts +2 -0
  78. package/dist/theme/theme.d.ts +147 -0
  79. package/dist/theme/tokens.d.ts +457 -0
  80. package/dist/theme/variantColorResolver.d.ts +9 -0
  81. package/dist/tokens-BBTJY6nq.js +316 -0
  82. package/dist/tokens-BBTJY6nq.js.map +1 -0
  83. package/dist/tokens.d.ts +2 -0
  84. package/dist/tokens.js +4 -0
  85. package/dist/utils/format.d.ts +14 -0
  86. package/package.json +105 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","names":["classes","classes","classes","classes","classes","classes","DAY","toDate","value","startOfDay","date","dayLabel","diff","now","shortTimeLabel","buildThread","items","lastDay","messages","message","day","prev","index","next","joinsPrev","groupWindowMs","joinsNext","position","initialsName","name","formatBytes","bytes","Chat_module_default","statusLabel","StatusIcon","status","Loader","IconCheck","IconAlertCircle","IconChecks","ChatMessage","others","system","Box","isLast","isFirst","time","createdAt","own","showAvatar","author","Avatar","showAuthor","attachments","file","IconFile","children","Tooltip","typingLabel","names","TypingIndicator","label","ChatThread","viewport","useRef","content","nearBottom","mounted","userMap","useMemo","users","u","groupWindow","lastMessage","useEffect","el","autoScroll","currentUserId","typing","inner","observer","ScrollArea","y","empty","item","showAuthors","renderContent","ChatComposer","text","setText","useUncontrolled","files","setFiles","useState","canSend","disabled","send","onSend","Pill","f","i","leftSection","allowAttachments","FileButton","selected","ActionIcon","props","IconPaperclip","Textarea","event","placeholder","sendLabel","IconSend2","ConversationList","query","setQuery","filtered","q","conversations","c","searchable","TextInput","searchPlaceholder","IconSearch","activeId","onSelect","Indicator","Badge","ChatHeader","onBack","IconArrowLeft","avatar","avatarName","online","title","subtitle","actions","ChatLayout","toCss","v","height","sidebarWidth","style","sidebar","mobileView","header","composer"],"sources":["../src/chat/utils.ts","../src/chat/Chat.module.css","../src/chat/ChatMessage.tsx","../src/chat/TypingIndicator.tsx","../src/chat/ChatThread.tsx","../src/chat/ChatComposer.tsx","../src/chat/ConversationList.tsx","../src/chat/ChatLayout.tsx"],"sourcesContent":["import type { ChatMessageData } from './types';\n\nconst DAY = 24 * 60 * 60 * 1000;\n\nexport function toDate(value: Date | string | number) {\n return value instanceof Date ? value : new Date(value);\n}\n\nfunction startOfDay(date: Date) {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();\n}\n\n/** \"Hoje\", \"Ontem\", \"segunda-feira\" (últimos 7 dias) ou \"12 de set. de 2026\". */\nexport function dayLabel(value: Date | string | number, now = new Date()) {\n const date = toDate(value);\n const diff = Math.round((startOfDay(now) - startOfDay(date)) / DAY);\n if (diff === 0) return 'Hoje';\n if (diff === 1) return 'Ontem';\n if (diff > 1 && diff < 7) return new Intl.DateTimeFormat('pt-BR', { weekday: 'long' }).format(date);\n return new Intl.DateTimeFormat('pt-BR', { day: 'numeric', month: 'short', year: 'numeric' }).format(date);\n}\n\n/** Horário curto para listas: \"14:32\" hoje, \"Ontem\", ou \"12/09\". */\nexport function shortTimeLabel(value: Date | string | number, now = new Date()) {\n const date = toDate(value);\n const diff = Math.round((startOfDay(now) - startOfDay(date)) / DAY);\n if (diff === 0) return new Intl.DateTimeFormat('pt-BR', { hour: '2-digit', minute: '2-digit' }).format(date);\n if (diff === 1) return 'Ontem';\n return new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit' }).format(date);\n}\n\nexport type GroupPosition = 'single' | 'first' | 'middle' | 'last';\n\nexport type ThreadItem =\n | { type: 'separator'; key: string; label: string }\n | { type: 'message'; key: string; message: ChatMessageData; position: GroupPosition };\n\n/**\n * Agrupa mensagens consecutivas do mesmo autor (janela de `groupWindowMs`)\n * e insere separadores de data.\n */\nexport function buildThread(messages: ChatMessageData[], groupWindowMs = 5 * 60 * 1000, now = new Date()): ThreadItem[] {\n const items: ThreadItem[] = [];\n let lastDay: number | null = null;\n\n messages.forEach((message, index) => {\n const date = toDate(message.createdAt);\n const day = startOfDay(date);\n if (day !== lastDay) {\n items.push({ type: 'separator', key: `sep-${day}`, label: dayLabel(date, now) });\n lastDay = day;\n }\n\n const prev = messages[index - 1];\n const next = messages[index + 1];\n const joinsPrev =\n !!prev && !prev.system && !message.system && prev.authorId === message.authorId &&\n startOfDay(toDate(prev.createdAt)) === day &&\n date.getTime() - toDate(prev.createdAt).getTime() <= groupWindowMs;\n const joinsNext =\n !!next && !next.system && !message.system && next.authorId === message.authorId &&\n startOfDay(toDate(next.createdAt)) === day &&\n toDate(next.createdAt).getTime() - date.getTime() <= groupWindowMs;\n\n const position: GroupPosition = joinsPrev && joinsNext ? 'middle' : joinsPrev ? 'last' : joinsNext ? 'first' : 'single';\n items.push({ type: 'message', key: message.id, message, position });\n });\n\n return items;\n}\n\n/** Remove símbolos/separadores para as iniciais do Avatar: \"Bruno · Instalação\" → \"Bruno Instalação\" (BI). */\nexport function initialsName(name: string) {\n return name.replace(/[^\\p{L}\\s]/gu, ' ').replace(/\\s+/g, ' ').trim() || name;\n}\n\nexport function formatBytes(bytes: number) {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toLocaleString('pt-BR', { maximumFractionDigits: 1 })} KB`;\n return `${(bytes / 1024 / 1024).toLocaleString('pt-BR', { maximumFractionDigits: 1 })} MB`;\n}\n","/* ── ChatMessage ── */\n.message {\n display: flex;\n gap: var(--sp-8);\n align-items: flex-end;\n max-width: 100%;\n}\n.message[data-own] {\n flex-direction: row-reverse;\n}\n.avatarSlot {\n width: 32px;\n flex-shrink: 0;\n}\n.bubbleColumn {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n max-width: min(75%, 560px);\n min-width: 0;\n}\n.message[data-own] .bubbleColumn {\n align-items: flex-end;\n}\n.author {\n font-size: var(--type-disclaimer);\n font-weight: 600;\n color: var(--ds-text-3);\n margin: 0 var(--sp-12) var(--sp-4);\n}\n.bubble {\n --bubble-radius: 16px;\n --bubble-tail: 4px;\n position: relative;\n padding: var(--sp-8) var(--sp-12);\n border-radius: var(--bubble-radius);\n background: var(--ds-surface);\n color: var(--ds-text);\n border: 1px solid var(--ds-border-soft);\n font-size: var(--type-subheadline-sm);\n line-height: 1.45;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n box-shadow: var(--ds-shadow-sm);\n}\n.message[data-own] .bubble {\n /* azul fixo (#2663EB): branco 5,2:1 nos dois temas */\n background: var(--dc-horizon);\n color: #fff;\n border-color: transparent;\n}\n/* cantos agrupados (estilo mensageiro) */\n.message:not([data-own])[data-position='first'] .bubble { border-bottom-left-radius: var(--bubble-tail); }\n.message:not([data-own])[data-position='middle'] .bubble { border-top-left-radius: var(--bubble-tail); border-bottom-left-radius: var(--bubble-tail); }\n.message:not([data-own])[data-position='last'] .bubble { border-top-left-radius: var(--bubble-tail); }\n.message[data-own][data-position='first'] .bubble { border-bottom-right-radius: var(--bubble-tail); }\n.message[data-own][data-position='middle'] .bubble { border-top-right-radius: var(--bubble-tail); border-bottom-right-radius: var(--bubble-tail); }\n.message[data-own][data-position='last'] .bubble { border-top-right-radius: var(--bubble-tail); }\n.message[data-status='error'] .bubble {\n background: var(--ds-error-bg);\n color: var(--ds-error);\n border-color: var(--ds-error);\n}\n.meta {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--ds-text-3);\n margin: 2px var(--sp-8) 0;\n font-variant-numeric: tabular-nums;\n}\n.meta[data-read] .status {\n color: var(--ds-primary);\n}\n.attachments {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sp-4);\n margin-bottom: var(--sp-4);\n}\n.message[data-own] .attachments {\n justify-content: flex-end;\n}\n.imageAttachment {\n display: block;\n max-width: 240px;\n max-height: 180px;\n border-radius: 12px;\n object-fit: cover;\n border: 1px solid var(--ds-border-soft);\n}\n.fileAttachment {\n display: inline-flex;\n align-items: center;\n gap: var(--sp-8);\n padding: var(--sp-8) var(--sp-12);\n border-radius: 12px;\n background: var(--ds-surface-2);\n border: 1px solid var(--ds-border-soft);\n color: var(--ds-text);\n text-decoration: none;\n font-size: var(--type-disclaimer);\n max-width: 260px;\n}\n.fileName {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.fileSize {\n flex-shrink: 0;\n white-space: nowrap;\n color: var(--ds-text-3);\n}\n.fileAttachment:hover {\n border-color: var(--ds-primary);\n}\n.system {\n align-self: center;\n font-size: var(--type-disclaimer);\n color: var(--ds-text-3);\n background: var(--ds-surface-2);\n padding: 4px var(--sp-12);\n border-radius: 99px;\n text-align: center;\n}\n\n/* ── ChatThread ── */\n.thread {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: var(--sp-16);\n}\n.thread > [data-group-start] {\n margin-top: var(--sp-12);\n}\n.separator {\n display: flex;\n align-items: center;\n gap: var(--sp-12);\n margin: var(--sp-16) 0 var(--sp-8);\n font-size: var(--type-disclaimer);\n font-weight: 600;\n color: var(--ds-text-3);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.separator::before,\n.separator::after {\n content: '';\n flex: 1;\n height: 1px;\n background: var(--ds-border-soft);\n}\n\n/* ── TypingIndicator ── */\n.typing {\n display: inline-flex;\n align-items: center;\n gap: var(--sp-8);\n font-size: var(--type-disclaimer);\n color: var(--ds-text-3);\n}\n.dots {\n display: inline-flex;\n gap: 3px;\n padding: 10px 12px;\n border-radius: 16px;\n background: var(--ds-surface);\n border: 1px solid var(--ds-border-soft);\n}\n.dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--ds-text-3);\n animation: jc-typing 1.2s infinite ease-in-out;\n}\n.dot:nth-child(2) { animation-delay: 0.15s; }\n.dot:nth-child(3) { animation-delay: 0.3s; }\n@keyframes jc-typing {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }\n 30% { transform: translateY(-4px); opacity: 1; }\n}\n@media (prefers-reduced-motion: reduce) {\n .dot { animation: none; opacity: 0.7; }\n}\n\n/* ── ChatComposer ── */\n.composer {\n display: flex;\n align-items: flex-end;\n gap: var(--sp-8);\n padding: var(--sp-12);\n border-top: 1px solid var(--ds-border-soft);\n background: var(--ds-surface);\n}\n.composerInput {\n flex: 1;\n}\n.composerFiles {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sp-4);\n padding: var(--sp-8) var(--sp-12) 0;\n background: var(--ds-surface);\n border-top: 1px solid var(--ds-border-soft);\n}\n\n/* ── ConversationList ── */\n.conversations {\n display: flex;\n flex-direction: column;\n min-height: 0;\n height: 100%;\n}\n.conversationSearch {\n padding: var(--sp-12);\n border-bottom: 1px solid var(--ds-border-soft);\n}\n.conversation {\n all: unset;\n box-sizing: border-box;\n display: flex;\n gap: var(--sp-12);\n align-items: center;\n width: 100%;\n padding: var(--sp-12) var(--sp-16);\n cursor: pointer;\n border-bottom: 1px solid var(--ds-border-soft);\n transition: background 0.15s;\n}\n.conversation:hover {\n background: var(--ds-surface-2);\n}\n.conversation[data-active] {\n background: var(--ds-primary-soft);\n box-shadow: inset 3px 0 0 var(--ds-primary);\n}\n.conversation:focus-visible {\n outline: 2px solid var(--ds-primary);\n outline-offset: -2px;\n}\n.conversationBody {\n flex: 1;\n min-width: 0;\n}\n.conversationTop {\n display: flex;\n justify-content: space-between;\n gap: var(--sp-8);\n align-items: baseline;\n}\n.conversationName {\n font-weight: 600;\n font-size: var(--type-subheadline-sm);\n color: var(--ds-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.conversation[data-unread] .conversationName {\n font-weight: 700;\n}\n.conversationTime {\n font-size: 11px;\n color: var(--ds-text-3);\n flex-shrink: 0;\n}\n.conversationBottom {\n display: flex;\n justify-content: space-between;\n gap: var(--sp-8);\n align-items: center;\n margin-top: 2px;\n}\n.conversationPreview {\n font-size: var(--type-disclaimer);\n color: var(--ds-text-3);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.conversation[data-unread] .conversationPreview {\n color: var(--ds-text-2);\n font-weight: 500;\n}\n\n/* ── ChatHeader / ChatLayout ── */\n.header {\n display: flex;\n align-items: center;\n gap: var(--sp-12);\n padding: var(--sp-12) var(--sp-16);\n border-bottom: 1px solid var(--ds-border-soft);\n background: var(--ds-surface);\n min-height: 64px;\n box-sizing: border-box;\n}\n.headerBody {\n flex: 1;\n min-width: 0;\n}\n.headerTitle {\n font-weight: 600;\n color: var(--ds-text);\n}\n.headerSubtitle {\n font-size: var(--type-disclaimer);\n color: var(--ds-text-3);\n}\n.layoutContainer {\n container-type: inline-size;\n container-name: jc-chat;\n}\n.layout {\n display: grid;\n grid-template-columns: var(--chat-sidebar-width, 320px) 1fr;\n height: var(--chat-height, 600px);\n background: var(--ds-bg);\n border: 1px solid var(--ds-border-soft);\n border-radius: var(--ds-radius);\n overflow: hidden;\n}\n.layout[data-single] {\n grid-template-columns: 1fr;\n}\n.layoutSidebar {\n background: var(--ds-surface);\n border-right: 1px solid var(--ds-border-soft);\n min-height: 0;\n overflow: hidden;\n}\n.layoutMain {\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n}\n.layoutMessages {\n flex: 1;\n min-height: 0;\n}\n/* Abaixo de 640px de largura útil: uma coluna (lista OU conversa) */\n@container jc-chat (max-width: 640px) {\n .layout {\n grid-template-columns: 1fr;\n }\n .layout[data-mobile-view='thread'] .layoutSidebar,\n .layout[data-mobile-view='list'] .layoutMain {\n display: none;\n }\n}\n@container jc-chat (min-width: 640.02px) {\n .backButton {\n display: none;\n }\n}\n/* fora de um ChatLayout o botão voltar segue a tela */\n@media (min-width: 48em) {\n :not(.layoutContainer *) > .backButton {\n display: none;\n }\n}\n","import { Avatar, Box, Loader, Tooltip, type BoxProps } from '@mantine/core';\nimport { IconAlertCircle, IconCheck, IconChecks, IconFile } from '@tabler/icons-react';\nimport { formatBytes, initialsName, toDate, type GroupPosition } from './utils';\nimport type { ChatAttachment, ChatMessageStatus, ChatUser } from './types';\nimport classes from './Chat.module.css';\n\nexport interface ChatMessageProps extends BoxProps, Omit<React.ComponentProps<'div'>, keyof BoxProps> {\n /** Mensagem do usuário atual (alinhada à direita, bolha azul) */\n own?: boolean;\n author?: ChatUser;\n /** Exibe o nome do autor acima da bolha (útil em grupos) */\n showAuthor?: boolean;\n /** Exibe avatar ao lado da bolha (só na última mensagem do grupo) @default true */\n showAvatar?: boolean;\n createdAt?: Date | string | number;\n status?: ChatMessageStatus;\n attachments?: ChatAttachment[];\n /** Posição dentro de um grupo de mensagens consecutivas @default 'single' */\n position?: GroupPosition;\n /** Mensagem de sistema (centralizada) */\n system?: boolean;\n children?: React.ReactNode;\n}\n\nconst statusLabel: Record<ChatMessageStatus, string> = {\n sending: 'Enviando…',\n sent: 'Enviada',\n delivered: 'Entregue',\n read: 'Lida',\n error: 'Falha no envio',\n};\n\nfunction StatusIcon({ status }: { status: ChatMessageStatus }) {\n if (status === 'sending') return <Loader size={10} color=\"gray\" />;\n if (status === 'sent') return <IconCheck size={14} />;\n if (status === 'error') return <IconAlertCircle size={14} color=\"var(--ds-error)\" />;\n return <IconChecks size={14} />;\n}\n\n/** Bolha de mensagem de chat. */\nexport function ChatMessage({\n own,\n author,\n showAuthor,\n showAvatar = true,\n createdAt,\n status,\n attachments,\n position = 'single',\n system,\n children,\n ...others\n}: ChatMessageProps) {\n if (system) {\n return (\n <Box className={classes.system} role=\"status\" {...others}>\n {children}\n </Box>\n );\n }\n\n const isLast = position === 'single' || position === 'last';\n const isFirst = position === 'single' || position === 'first';\n const time = createdAt\n ? new Intl.DateTimeFormat('pt-BR', { hour: '2-digit', minute: '2-digit' }).format(toDate(createdAt))\n : null;\n\n return (\n <Box className={classes.message} mod={{ own, position, status }} {...others}>\n {!own && (\n <div className={classes.avatarSlot}>\n {showAvatar && isLast && author && (\n <Avatar src={author.avatar} name={initialsName(author.name)} color={author.color ?? 'initials'} size={32} alt={author.name} />\n )}\n </div>\n )}\n <div className={classes.bubbleColumn}>\n {showAuthor && isFirst && author && !own && <div className={classes.author}>{author.name}</div>}\n {attachments && attachments.length > 0 && (\n <div className={classes.attachments}>\n {attachments.map((file, index) =>\n file.type === 'image' && file.url ? (\n <a key={index} href={file.url} target=\"_blank\" rel=\"noreferrer\">\n <img className={classes.imageAttachment} src={file.url} alt={file.name} />\n </a>\n ) : (\n <a key={index} className={classes.fileAttachment} href={file.url} target=\"_blank\" rel=\"noreferrer\" download={file.name}>\n <IconFile size={18} />\n <span className={classes.fileName}>{file.name}</span>\n {file.size !== undefined && <span className={classes.fileSize}>{formatBytes(file.size)}</span>}\n </a>\n ),\n )}\n </div>\n )}\n {children !== undefined && children !== null && children !== '' && <div className={classes.bubble}>{children}</div>}\n {isLast && (time || (own && status)) && (\n <div className={classes.meta} data-read={status === 'read' || undefined}>\n {time && createdAt && <time dateTime={toDate(createdAt).toISOString()}>{time}</time>}\n {own && status && (\n <Tooltip label={statusLabel[status]} withArrow>\n <span className={classes.status} aria-label={statusLabel[status]} style={{ display: 'inline-flex' }}>\n <StatusIcon status={status} />\n </span>\n </Tooltip>\n )}\n </div>\n )}\n </div>\n </Box>\n );\n}\nChatMessage.displayName = '@jcdecor/ui/ChatMessage';\n","import { Box, type BoxProps } from '@mantine/core';\nimport classes from './Chat.module.css';\n\nexport interface TypingIndicatorProps extends BoxProps {\n /** Nomes de quem está digitando. Vazio = só os pontinhos. */\n names?: string[];\n}\n\nfunction typingLabel(names: string[]) {\n if (names.length === 0) return '';\n if (names.length === 1) return `${names[0]} está digitando…`;\n if (names.length === 2) return `${names[0]} e ${names[1]} estão digitando…`;\n return `${names[0]} e mais ${names.length - 1} estão digitando…`;\n}\n\n/** Indicador animado de \"digitando…\". */\nexport function TypingIndicator({ names = [], ...others }: TypingIndicatorProps) {\n const label = typingLabel(names);\n return (\n <Box className={classes.typing} role=\"status\" aria-live=\"polite\" aria-label={label || 'Digitando'} {...others}>\n <span className={classes.dots} aria-hidden>\n <span className={classes.dot} />\n <span className={classes.dot} />\n <span className={classes.dot} />\n </span>\n {label && <span>{label}</span>}\n </Box>\n );\n}\nTypingIndicator.displayName = '@jcdecor/ui/TypingIndicator';\n","import { useEffect, useMemo, useRef } from 'react';\nimport { Box, ScrollArea, type ScrollAreaProps } from '@mantine/core';\nimport { ChatMessage } from './ChatMessage';\nimport { TypingIndicator } from './TypingIndicator';\nimport { buildThread } from './utils';\nimport type { ChatMessageData, ChatUser } from './types';\nimport classes from './Chat.module.css';\n\nexport interface ChatThreadProps extends Omit<ScrollAreaProps, 'children'> {\n messages: ChatMessageData[];\n /** Id do usuário atual — suas mensagens ficam à direita */\n currentUserId: string;\n users?: ChatUser[] | Record<string, ChatUser>;\n /** Nomes de quem está digitando agora */\n typing?: string[];\n /** Mostra nome do autor no início de cada grupo (conversas em grupo) */\n showAuthors?: boolean;\n /** Rola para a última mensagem quando chegam novas (se o usuário estiver perto do fim) @default true */\n autoScroll?: boolean;\n /** Janela (ms) para agrupar mensagens consecutivas do mesmo autor @default 300000 */\n groupWindow?: number;\n /** Conteúdo exibido quando não há mensagens */\n empty?: React.ReactNode;\n /** Renderização customizada do conteúdo da bolha */\n renderContent?: (message: ChatMessageData) => React.ReactNode;\n}\n\n/** Lista rolável de mensagens com separadores de data, agrupamento e auto-scroll. */\nexport function ChatThread({\n messages,\n currentUserId,\n users = [],\n typing,\n showAuthors,\n autoScroll = true,\n groupWindow,\n empty,\n renderContent,\n h = '100%',\n ...others\n}: ChatThreadProps) {\n const viewport = useRef<HTMLDivElement>(null);\n const content = useRef<HTMLDivElement>(null);\n const nearBottom = useRef(true);\n const mounted = useRef(false);\n\n const userMap = useMemo<Record<string, ChatUser>>(\n () => (Array.isArray(users) ? Object.fromEntries(users.map((u) => [u.id, u])) : users),\n [users],\n );\n const items = useMemo(() => buildThread(messages, groupWindow), [messages, groupWindow]);\n\n const lastMessage = messages[messages.length - 1];\n useEffect(() => {\n const el = viewport.current;\n if (!el || !autoScroll) return;\n if (nearBottom.current || lastMessage?.authorId === currentUserId) {\n // Primeira renderização: pula direto para o fim; depois, rolagem suave.\n el.scrollTo({ top: el.scrollHeight, behavior: mounted.current ? 'smooth' : 'auto' });\n }\n mounted.current = true;\n }, [messages.length, lastMessage?.id, typing?.length, autoScroll, currentUserId]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Conteúdo que cresce depois (imagens, fontes) mantém o fim visível se o usuário já estava lá.\n useEffect(() => {\n const el = viewport.current;\n const inner = content.current;\n if (!el || !inner || !autoScroll || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n if (nearBottom.current) el.scrollTo({ top: el.scrollHeight });\n });\n observer.observe(inner);\n return () => observer.disconnect();\n }, [autoScroll]);\n\n return (\n <ScrollArea\n h={h}\n viewportRef={viewport}\n onScrollPositionChange={({ y }) => {\n const el = viewport.current;\n if (el) nearBottom.current = el.scrollHeight - el.clientHeight - y < 80;\n }}\n {...others}\n >\n <Box ref={content} className={classes.thread} role=\"log\" aria-live=\"polite\" aria-relevant=\"additions\">\n {messages.length === 0 && empty}\n {items.map((item) => {\n if (item.type === 'separator') {\n return (\n <div key={item.key} className={classes.separator} role=\"separator\">\n {item.label}\n </div>\n );\n }\n const { message, position } = item;\n const own = message.authorId === currentUserId;\n return (\n <ChatMessage\n key={item.key}\n own={own}\n system={message.system}\n author={userMap[message.authorId]}\n showAuthor={showAuthors}\n createdAt={message.createdAt}\n status={own ? message.status : undefined}\n attachments={message.attachments}\n position={position}\n data-group-start={position === 'single' || position === 'first' ? true : undefined}\n >\n {renderContent ? renderContent(message) : message.text}\n </ChatMessage>\n );\n })}\n {typing && typing.length > 0 && <TypingIndicator names={typing} mt=\"sm\" ml={40} />}\n </Box>\n </ScrollArea>\n );\n}\nChatThread.displayName = '@jcdecor/ui/ChatThread';\n","import { useState } from 'react';\nimport { ActionIcon, Box, FileButton, Pill, Textarea, Tooltip, type BoxProps } from '@mantine/core';\nimport { useUncontrolled } from '@mantine/hooks';\nimport { IconPaperclip, IconSend2 } from '@tabler/icons-react';\nimport classes from './Chat.module.css';\n\nexport interface ChatComposerSendPayload {\n text: string;\n files: File[];\n}\n\nexport interface ChatComposerProps extends Omit<BoxProps, 'onSubmit'> {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n /** Chamado ao enviar (Enter ou botão). O campo é limpo em seguida. */\n onSend: (payload: ChatComposerSendPayload) => void;\n placeholder?: string;\n disabled?: boolean;\n /** Habilita anexos; `accept` segue o atributo HTML (ex.: \"image/*,.pdf\") */\n allowAttachments?: boolean;\n accept?: string;\n /** Máximo de linhas antes de rolar @default 5 */\n maxRows?: number;\n /** Elementos extras à esquerda (ex.: emoji picker, respostas rápidas) */\n leftSection?: React.ReactNode;\n sendLabel?: string;\n}\n\n/** Campo de mensagem: Enter envia, Shift+Enter quebra linha, anexos opcionais. */\nexport function ChatComposer({\n value,\n defaultValue,\n onChange,\n onSend,\n placeholder = 'Digite uma mensagem…',\n disabled,\n allowAttachments,\n accept,\n maxRows = 5,\n leftSection,\n sendLabel = 'Enviar',\n ...others\n}: ChatComposerProps) {\n const [text, setText] = useUncontrolled({ value, defaultValue, finalValue: '', onChange });\n const [files, setFiles] = useState<File[]>([]);\n const canSend = !disabled && (text.trim().length > 0 || files.length > 0);\n\n const send = () => {\n if (!canSend) return;\n onSend({ text: text.trim(), files });\n setText('');\n setFiles([]);\n };\n\n return (\n <Box {...others}>\n {files.length > 0 && (\n <div className={classes.composerFiles}>\n {files.map((file, index) => (\n <Pill key={`${file.name}-${index}`} withRemoveButton onRemove={() => setFiles((f) => f.filter((_, i) => i !== index))}>\n {file.name}\n </Pill>\n ))}\n </div>\n )}\n <div className={classes.composer}>\n {leftSection}\n {allowAttachments && (\n <FileButton multiple accept={accept} onChange={(selected) => setFiles((f) => [...f, ...(selected ?? [])])} disabled={disabled}>\n {(props) => (\n <Tooltip label=\"Anexar arquivo\">\n <ActionIcon {...props} size=\"lg\" variant=\"subtle\" aria-label=\"Anexar arquivo\">\n <IconPaperclip size={20} />\n </ActionIcon>\n </Tooltip>\n )}\n </FileButton>\n )}\n <Textarea\n className={classes.composerInput}\n value={text}\n onChange={(event) => setText(event.currentTarget.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n send();\n }\n }}\n placeholder={placeholder}\n disabled={disabled}\n autosize\n minRows={1}\n maxRows={maxRows}\n radius=\"lg\"\n aria-label={placeholder}\n />\n <ActionIcon size={44} radius=\"xl\" variant=\"filled\" onClick={send} disabled={!canSend} aria-label={sendLabel}>\n <IconSend2 size={20} />\n </ActionIcon>\n </div>\n </Box>\n );\n}\nChatComposer.displayName = '@jcdecor/ui/ChatComposer';\n","import { useMemo, useState } from 'react';\nimport { Avatar, Badge, Box, Indicator, ScrollArea, TextInput, type BoxProps } from '@mantine/core';\nimport { IconSearch } from '@tabler/icons-react';\nimport { initialsName, shortTimeLabel } from './utils';\nimport type { Conversation } from './types';\nimport classes from './Chat.module.css';\n\nexport interface ConversationListProps extends Omit<BoxProps, 'onSelect'> {\n conversations: Conversation[];\n activeId?: string | null;\n onSelect?: (conversation: Conversation) => void;\n /** Exibe campo de busca @default true */\n searchable?: boolean;\n searchPlaceholder?: string;\n empty?: React.ReactNode;\n}\n\n/** Lista de conversas com busca, não lidas, presença e última mensagem. */\nexport function ConversationList({\n conversations,\n activeId,\n onSelect,\n searchable = true,\n searchPlaceholder = 'Buscar conversas',\n empty,\n ...others\n}: ConversationListProps) {\n const [query, setQuery] = useState('');\n const filtered = useMemo(() => {\n const q = query.trim().toLocaleLowerCase('pt-BR');\n if (!q) return conversations;\n return conversations.filter(\n (c) => c.name.toLocaleLowerCase('pt-BR').includes(q) || c.lastMessage?.toLocaleLowerCase('pt-BR').includes(q),\n );\n }, [conversations, query]);\n\n return (\n <Box className={classes.conversations} {...others}>\n {searchable && (\n <div className={classes.conversationSearch}>\n <TextInput\n size=\"sm\"\n placeholder={searchPlaceholder}\n leftSection={<IconSearch size={16} />}\n value={query}\n onChange={(e) => setQuery(e.currentTarget.value)}\n aria-label={searchPlaceholder}\n />\n </div>\n )}\n <ScrollArea style={{ flex: 1 }} type=\"auto\">\n <div role=\"list\">\n {filtered.length === 0 && (\n <Box p=\"lg\" ta=\"center\" c=\"var(--ds-text-3)\" fz=\"sm\">\n {empty ?? 'Nenhuma conversa encontrada'}\n </Box>\n )}\n {filtered.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n role=\"listitem\"\n className={classes.conversation}\n data-active={c.id === activeId || undefined}\n data-unread={(c.unread ?? 0) > 0 || undefined}\n aria-current={c.id === activeId ? 'true' : undefined}\n onClick={() => onSelect?.(c)}\n >\n <Indicator color=\"evergreen\" position=\"bottom-end\" offset={5} size={10} withBorder disabled={!c.online}>\n <Avatar src={c.avatar} name={initialsName(c.name)} color={c.color ?? 'initials'} size={40} />\n </Indicator>\n <div className={classes.conversationBody}>\n <div className={classes.conversationTop}>\n <span className={classes.conversationName}>{c.name}</span>\n {c.lastMessageAt !== undefined && <span className={classes.conversationTime}>{shortTimeLabel(c.lastMessageAt)}</span>}\n </div>\n <div className={classes.conversationBottom}>\n <span className={classes.conversationPreview}>{c.lastMessage}</span>\n {(c.unread ?? 0) > 0 && (\n <Badge size=\"sm\" variant=\"filled\" circle={(c.unread ?? 0) < 10} aria-label={`${c.unread} não lidas`}>\n {c.unread! > 99 ? '99+' : c.unread}\n </Badge>\n )}\n </div>\n {c.tag && (\n <Badge size=\"xs\" color=\"obsidian\" mt={6}>\n {c.tag}\n </Badge>\n )}\n </div>\n </button>\n ))}\n </div>\n </ScrollArea>\n </Box>\n );\n}\nConversationList.displayName = '@jcdecor/ui/ConversationList';\n","import { ActionIcon, Avatar, Box, Indicator, type BoxProps } from '@mantine/core';\nimport { IconArrowLeft } from '@tabler/icons-react';\nimport { initialsName } from './utils';\nimport classes from './Chat.module.css';\n\nexport interface ChatHeaderProps extends BoxProps {\n title: React.ReactNode;\n subtitle?: React.ReactNode;\n avatar?: string;\n /** Nome para iniciais do avatar */\n avatarName?: string;\n online?: boolean;\n actions?: React.ReactNode;\n /** Exibe botão voltar (mobile) */\n onBack?: () => void;\n}\n\n/** Cabeçalho da conversa: avatar, nome, status e ações. */\nexport function ChatHeader({ title, subtitle, avatar, avatarName, online, actions, onBack, ...others }: ChatHeaderProps) {\n return (\n <Box className={classes.header} {...others}>\n {onBack && (\n <ActionIcon className={classes.backButton} variant=\"subtle\" onClick={onBack} aria-label=\"Voltar\">\n <IconArrowLeft size={20} />\n </ActionIcon>\n )}\n {(avatar || avatarName) && (\n <Indicator color=\"evergreen\" position=\"bottom-end\" offset={5} size={10} withBorder disabled={!online}>\n <Avatar src={avatar} name={avatarName ? initialsName(avatarName) : undefined} color=\"initials\" size={40} />\n </Indicator>\n )}\n <div className={classes.headerBody}>\n <div className={classes.headerTitle}>{title}</div>\n {subtitle && <div className={classes.headerSubtitle}>{subtitle}</div>}\n </div>\n {actions}\n </Box>\n );\n}\nChatHeader.displayName = '@jcdecor/ui/ChatHeader';\n\nexport interface ChatLayoutProps extends BoxProps {\n /** Lista de conversas (coluna esquerda) — omita para um chat único */\n sidebar?: React.ReactNode;\n header?: React.ReactNode;\n /** Normalmente um <ChatThread /> */\n children: React.ReactNode;\n /** Normalmente um <ChatComposer /> */\n composer?: React.ReactNode;\n /** Altura total @default 600 */\n height?: number | string;\n sidebarWidth?: number | string;\n /** Em telas pequenas mostra só a lista ou só a conversa @default 'thread' */\n mobileView?: 'list' | 'thread';\n}\n\n/** Layout de chat: conversas + cabeçalho + mensagens + composer (responsivo). */\nexport function ChatLayout({\n sidebar,\n header,\n children,\n composer,\n height = 600,\n sidebarWidth = 320,\n mobileView = 'thread',\n style,\n ...others\n}: ChatLayoutProps) {\n const toCss = (v: number | string) => (typeof v === 'number' ? `${v}px` : v);\n // O wrapper é o contêiner das container queries: o layout se adapta ao espaço disponível, não à tela.\n return (\n <Box\n className={classes.layoutContainer}\n style={[\n {\n ['--chat-height' as string]: toCss(height),\n ['--chat-sidebar-width' as string]: toCss(sidebarWidth),\n },\n style,\n ]}\n {...others}\n >\n <div\n className={classes.layout}\n data-mobile-view={sidebar ? mobileView : undefined}\n data-single={sidebar ? undefined : true}\n >\n {sidebar && <aside className={classes.layoutSidebar}>{sidebar}</aside>}\n <section className={classes.layoutMain}>\n {header}\n <div className={classes.layoutMessages}>{children}</div>\n {composer}\n </section>\n </div>\n </Box>\n );\n}\nChatLayout.displayName = '@jcdecor/ui/ChatLayout';\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { i as e, l as t } from "./tokens-BBTJY6nq.js";
3
+ //#region src/theme/colors.ts
4
+ var n = [
5
+ 50,
6
+ 100,
7
+ 200,
8
+ 300,
9
+ 400,
10
+ 500,
11
+ 600,
12
+ 700,
13
+ 800,
14
+ 900
15
+ ], r = (e) => n.map((t) => e[t]), i = r(t.horizon), a = r(t.obsidian), o = r(t.electric), s = r(t.evergreen), c = r(t.danger), l = {
16
+ horizon: i,
17
+ obsidian: a,
18
+ electric: o,
19
+ evergreen: s,
20
+ danger: c,
21
+ gray: r(t.gray),
22
+ dark: [...e],
23
+ blue: i,
24
+ indigo: a,
25
+ green: s,
26
+ teal: s,
27
+ yellow: o,
28
+ red: c
29
+ };
30
+ //#endregion
31
+ export { l as t };
32
+
33
+ //# sourceMappingURL=colors-C_5SHERN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors-C_5SHERN.js","names":["darkNeutrals","ramps","STEPS","tuple","ramp","s","horizon","obsidian","electric","evergreen","danger","jcColors"],"sources":["../src/theme/colors.ts"],"sourcesContent":["import type { MantineColorsTuple } from '@mantine/core';\nimport { darkNeutrals, ramps, type RampStep } from './tokens';\n\n/**\n * Tuplas de 10 tons do Mantine geradas das rampas OKLCH em tokens.ts.\n * Índice → tom: 0=50 · 1=100 · … · 6=600 · … · 9=900.\n * `primaryShade` é 6 no claro e 4 no escuro, então `color=\"evergreen\"` etc.\n * preenche com o tom 600 (claro) / 400 (escuro) de qualquer cor.\n */\nconst STEPS: RampStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\nconst tuple = (ramp: Record<RampStep, string>) => STEPS.map((s) => ramp[s]) as unknown as MantineColorsTuple;\n\nexport const horizon = tuple(ramps.horizon);\nexport const obsidian = tuple(ramps.obsidian);\nexport const electric = tuple(ramps.electric);\nexport const evergreen = tuple(ramps.evergreen);\nexport const danger = tuple(ramps.danger);\n/** Substitui o `gray` do Mantine: neutro frio da marca. */\nexport const gray = tuple(ramps.gray);\n/** Substitui o `dark` do Mantine com os neutros do tema escuro (0 = texto · 7 = fundo). */\nexport const dark = [...darkNeutrals] as unknown as MantineColorsTuple;\n\nexport const jcColors = {\n horizon,\n obsidian,\n electric,\n evergreen,\n danger,\n gray,\n dark,\n // Aliases: `color=\"blue\"` etc. continuam na paleta da marca.\n blue: horizon,\n indigo: obsidian,\n green: evergreen,\n teal: evergreen,\n yellow: electric,\n red: danger,\n} satisfies Record<string, MantineColorsTuple>;\n\nexport type JcColor = 'horizon' | 'obsidian' | 'electric' | 'evergreen' | 'danger' | 'gray';\n"],"mappings":";;;AASA,IAAME,IAAoB;CAAC;CAAI;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,GACpEC,KAAS,MAAmCD,EAAM,KAAK,MAAME,EAAKC,EAAE,GAE7DC,IAAUH,EAAMF,EAAM,OAAO,GAC7BM,IAAWJ,EAAMF,EAAM,QAAQ,GAC/BO,IAAWL,EAAMF,EAAM,QAAQ,GAC/BQ,IAAYN,EAAMF,EAAM,SAAS,GACjCS,IAASP,EAAMF,EAAM,MAAM,GAM3BU,IAAW;CACtB;CACA;CACA;CACA;CACA;CACA,MAVkBR,EAAMF,EAAM,IAU9B;CACA,OATmB,GAAGD,CAStB;CAEA,MAAMM;CACN,QAAQC;CACR,OAAOE;CACP,MAAMA;CACN,QAAQD;CACR,KAAKE;AACP"}
@@ -0,0 +1,25 @@
1
+ import { type CardProps, type Factory, type StylesApiProps } from '@mantine/core';
2
+ export type ContentCardStylesNames = 'root' | 'image' | 'kicker' | 'title' | 'body' | 'actions';
3
+ export interface ContentCardProps extends Omit<CardProps, 'classNames' | 'styles' | 'vars' | 'attributes' | 'title'>, StylesApiProps<ContentCardFactory> {
4
+ /** Sobretítulo em caixa-alta */
5
+ kicker?: React.ReactNode;
6
+ title?: React.ReactNode;
7
+ /** Imagem de capa (URL) ou nó customizado */
8
+ image?: string | React.ReactNode;
9
+ imageAlt?: string;
10
+ imageHeight?: number;
11
+ /** Botões/links no rodapé do card */
12
+ actions?: React.ReactNode;
13
+ children?: React.ReactNode;
14
+ }
15
+ export type ContentCardFactory = Factory<{
16
+ props: ContentCardProps;
17
+ ref: HTMLDivElement;
18
+ stylesNames: ContentCardStylesNames;
19
+ }>;
20
+ /** Card de conteúdo: superfície branca sobre LightGray, borda suave e sombra pequena (ds-card). */
21
+ export declare const ContentCard: import("@mantine/core").MantineComponent<{
22
+ props: ContentCardProps;
23
+ ref: HTMLDivElement;
24
+ stylesNames: ContentCardStylesNames;
25
+ }>;
@@ -0,0 +1 @@
1
+ export * from './ContentCard';
@@ -0,0 +1,14 @@
1
+ import { type BoxProps } from '@mantine/core';
2
+ export interface CouponCodeProps extends BoxProps {
3
+ code: string;
4
+ /** Texto do benefício (ex.: "5% OFF na 1ª compra") */
5
+ description?: React.ReactNode;
6
+ copyLabel?: string;
7
+ copiedLabel?: string;
8
+ onCopy?: (code: string) => void;
9
+ }
10
+ /** Cupom com borda tracejada e botão de copiar. */
11
+ export declare function CouponCode({ code, description, copyLabel, copiedLabel, onCopy, ...others }: CouponCodeProps): import("react").JSX.Element;
12
+ export declare namespace CouponCode {
13
+ var displayName: string;
14
+ }
@@ -0,0 +1 @@
1
+ export * from './CouponCode';
@@ -0,0 +1,52 @@
1
+ import { type BoxProps, type TableProps } from '@mantine/core';
2
+ export interface DataTableColumn<T> {
3
+ /** Chave do campo em `T` (também usada para ordenar). Omita em colunas calculadas/de ações e use `id` + `render`. */
4
+ key?: keyof T & string;
5
+ /** Identificador da coluna quando não há `key` (ex.: 'acoes') */
6
+ id?: string;
7
+ header: React.ReactNode;
8
+ /** Renderização customizada da célula */
9
+ render?: (row: T, index: number) => React.ReactNode;
10
+ /** Coluna numérica: alinhada à direita, tabular-nums e formatada em pt-BR */
11
+ numeric?: boolean;
12
+ /** Opções de Intl.NumberFormat para colunas numéricas */
13
+ format?: Intl.NumberFormatOptions;
14
+ sortable?: boolean;
15
+ /** Valor usado na ordenação quando diferente do campo */
16
+ sortValue?: (row: T) => string | number;
17
+ width?: number | string;
18
+ }
19
+ export type SortDirection = 'asc' | 'desc';
20
+ export interface DataTableSort<T> {
21
+ /** `key` ou `id` da coluna ordenada */
22
+ key: (keyof T & string) | (string & {});
23
+ direction: SortDirection;
24
+ }
25
+ export interface DataTableProps<T> extends BoxProps {
26
+ columns: DataTableColumn<T>[];
27
+ data: T[];
28
+ /** Chave única de cada linha @default índice */
29
+ rowKey?: (row: T, index: number) => React.Key;
30
+ initialSort?: DataTableSort<T>;
31
+ /** Controle externo da ordenação */
32
+ sort?: DataTableSort<T> | null;
33
+ onSortChange?: (sort: DataTableSort<T> | null) => void;
34
+ /** Ativa paginação local com N linhas por página */
35
+ pageSize?: number;
36
+ loading?: boolean;
37
+ /** Linhas de skeleton durante loading @default 5 */
38
+ loadingRows?: number;
39
+ empty?: React.ReactNode;
40
+ onRowClick?: (row: T, index: number) => void;
41
+ /** Remove a moldura (card) em volta da tabela */
42
+ plain?: boolean;
43
+ striped?: boolean;
44
+ stickyHeader?: boolean;
45
+ /** Props repassadas para o Table do Mantine */
46
+ tableProps?: Omit<TableProps, 'data'>;
47
+ }
48
+ /** Tabela de dados com ordenação, colunas numéricas pt-BR, paginação e estados vazio/carregando. */
49
+ export declare function DataTable<T extends Record<string, any>>({ columns, data, rowKey, initialSort, sort: controlledSort, onSortChange, pageSize, loading, loadingRows, empty, onRowClick, plain, striped, stickyHeader, tableProps, ...others }: DataTableProps<T>): import("react").JSX.Element;
50
+ export declare namespace DataTable {
51
+ var displayName: string;
52
+ }
@@ -0,0 +1 @@
1
+ export * from './DataTable';
@@ -0,0 +1,48 @@
1
+ import { type BoxProps, type ElementProps, type Factory, type SimpleGridProps, type StylesApiProps } from '@mantine/core';
2
+ export type KpiCardStylesNames = 'root' | 'header' | 'label' | 'icon' | 'value' | 'footer' | 'delta' | 'chart';
3
+ export type KpiCardCssVariables = {
4
+ root: '--kpi-delta-color' | '--kpi-value-color';
5
+ };
6
+ export interface KpiCardProps extends BoxProps, StylesApiProps<KpiCardFactory>, Omit<ElementProps<'div'>, 'title'> {
7
+ /** Rótulo em caixa-alta (ex.: "Acessos (60d)") */
8
+ label: React.ReactNode;
9
+ /** Valor principal. Números são formatados em pt-BR. */
10
+ value: React.ReactNode;
11
+ /** Variação em pontos percentuais (ex.: -5.6). Cor e seta automáticas. */
12
+ delta?: number;
13
+ /** Texto ao lado da variação (ex.: "vs. mês anterior") */
14
+ deltaLabel?: React.ReactNode;
15
+ /** Quando queda é algo bom (ex.: taxa de rejeição), inverte as cores. */
16
+ invertDelta?: boolean;
17
+ /** Pinta o próprio valor com a cor da variação (como o KPI "-5,6%" do DS) */
18
+ colorValue?: boolean;
19
+ icon?: React.ReactNode;
20
+ /** Slot para um Sparkline ou mini-gráfico */
21
+ chart?: React.ReactNode;
22
+ loading?: boolean;
23
+ }
24
+ export type KpiCardFactory = Factory<{
25
+ props: KpiCardProps;
26
+ ref: HTMLDivElement;
27
+ stylesNames: KpiCardStylesNames;
28
+ vars: KpiCardCssVariables;
29
+ }>;
30
+ export declare function getDeltaTone(delta: number | undefined, invert?: boolean): 'up' | 'down' | 'flat';
31
+ /** Tile de KPI dos dashboards (ds-kpi). */
32
+ export declare const KpiCard: import("@mantine/core").MantineComponent<{
33
+ props: KpiCardProps;
34
+ ref: HTMLDivElement;
35
+ stylesNames: KpiCardStylesNames;
36
+ vars: KpiCardCssVariables;
37
+ }>;
38
+ export interface KpiGroupProps extends SimpleGridProps {
39
+ children?: React.ReactNode;
40
+ }
41
+ /**
42
+ * Grade responsiva de KPIs baseada na largura do CONTÊINER (container queries), não da tela:
43
+ * 1 coluna até 440px · 2 até 880px · 4 acima. Funciona igual em página cheia, ao lado de sidebar ou dentro de card.
44
+ */
45
+ export declare function KpiGroup({ cols, spacing, type, ...others }: KpiGroupProps): import("react").JSX.Element;
46
+ export declare namespace KpiGroup {
47
+ var displayName: string;
48
+ }
@@ -0,0 +1 @@
1
+ export * from './KpiCard';
@@ -0,0 +1,22 @@
1
+ import { type BoxProps } from '@mantine/core';
2
+ export interface PageHeaderBreadcrumb {
3
+ label: React.ReactNode;
4
+ href?: string;
5
+ }
6
+ export interface PageHeaderProps extends BoxProps, Omit<React.ComponentProps<'div'>, keyof BoxProps | 'title'> {
7
+ kicker?: React.ReactNode;
8
+ title: React.ReactNode;
9
+ description?: React.ReactNode;
10
+ /** Botões à direita do título */
11
+ actions?: React.ReactNode;
12
+ breadcrumbs?: PageHeaderBreadcrumb[];
13
+ /** Componente dos links do breadcrumb (ex.: Link do react-router) @default 'a' */
14
+ linkComponent?: React.ElementType;
15
+ /** `display` usa display-small (página principal); `headline` usa headline-large @default 'headline' */
16
+ size?: 'display' | 'headline';
17
+ }
18
+ /** Cabeçalho de página: breadcrumbs, kicker, título, descrição e ações. */
19
+ export declare function PageHeader({ kicker, title, description, actions, breadcrumbs, linkComponent, size, ...others }: PageHeaderProps): import("react").JSX.Element;
20
+ export declare namespace PageHeader {
21
+ var displayName: string;
22
+ }
@@ -0,0 +1 @@
1
+ export * from './PageHeader';
@@ -0,0 +1,22 @@
1
+ import { type BoxProps } from '@mantine/core';
2
+ export interface PriceTagProps extends BoxProps {
3
+ /** Preço atual em reais */
4
+ value: number;
5
+ /** Preço "de" (riscado) */
6
+ oldValue?: number;
7
+ /** Parcelamento: `{ count: 10 }` → "ou 10x de R$ 12,99 sem juros" */
8
+ installments?: {
9
+ count: number;
10
+ interestFree?: boolean;
11
+ };
12
+ /** Desconto no Pix em % (ex.: 5) → "R$ 94,05 no Pix" */
13
+ pixDiscount?: number;
14
+ /** Unidade (ex.: "/m²", "/rolo") */
15
+ unit?: string;
16
+ size?: 'sm' | 'md' | 'lg';
17
+ }
18
+ /** Preço em BRL com preço antigo, parcelamento e desconto Pix. */
19
+ export declare function PriceTag({ value, oldValue, installments, pixDiscount, unit, size, ...others }: PriceTagProps): import("react").JSX.Element;
20
+ export declare namespace PriceTag {
21
+ var displayName: string;
22
+ }
@@ -0,0 +1 @@
1
+ export * from './PriceTag';
@@ -0,0 +1,39 @@
1
+ import { type CardProps, type Factory, type StylesApiProps } from '@mantine/core';
2
+ import { type PriceTagProps } from '../PriceTag';
3
+ export type ProductCardStylesNames = 'root' | 'media' | 'badges' | 'favorite' | 'category' | 'name' | 'price' | 'action';
4
+ export interface ProductCardProps extends Omit<CardProps, 'classNames' | 'styles' | 'vars' | 'attributes'>, StylesApiProps<ProductCardFactory> {
5
+ name: string;
6
+ image: string;
7
+ href?: string;
8
+ category?: string;
9
+ price: number;
10
+ oldPrice?: number;
11
+ installments?: PriceTagProps['installments'];
12
+ pixDiscount?: number;
13
+ unit?: string;
14
+ /** Selos extras (ex.: "Novo", "Frete grátis") */
15
+ badges?: React.ReactNode[];
16
+ /** Mostra selo "-X%" calculado a partir de oldPrice @default true */
17
+ showDiscount?: boolean;
18
+ rating?: number;
19
+ reviews?: number;
20
+ favorite?: boolean;
21
+ onFavoriteChange?: (favorite: boolean) => void;
22
+ actionLabel?: string;
23
+ onAction?: () => void;
24
+ /** Proporção da imagem (largura/altura) @default 1 */
25
+ imageRatio?: number;
26
+ /** Componente do link (ex.: Link do react-router) @default 'a' */
27
+ linkComponent?: React.ElementType;
28
+ }
29
+ export type ProductCardFactory = Factory<{
30
+ props: ProductCardProps;
31
+ ref: HTMLDivElement;
32
+ stylesNames: ProductCardStylesNames;
33
+ }>;
34
+ /** Card de produto do e-commerce: imagem, selos, nome, avaliação, preço e CTA. */
35
+ export declare const ProductCard: import("@mantine/core").MantineComponent<{
36
+ props: ProductCardProps;
37
+ ref: HTMLDivElement;
38
+ stylesNames: ProductCardStylesNames;
39
+ }>;
@@ -0,0 +1 @@
1
+ export * from './ProductCard';
@@ -0,0 +1,35 @@
1
+ import { type BoxProps, type ElementProps, type Factory, type MantineRadius, type StylesApiProps } from '@mantine/core';
2
+ export type PromoBannerStylesNames = 'root' | 'content' | 'highlight' | 'close';
3
+ export type PromoBannerCssVariables = {
4
+ root: '--banner-bg' | '--banner-color' | '--banner-highlight' | '--banner-radius';
5
+ };
6
+ export interface PromoBannerProps extends BoxProps, StylesApiProps<PromoBannerFactory>, ElementProps<'div'> {
7
+ /** `horizon` = azul com destaque Electric · `electric` = amarelo com destaque Horizon · `obsidian` @default 'horizon' */
8
+ variant?: 'horizon' | 'electric' | 'obsidian';
9
+ /** Trecho destacado ao final (ex.: cupom). Também é possível usar `<strong>` dentro de children. */
10
+ highlight?: React.ReactNode;
11
+ /** Ícone/elemento à esquerda do texto */
12
+ icon?: React.ReactNode;
13
+ /** Exibe botão de fechar; o banner some e chama `onClose` */
14
+ withCloseButton?: boolean;
15
+ /** Controle externo da visibilidade (opcional) */
16
+ opened?: boolean;
17
+ onClose?: () => void;
18
+ /** Arredonda os cantos (por padrão o banner é full-bleed, sem raio) */
19
+ radius?: MantineRadius;
20
+ }
21
+ export type PromoBannerFactory = Factory<{
22
+ props: PromoBannerProps;
23
+ ref: HTMLDivElement;
24
+ stylesNames: PromoBannerStylesNames;
25
+ vars: PromoBannerCssVariables;
26
+ variant: 'horizon' | 'electric' | 'obsidian';
27
+ }>;
28
+ /** Banner promocional (topo do site / campanhas). */
29
+ export declare const PromoBanner: import("@mantine/core").MantineComponent<{
30
+ props: PromoBannerProps;
31
+ ref: HTMLDivElement;
32
+ stylesNames: PromoBannerStylesNames;
33
+ vars: PromoBannerCssVariables;
34
+ variant: "horizon" | "electric" | "obsidian";
35
+ }>;
@@ -0,0 +1 @@
1
+ export * from './PromoBanner';
@@ -0,0 +1,16 @@
1
+ import { type BadgeProps } from '@mantine/core';
2
+ export type TagTone = 'primary' | 'success' | 'warn' | 'error' | 'neutral';
3
+ export interface TagProps extends Omit<BadgeProps, 'color' | 'variant'> {
4
+ /** Tom semântico (ds-tag-*) @default 'primary' */
5
+ tone?: TagTone;
6
+ /** Mostra o ícone padrão do tom (✓ ⚠ ✕ ⓘ). Passe um nó em `leftSection` para usar outro. */
7
+ withIcon?: boolean;
8
+ /** `light` = selo suave (padrão) · `filled` = cor sólida · `outline` · `dot` */
9
+ variant?: 'light' | 'filled' | 'outline' | 'dot';
10
+ children?: React.ReactNode;
11
+ }
12
+ /** Selo / tag de status — preset de Badge com os tons do ds.css. */
13
+ export declare function Tag({ tone, withIcon, variant, leftSection, ...others }: TagProps): import("react").JSX.Element;
14
+ export declare namespace Tag {
15
+ var displayName: string;
16
+ }
@@ -0,0 +1 @@
1
+ export * from './Tag';
@@ -0,0 +1,11 @@
1
+ import { type ActionIconProps } from '@mantine/core';
2
+ export interface ThemeToggleProps extends Omit<ActionIconProps, 'children'> {
3
+ /** `icon` = ActionIcon · `button` = botão "Alternar tema" (como no DS) @default 'icon' */
4
+ as?: 'icon' | 'button';
5
+ label?: string;
6
+ }
7
+ /** Alterna entre tema claro e escuro (persistido pelo colorSchemeManager do Mantine). */
8
+ export declare function ThemeToggle({ as, label, variant, color, size, ...others }: ThemeToggleProps): import("react").JSX.Element;
9
+ export declare namespace ThemeToggle {
10
+ var displayName: string;
11
+ }
@@ -0,0 +1 @@
1
+ export * from './ThemeToggle';
@@ -0,0 +1,26 @@
1
+ export interface TokenSwatchProps extends Omit<React.ComponentProps<'button'>, 'value' | 'name'> {
2
+ name: string;
3
+ /** Cor (hex) exibida e copiada */
4
+ value: string;
5
+ /** Variável CSS correspondente (ex.: --dc-horizon) */
6
+ cssVar?: string;
7
+ /** O que copiar ao clicar @default 'value' */
8
+ copy?: 'value' | 'cssVar';
9
+ }
10
+ /** Amostra de cor clicável (copia hex ou var CSS). */
11
+ export declare function TokenSwatch({ name, value, cssVar, copy, className, ...others }: TokenSwatchProps): import("react").JSX.Element;
12
+ export declare namespace TokenSwatch {
13
+ var displayName: string;
14
+ }
15
+ export interface ColorRampProps extends React.ComponentProps<'div'> {
16
+ steps: {
17
+ label: string;
18
+ value: string;
19
+ derived?: boolean;
20
+ }[];
21
+ }
22
+ /** Rampa de cor horizontal (base → 50). Clique copia o hex. */
23
+ export declare function ColorRamp({ steps, className, ...others }: ColorRampProps): import("react").JSX.Element;
24
+ export declare namespace ColorRamp {
25
+ var displayName: string;
26
+ }
@@ -0,0 +1 @@
1
+ export * from './TokenSwatch';
@@ -0,0 +1,32 @@
1
+ import { type BoxProps, type ElementProps, type Factory, type StylesApiProps } from '@mantine/core';
2
+ export type TopNavStylesNames = 'root' | 'brand' | 'links' | 'link' | 'right' | 'burger' | 'mobileLinks';
3
+ export interface TopNavLink {
4
+ label: React.ReactNode;
5
+ href?: string;
6
+ active?: boolean;
7
+ onClick?: (event: React.MouseEvent) => void;
8
+ }
9
+ export interface TopNavProps extends BoxProps, StylesApiProps<TopNavFactory>, ElementProps<'div'> {
10
+ /** Marca à esquerda @default <JcLogo variant="dark" /> (a barra é navy em ambos os temas) */
11
+ brand?: React.ReactNode;
12
+ /** Link da marca; `null` renderiza a marca como texto @default '/' */
13
+ brandHref?: string | null;
14
+ links?: TopNavLink[];
15
+ /** Componente usado nos links (ex.: `Link` do react-router, recebe `to`/`href`) @default 'a' */
16
+ linkComponent?: React.ElementType;
17
+ /** Conteúdo à direita (ex.: ThemeToggle, avatar) */
18
+ rightSection?: React.ReactNode;
19
+ /** Esconde os links em telas pequenas e mostra um hambúrguer @default true */
20
+ collapseOnMobile?: boolean;
21
+ }
22
+ export type TopNavFactory = Factory<{
23
+ props: TopNavProps;
24
+ ref: HTMLDivElement;
25
+ stylesNames: TopNavStylesNames;
26
+ }>;
27
+ /** Barra de navegação Obsidian dos painéis (ds-nav). */
28
+ export declare const TopNav: import("@mantine/core").MantineComponent<{
29
+ props: TopNavProps;
30
+ ref: HTMLDivElement;
31
+ stylesNames: TopNavStylesNames;
32
+ }>;
@@ -0,0 +1 @@
1
+ export * from './TopNav';