@jcdecor/vue 0.1.0 → 0.1.6
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.
- package/README.md +7 -7
- package/dist/charts.js +97 -74
- package/dist/charts.js.map +1 -1
- package/dist/chat/ChatComposer.d.ts +14 -0
- package/dist/chat/ChatMessage.d.ts +4 -0
- package/dist/chat/ChatThread.d.ts +8 -0
- package/dist/chat/ConversationList.d.ts +8 -0
- package/dist/chat/types.d.ts +2 -0
- package/dist/chat.js +97 -55
- package/dist/chat.js.map +1 -1
- package/dist/components/PageHeader/PageHeader.d.ts +10 -29
- package/dist/components/PageHeader/PageHeader.types.d.ts +55 -0
- package/dist/components/PageHeader/PageHeader.vars.d.ts +2 -0
- package/dist/components/PageHeader/PageHeader.vue.d.ts +43 -0
- package/dist/components/TopNav/TopNav.types.d.ts +10 -2
- package/dist/components/TopNav/TopNav.vue.d.ts +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +634 -565
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme/themeDefaults.d.ts +27 -2
- package/dist/themeDefaults-C4raY1h-.js +52 -0
- package/dist/themeDefaults-C4raY1h-.js.map +1 -0
- package/package.json +1 -1
- package/dist/themeDefaults-Ckd05FUY.js +0 -29
- package/dist/themeDefaults-Ckd05FUY.js.map +0 -1
package/dist/chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","names":["Text","classes","Tooltip","classes","classes","classes","Tooltip","classes","classes","Tooltip$1","Text$1","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","nodeProp","hasContent","node","child","isVNode","Comment","Fragment","Chat_module_default","statusLabel","statusIcon","status","h","Loader","IconCheck","IconAlertCircle","IconChecks","timeFormat","ChatMessage","defineComponent","children","slots","props","Box","isLast","isFirst","time","createdAt","avatarSlot","own","author","Avatar","attachmentList","attachments","file","IconFile","meta","typingLabel","names","TypingIndicator","label","NEAR_BOTTOM","ChatThread","viewport","content","nearBottom","mounted","observer","userMap","computed","u","scrollToEnd","el","lastMessage","observeContent","onMounted","watch","onBeforeUnmount","viewportProps","setContent","onScrollPositionChange","y","renderBubble","emptyNode","item","bubble","ScrollArea","ChatComposer","text","setText","useUncontrolled","emit","files","ref","canSend","send","onKeydown","event","removeFile","i","addFiles","selected","Pill","resolveNode","FileButton","ActionIcon","triggerProps","IconPaperclip","Textarea","IconSend2","ConversationList","query","filtered","q","c","renderItem","unread","active","Indicator","Badge","TextInput","IconSearch","ChatHeader","back","handlers","fn","subtitle","IconArrowLeft","toCss","v","ChatLayout","sidebar","withSidebar"],"sources":["../src/chat/utils.ts","../src/chat/node.ts","../src/chat/Chat.module.css","../src/chat/ChatMessage.ts","../src/chat/TypingIndicator.ts","../src/chat/ChatThread.ts","../src/chat/ChatComposer.ts","../src/chat/ConversationList.ts","../src/chat/ChatLayout.ts"],"sourcesContent":["// @generated sincronizado de packages/ui/src/chat/utils.ts — edite lá e rode `npm run sync -w @jcdecor/vue`\nimport 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","import { Comment, Fragment, Text, isVNode, type PropType, type VNodeChild } from 'vue';\nimport type { MantineNode } from '@mantine-vue/core';\n\n/**\n * Prop que espelha um `React.ReactNode` do @jcdecor/ui: aceita VNode, texto ou função de render.\n * Sem validação de tipo em runtime (VNodeChild inclui string, número, array, objeto, função…).\n */\nexport const nodeProp = { type: null as unknown as PropType<MantineNode>, default: undefined };\n\n/** `true` se o conteúdo renderiza algo visível (ignora null, false, '', comentários e fragments vazios). */\nexport function hasContent(node: VNodeChild): boolean {\n if (node === null || node === undefined || node === false || node === true || node === '') return false;\n if (Array.isArray(node)) return node.some((child) => hasContent(child as VNodeChild));\n if (isVNode(node)) {\n if (node.type === Comment) return false;\n if (node.type === Text) return node.children !== '' && node.children != null;\n if (node.type === Fragment) return hasContent(node.children as VNodeChild);\n }\n return true;\n}\n","/* @generated sincronizado de packages/ui/src/chat/Chat.module.css — edite lá e rode `npm run sync -w @jcdecor/vue` */\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 { defineComponent, h, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { Avatar, Box, Loader, type BoxProps } from '@mantine-vue/core';\nimport { Tooltip } from '../theme/themeDefaults';\nimport { IconAlertCircle, IconCheck, IconChecks, IconFile } from '@tabler/icons-vue';\nimport { formatBytes, initialsName, toDate, type GroupPosition } from './utils';\nimport type { ChatAttachment, ChatMessageStatus, ChatUser } from './types';\nimport { hasContent } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatMessageProps extends 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}\n\nexport interface ChatMessageSlots {\n /** Conteúdo da bolha (texto, links, cards…). Sem conteúdo, a bolha não é renderizada. */\n default?: () => VNodeChild;\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: ChatMessageStatus) {\n if (status === 'sending') return h(Loader as any, { size: 10, color: 'gray' });\n if (status === 'sent') return h(IconCheck, { size: 14 });\n if (status === 'error') return h(IconAlertCircle, { size: 14, color: 'var(--ds-error)' });\n return h(IconChecks, { size: 14 });\n}\n\nconst timeFormat = new Intl.DateTimeFormat('pt-BR', { hour: '2-digit', minute: '2-digit' });\n\n/** Bolha de mensagem de chat. O slot padrão é o conteúdo da bolha; atributos vão para o Box raiz. */\nexport const ChatMessage = defineComponent({\n name: 'ChatMessage',\n props: {\n own: { type: Boolean, default: false },\n author: { type: Object as PropType<ChatUser>, default: undefined },\n showAuthor: { type: Boolean, default: false },\n showAvatar: { type: Boolean, default: true },\n createdAt: { type: [Date, String, Number] as PropType<Date | string | number>, default: undefined },\n status: { type: String as PropType<ChatMessageStatus>, default: undefined },\n attachments: { type: Array as PropType<ChatAttachment[]>, default: undefined },\n position: { type: String as PropType<GroupPosition>, default: 'single' },\n system: { type: Boolean, default: false },\n },\n setup(props, { slots }) {\n return () => {\n const children = slots.default?.();\n\n if (props.system) {\n return h(Box as any, { class: classes.system, role: 'status' }, () => children);\n }\n\n const { own, author, status, position, createdAt, attachments } = props;\n const isLast = position === 'single' || position === 'last';\n const isFirst = position === 'single' || position === 'first';\n const time = createdAt ? timeFormat.format(toDate(createdAt)) : null;\n\n const avatarSlot = !own\n ? h(\n 'div',\n { class: classes.avatarSlot },\n props.showAvatar && isLast && author\n ? [\n h(Avatar as any, {\n src: author.avatar,\n name: initialsName(author.name),\n color: author.color ?? 'initials',\n size: 32,\n alt: author.name,\n }),\n ]\n : undefined,\n )\n : null;\n\n const attachmentList =\n attachments && attachments.length > 0\n ? h(\n 'div',\n { class: classes.attachments },\n attachments.map((file, index) =>\n file.type === 'image' && file.url\n ? h('a', { key: index, href: file.url, target: '_blank', rel: 'noreferrer' }, [\n h('img', { class: classes.imageAttachment, src: file.url, alt: file.name }),\n ])\n : h(\n 'a',\n {\n key: index,\n class: classes.fileAttachment,\n href: file.url,\n target: '_blank',\n rel: 'noreferrer',\n download: file.name,\n },\n [\n h(IconFile, { size: 18 }),\n h('span', { class: classes.fileName }, file.name),\n file.size !== undefined ? h('span', { class: classes.fileSize }, formatBytes(file.size)) : null,\n ],\n ),\n ),\n )\n : null;\n\n const meta =\n isLast && (time || (own && status))\n ? h('div', { class: classes.meta, 'data-read': status === 'read' || undefined }, [\n time && createdAt\n ? h('time', { datetime: toDate(createdAt).toISOString() }, time)\n : null,\n own && status\n ? h(\n Tooltip as any,\n { label: statusLabel[status], withArrow: true },\n () =>\n h(\n 'span',\n { class: classes.status, 'aria-label': statusLabel[status], style: { display: 'inline-flex' } },\n [statusIcon(status)],\n ),\n )\n : null,\n ])\n : null;\n\n return h(Box as any, { class: classes.message, mod: { own, position, status } }, () => [\n avatarSlot,\n h('div', { class: classes.bubbleColumn }, [\n props.showAuthor && isFirst && author && !own ? h('div', { class: classes.author }, author.name) : null,\n attachmentList,\n hasContent(children) ? h('div', { class: classes.bubble }, children) : null,\n meta,\n ]),\n ]);\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatMessageProps, {}, SlotsType<ChatMessageSlots>>;\n","import { defineComponent, h, type DefineSetupFnComponent, type PropType } from 'vue';\nimport { Box, type BoxProps } from '@mantine-vue/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…\". Atributos e style props (`mt`, `ml`…) vão para o Box raiz. */\nexport const TypingIndicator = defineComponent({\n name: 'TypingIndicator',\n props: {\n names: { type: Array as PropType<string[]>, default: () => [] },\n },\n setup(props) {\n return () => {\n const label = typingLabel(props.names);\n return h(\n Box as any,\n { class: classes.typing, role: 'status', 'aria-live': 'polite', 'aria-label': label || 'Digitando' },\n () => [\n h('span', { class: classes.dots, 'aria-hidden': 'true' }, [\n h('span', { class: classes.dot }),\n h('span', { class: classes.dot }),\n h('span', { class: classes.dot }),\n ]),\n label ? h('span', label) : null,\n ],\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<TypingIndicatorProps>;\n","import {\n computed,\n defineComponent,\n h,\n onBeforeUnmount,\n onMounted,\n watch,\n type DefineSetupFnComponent,\n type PropType,\n type SlotsType,\n type VNodeChild,\n} from 'vue';\nimport { Box, ScrollArea, type MantineNode, type ScrollAreaProps } from '@mantine-vue/core';\nimport { ChatMessage } from './ChatMessage';\nimport { TypingIndicator } from './TypingIndicator';\nimport { buildThread } from './utils';\nimport type { ChatMessageData, ChatUser } from './types';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatThreadProps extends ScrollAreaProps {\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. O slot `#empty` tem prioridade. */\n empty?: MantineNode;\n /** Renderização customizada do conteúdo da bolha. O slot `#content=\"{ message }\"` tem prioridade. */\n renderContent?: (message: ChatMessageData) => VNodeChild;\n /** Altura da área rolável @default '100%' */\n h?: ScrollAreaProps['h'];\n}\n\nexport interface ChatThreadSlots {\n /** Conteúdo exibido quando não há mensagens */\n empty?: () => VNodeChild;\n /** Conteúdo customizado da bolha de cada mensagem */\n content?: (props: { message: ChatMessageData }) => VNodeChild;\n}\n\n/** Distância (px) do fim abaixo da qual o usuário é considerado \"no fim\" da conversa. */\nconst NEAR_BOTTOM = 80;\n\n/**\n * Lista rolável de mensagens com separadores de data, agrupamento e auto-scroll.\n * Atributos e props do ScrollArea (`type`, `scrollbarSize`…) vão para o ScrollArea raiz.\n */\nexport const ChatThread = defineComponent({\n name: 'ChatThread',\n props: {\n messages: { type: Array as PropType<ChatMessageData[]>, required: true },\n currentUserId: { type: String, required: true },\n users: { type: [Array, Object] as PropType<ChatUser[] | Record<string, ChatUser>>, default: () => [] },\n typing: { type: Array as PropType<string[]>, default: undefined },\n showAuthors: { type: Boolean, default: false },\n autoScroll: { type: Boolean, default: true },\n groupWindow: { type: Number, default: undefined },\n empty: nodeProp,\n renderContent: { type: Function as PropType<(message: ChatMessageData) => VNodeChild>, default: undefined },\n h: { type: [String, Number] as PropType<ScrollAreaProps['h']>, default: '100%' },\n },\n setup(props, { slots }) {\n let viewport: HTMLDivElement | null = null;\n let content: HTMLElement | null = null;\n let nearBottom = true;\n let mounted = false;\n let observer: ResizeObserver | null = null;\n\n const userMap = computed<Record<string, ChatUser>>(() =>\n Array.isArray(props.users) ? Object.fromEntries(props.users.map((u) => [u.id, u])) : props.users,\n );\n const items = computed(() => buildThread(props.messages, props.groupWindow));\n\n function scrollToEnd() {\n const el = viewport;\n if (!el || !props.autoScroll) return;\n const lastMessage = props.messages[props.messages.length - 1];\n if (nearBottom || lastMessage?.authorId === props.currentUserId) {\n // Primeira renderização: pula direto para o fim; depois, rolagem suave.\n el.scrollTo({ top: el.scrollHeight, behavior: mounted ? 'smooth' : 'auto' });\n }\n mounted = true;\n }\n\n // Conteúdo que cresce depois (imagens, fontes) mantém o fim visível se o usuário já estava lá.\n function observeContent() {\n observer?.disconnect();\n observer = null;\n const el = viewport;\n if (!el || !content || !props.autoScroll || typeof ResizeObserver === 'undefined') return;\n observer = new ResizeObserver(() => {\n if (nearBottom) el.scrollTo({ top: el.scrollHeight });\n });\n observer.observe(content);\n }\n\n onMounted(() => {\n scrollToEnd();\n observeContent();\n });\n\n watch(\n () => [\n props.messages.length,\n props.messages[props.messages.length - 1]?.id,\n props.typing?.length,\n props.autoScroll,\n props.currentUserId,\n ],\n scrollToEnd,\n { flush: 'post' },\n );\n watch(() => props.autoScroll, observeContent, { flush: 'post' });\n\n onBeforeUnmount(() => observer?.disconnect());\n\n // O ScrollArea do Mantine Vue não tem `viewportRef`: o elemento chega via `viewportProps.rootRef`.\n const viewportProps = {\n rootRef: (node: Element | null) => {\n viewport = node as HTMLDivElement | null;\n },\n };\n const setContent = (node: Element | null) => {\n content = node as HTMLElement | null;\n };\n const onScrollPositionChange = ({ y }: { x: number; y: number }) => {\n const el = viewport;\n if (el) nearBottom = el.scrollHeight - el.clientHeight - y < NEAR_BOTTOM;\n };\n\n return () => {\n const renderBubble = (message: ChatMessageData): VNodeChild =>\n slots.content\n ? slots.content({ message })\n : props.renderContent\n ? props.renderContent(message)\n : message.text;\n\n const emptyNode =\n props.messages.length === 0\n ? slots.empty\n ? slots.empty()\n : typeof props.empty === 'function'\n ? props.empty()\n : props.empty\n : null;\n\n const children = items.value.map((item) => {\n if (item.type === 'separator') {\n return h('div', { key: item.key, class: classes.separator, role: 'separator' }, item.label);\n }\n const { message, position } = item;\n const own = message.authorId === props.currentUserId;\n const bubble = renderBubble(message);\n return h(\n ChatMessage as any,\n {\n key: item.key,\n own,\n system: message.system,\n author: userMap.value[message.authorId],\n showAuthor: props.showAuthors,\n createdAt: message.createdAt,\n status: own ? message.status : undefined,\n attachments: message.attachments,\n position,\n 'data-group-start': position === 'single' || position === 'first' ? true : undefined,\n },\n { default: () => bubble },\n );\n });\n\n return h(\n ScrollArea as any,\n { h: props.h, viewportProps, onScrollPositionChange },\n () =>\n h(\n Box as any,\n {\n rootRef: setContent,\n class: classes.thread,\n role: 'log',\n 'aria-live': 'polite',\n 'aria-relevant': 'additions',\n },\n () => [\n emptyNode,\n ...children,\n props.typing && props.typing.length > 0\n ? h(TypingIndicator as any, { names: props.typing, mt: 'sm', ml: 40 })\n : null,\n ],\n ),\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatThreadProps, {}, SlotsType<ChatThreadSlots>>;\n","import { computed, defineComponent, h, ref, type DefineSetupFnComponent, type SlotsType, type VNodeChild } from 'vue';\nimport { ActionIcon, Box, FileButton, Pill, Textarea, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { Tooltip } from '../theme/themeDefaults';\nimport { useUncontrolled } from '@mantine-vue/hooks';\nimport { IconPaperclip, IconSend2 } from '@tabler/icons-vue';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatComposerSendPayload {\n text: string;\n files: File[];\n}\n\nexport interface ChatComposerProps extends BoxProps {\n /** Texto controlado (`v-model`). Sem `v-model`, o campo é não controlado. */\n modelValue?: string;\n /** Texto inicial no modo não controlado */\n defaultValue?: string;\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). O slot `#leftSection` tem prioridade. */\n leftSection?: MantineNode;\n sendLabel?: string;\n}\n\nexport type ChatComposerEmits = {\n /** Ao enviar (Enter ou botão). O campo é limpo em seguida. */\n send: (payload: ChatComposerSendPayload) => void;\n /** `v-model` do texto */\n 'update:modelValue': (value: string) => void;\n}\n\nexport interface ChatComposerSlots {\n /** Elementos extras à esquerda (ex.: emoji picker, respostas rápidas) */\n leftSection?: () => VNodeChild;\n}\n\n/** Campo de mensagem: Enter envia, Shift+Enter quebra linha, anexos opcionais. */\nexport const ChatComposer = defineComponent({\n name: 'ChatComposer',\n props: {\n modelValue: { type: String, default: undefined },\n defaultValue: { type: String, default: undefined },\n placeholder: { type: String, default: 'Digite uma mensagem…' },\n disabled: { type: Boolean, default: false },\n allowAttachments: { type: Boolean, default: false },\n accept: { type: String, default: undefined },\n maxRows: { type: Number, default: 5 },\n leftSection: nodeProp,\n sendLabel: { type: String, default: 'Enviar' },\n },\n emits: {\n send: (_payload: ChatComposerSendPayload) => true,\n 'update:modelValue': (_value: string) => true,\n },\n setup(props, { emit, slots }) {\n const [text, setText] = useUncontrolled<string>({\n value: () => props.modelValue,\n defaultValue: props.defaultValue,\n finalValue: '',\n onChange: (value) => emit('update:modelValue', value),\n });\n const files = ref<File[]>([]);\n const canSend = computed(() => !props.disabled && (text.value.trim().length > 0 || files.value.length > 0));\n\n const send = () => {\n if (!canSend.value) return;\n emit('send', { text: text.value.trim(), files: files.value });\n setText('');\n files.value = [];\n };\n\n const onKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n send();\n }\n };\n\n const removeFile = (index: number) => {\n files.value = files.value.filter((_, i) => i !== index);\n };\n\n const addFiles = (selected: File | File[] | null) => {\n files.value = [...files.value, ...(Array.isArray(selected) ? selected : selected ? [selected] : [])];\n };\n\n return () =>\n h(Box as any, null, () => [\n files.value.length > 0\n ? h(\n 'div',\n { class: classes.composerFiles },\n files.value.map((file, index) =>\n h(\n Pill as any,\n { key: `${file.name}-${index}`, withRemoveButton: true, onRemove: () => removeFile(index) },\n () => file.name,\n ),\n ),\n )\n : null,\n h('div', { class: classes.composer }, [\n resolveNode(props.leftSection, slots.leftSection),\n props.allowAttachments\n ? h(\n FileButton as any,\n { multiple: true, accept: props.accept, disabled: props.disabled, onChange: addFiles },\n {\n default: (triggerProps: Record<string, unknown>) =>\n h(Tooltip as any, { label: 'Anexar arquivo' }, () =>\n h(\n ActionIcon as any,\n { ...triggerProps, size: 'lg', variant: 'subtle', 'aria-label': 'Anexar arquivo' },\n () => h(IconPaperclip, { size: 20 }),\n ),\n ),\n },\n )\n : null,\n h(Textarea as any, {\n class: classes.composerInput,\n modelValue: text.value,\n 'onUpdate:modelValue': (value: string) => setText(value),\n onKeydown,\n placeholder: props.placeholder,\n disabled: props.disabled,\n autosize: true,\n minRows: 1,\n maxRows: props.maxRows,\n radius: 'lg',\n 'aria-label': props.placeholder,\n }),\n h(\n ActionIcon as any,\n { size: 44, radius: 'xl', variant: 'filled', onClick: send, disabled: !canSend.value, 'aria-label': props.sendLabel },\n () => h(IconSend2, { size: 20 }),\n ),\n ]),\n ]);\n },\n}) as unknown as DefineSetupFnComponent<ChatComposerProps, ChatComposerEmits, SlotsType<ChatComposerSlots>>;\n","import { computed, defineComponent, h, ref, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { Avatar, Badge, Box, Indicator, ScrollArea, TextInput, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { IconSearch } from '@tabler/icons-vue';\nimport { initialsName, shortTimeLabel } from './utils';\nimport type { Conversation } from './types';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ConversationListProps extends BoxProps {\n conversations: Conversation[];\n activeId?: string | null;\n /** Exibe campo de busca @default true */\n searchable?: boolean;\n searchPlaceholder?: string;\n /** Conteúdo quando a lista (ou a busca) fica vazia. O slot `#empty` tem prioridade. */\n empty?: MantineNode;\n}\n\nexport type ConversationListEmits = {\n /** Clique em uma conversa */\n select: (conversation: Conversation) => void;\n}\n\nexport interface ConversationListSlots {\n /** Conteúdo quando a lista (ou a busca) fica vazia */\n empty?: () => VNodeChild;\n}\n\n/** Lista de conversas com busca, não lidas, presença e última mensagem. */\nexport const ConversationList = defineComponent({\n name: 'ConversationList',\n props: {\n conversations: { type: Array as PropType<Conversation[]>, required: true },\n activeId: { type: String as PropType<string | null>, default: undefined },\n searchable: { type: Boolean, default: true },\n searchPlaceholder: { type: String, default: 'Buscar conversas' },\n empty: nodeProp,\n },\n emits: {\n select: (_conversation: Conversation) => true,\n },\n setup(props, { emit, slots }) {\n const query = ref('');\n const filtered = computed(() => {\n const q = query.value.trim().toLocaleLowerCase('pt-BR');\n if (!q) return props.conversations;\n return props.conversations.filter(\n (c) => c.name.toLocaleLowerCase('pt-BR').includes(q) || c.lastMessage?.toLocaleLowerCase('pt-BR').includes(q),\n );\n });\n\n const renderItem = (c: Conversation) => {\n const unread = c.unread ?? 0;\n const active = c.id === props.activeId;\n return h(\n 'button',\n {\n key: c.id,\n type: 'button',\n role: 'listitem',\n class: classes.conversation,\n 'data-active': active || undefined,\n 'data-unread': unread > 0 || undefined,\n 'aria-current': active ? 'true' : undefined,\n onClick: () => emit('select', c),\n },\n [\n h(\n Indicator as any,\n { color: 'evergreen', position: 'bottom-end', offset: 5, size: 10, withBorder: true, disabled: !c.online },\n () => h(Avatar as any, { src: c.avatar, name: initialsName(c.name), color: c.color ?? 'initials', size: 40 }),\n ),\n h('div', { class: classes.conversationBody }, [\n h('div', { class: classes.conversationTop }, [\n h('span', { class: classes.conversationName }, c.name),\n c.lastMessageAt !== undefined\n ? h('span', { class: classes.conversationTime }, shortTimeLabel(c.lastMessageAt))\n : null,\n ]),\n h('div', { class: classes.conversationBottom }, [\n h('span', { class: classes.conversationPreview }, c.lastMessage),\n unread > 0\n ? h(\n Badge as any,\n { size: 'sm', variant: 'filled', circle: unread < 10, 'aria-label': `${c.unread} não lidas` },\n () => (unread > 99 ? '99+' : String(unread)),\n )\n : null,\n ]),\n c.tag ? h(Badge as any, { size: 'xs', color: 'obsidian', mt: 6 }, () => c.tag) : null,\n ]),\n ],\n );\n };\n\n return () =>\n h(Box as any, { class: classes.conversations }, () => [\n props.searchable\n ? h('div', { class: classes.conversationSearch }, [\n h(\n TextInput as any,\n {\n size: 'sm',\n placeholder: props.searchPlaceholder,\n modelValue: query.value,\n 'onUpdate:modelValue': (value: string) => (query.value = value),\n 'aria-label': props.searchPlaceholder,\n },\n { leftSection: () => h(IconSearch, { size: 16 }) },\n ),\n ])\n : null,\n h(ScrollArea as any, { style: { flex: 1 }, type: 'auto' }, () =>\n h('div', { role: 'list' }, [\n filtered.value.length === 0\n ? h(Box as any, { p: 'lg', ta: 'center', c: 'var(--ds-text-3)', fz: 'sm' }, () =>\n slots.empty || props.empty != null ? resolveNode(props.empty, slots.empty) : 'Nenhuma conversa encontrada',\n )\n : null,\n ...filtered.value.map(renderItem),\n ]),\n ),\n ]);\n },\n}) as unknown as DefineSetupFnComponent<ConversationListProps, ConversationListEmits, SlotsType<ConversationListSlots>>;\n","import { defineComponent, h, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { ActionIcon, Avatar, Box, Indicator, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { IconArrowLeft } from '@tabler/icons-vue';\nimport { initialsName } from './utils';\nimport { hasContent, nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatHeaderProps extends BoxProps {\n /** Nome da conversa. O slot `#title` tem prioridade. */\n title?: MantineNode;\n /** Status/descrição abaixo do título. O slot `#subtitle` tem prioridade. */\n subtitle?: MantineNode;\n avatar?: string;\n /** Nome para iniciais do avatar */\n avatarName?: string;\n online?: boolean;\n /** Ações à direita (botões, menu). O slot `#actions` tem prioridade. */\n actions?: MantineNode;\n}\n\nexport type ChatHeaderEmits = {\n /** Clique no botão voltar. O botão (mobile) só aparece quando há um listener `@back`. */\n back: () => void;\n}\n\nexport interface ChatHeaderSlots {\n title?: () => VNodeChild;\n subtitle?: () => VNodeChild;\n actions?: () => VNodeChild;\n}\n\n/**\n * Cabeçalho da conversa: avatar, nome, status e ações.\n *\n * O botão voltar aparece só com `@back` (equivalente à presença de `onBack` no @jcdecor/ui):\n * `onBack` é declarado como prop — padrão do Vue para detectar se o pai escutou o evento.\n */\nexport const ChatHeader = defineComponent({\n name: 'ChatHeader',\n props: {\n title: nodeProp,\n subtitle: nodeProp,\n avatar: { type: String, default: undefined },\n avatarName: { type: String, default: undefined },\n online: { type: Boolean, default: false },\n actions: nodeProp,\n onBack: { type: [Function, Array] as PropType<(() => void) | Array<() => void>>, default: undefined },\n },\n setup(props, { slots }) {\n const back = () => {\n const handlers = props.onBack;\n if (Array.isArray(handlers)) handlers.forEach((fn) => fn());\n else handlers?.();\n };\n\n return () => {\n const subtitle = resolveNode(props.subtitle, slots.subtitle);\n return h(Box as any, { class: classes.header }, () => [\n props.onBack\n ? h(ActionIcon as any, { class: classes.backButton, variant: 'subtle', onClick: back, 'aria-label': 'Voltar' }, () =>\n h(IconArrowLeft, { size: 20 }),\n )\n : null,\n props.avatar || props.avatarName\n ? h(\n Indicator as any,\n { color: 'evergreen', position: 'bottom-end', offset: 5, size: 10, withBorder: true, disabled: !props.online },\n () =>\n h(Avatar as any, {\n src: props.avatar,\n name: props.avatarName ? initialsName(props.avatarName) : undefined,\n color: 'initials',\n size: 40,\n }),\n )\n : null,\n h('div', { class: classes.headerBody }, [\n h('div', { class: classes.headerTitle }, [resolveNode(props.title, slots.title)]),\n hasContent(subtitle) ? h('div', { class: classes.headerSubtitle }, [subtitle]) : null,\n ]),\n resolveNode(props.actions, slots.actions),\n ]);\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatHeaderProps, ChatHeaderEmits, SlotsType<ChatHeaderSlots>>;\n\nexport interface ChatLayoutProps extends BoxProps {\n /** Lista de conversas (coluna esquerda) — omita para um chat único. O slot `#sidebar` tem prioridade. */\n sidebar?: MantineNode;\n /** Normalmente um <ChatHeader />. O slot `#header` tem prioridade. */\n header?: MantineNode;\n /** Normalmente um <ChatComposer />. O slot `#composer` tem prioridade. */\n composer?: MantineNode;\n /** Altura total @default 600 */\n height?: number | string;\n /** @default 320 */\n sidebarWidth?: number | string;\n /** Em telas pequenas mostra só a lista ou só a conversa @default 'thread' */\n mobileView?: 'list' | 'thread';\n}\n\nexport interface ChatLayoutSlots {\n /** Normalmente um <ChatThread /> */\n default?: () => VNodeChild;\n sidebar?: () => VNodeChild;\n header?: () => VNodeChild;\n composer?: () => VNodeChild;\n}\n\nconst toCss = (v: number | string) => (typeof v === 'number' ? `${v}px` : v);\n\n/** Layout de chat: conversas + cabeçalho + mensagens + composer (responsivo). */\nexport const ChatLayout = defineComponent({\n name: 'ChatLayout',\n props: {\n sidebar: nodeProp,\n header: nodeProp,\n composer: nodeProp,\n height: { type: [Number, String], default: 600 },\n sidebarWidth: { type: [Number, String], default: 320 },\n mobileView: { type: String as PropType<'list' | 'thread'>, default: 'thread' },\n },\n setup(props, { slots }) {\n return () => {\n const sidebar = resolveNode(props.sidebar, slots.sidebar);\n const withSidebar = hasContent(sidebar);\n // O wrapper é o contêiner das container queries: o layout se adapta ao espaço disponível, não à tela.\n // Estilos do usuário (`style` em atributos) são mesclados depois destas variáveis.\n return h(\n Box as any,\n {\n class: classes.layoutContainer,\n style: { '--chat-height': toCss(props.height), '--chat-sidebar-width': toCss(props.sidebarWidth) },\n },\n () =>\n h(\n 'div',\n {\n class: classes.layout,\n 'data-mobile-view': withSidebar ? props.mobileView : undefined,\n 'data-single': withSidebar ? undefined : true,\n },\n [\n withSidebar ? h('aside', { class: classes.layoutSidebar }, [sidebar]) : null,\n h('section', { class: classes.layoutMain }, [\n resolveNode(props.header, slots.header),\n h('div', { class: classes.layoutMessages }, slots.default?.()),\n resolveNode(props.composer, slots.composer),\n ]),\n ],\n ),\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatLayoutProps, {}, SlotsType<ChatLayoutSlots>>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat.js","names":["Text","classes","Tooltip","classes","classes","classes","Tooltip","classes","classes","Tooltip$1","Text$1","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","nodeProp","hasContent","node","child","isVNode","Comment","Fragment","Chat_module_default","statusLabel","statusIcon","status","h","Loader","IconCheck","IconAlertCircle","IconChecks","timeFormat","ChatMessage","defineComponent","children","slots","props","Box","isLast","isFirst","time","createdAt","avatarSlot","own","author","Avatar","attachmentList","attachments","file","IconFile","meta","typingLabel","names","TypingIndicator","label","NEAR_BOTTOM","ChatThread","viewport","content","nearBottom","mounted","observer","userMap","computed","u","scrollToEnd","el","lastMessage","observeContent","onMounted","watch","onBeforeUnmount","viewportProps","setContent","onScrollPositionChange","y","renderBubble","emptyNode","item","bubble","ScrollArea","resolveNode","ChatComposer","text","setText","useUncontrolled","emit","files","ref","canSend","hasLeft","hasRight","send","onKeydown","event","removeFile","i","addFiles","selected","Pill","FileButton","ActionIcon","triggerProps","IconPaperclip","Textarea","IconPlayerStopFilled","IconSend2","ConversationList","query","filtered","q","c","renderItem","unread","active","button","Indicator","Badge","TextInput","IconSearch","ChatHeader","back","handlers","fn","subtitle","IconArrowLeft","toCss","v","ChatLayout","sidebar","withSidebar"],"sources":["../src/chat/utils.ts","../src/chat/node.ts","../src/chat/Chat.module.css","../src/chat/ChatMessage.ts","../src/chat/TypingIndicator.ts","../src/chat/ChatThread.ts","../src/chat/ChatComposer.ts","../src/chat/ConversationList.ts","../src/chat/ChatLayout.ts"],"sourcesContent":["// @generated sincronizado de packages/ui/src/chat/utils.ts — edite lá e rode `npm run sync -w @jcdecor/vue`\nimport 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","import { Comment, Fragment, Text, isVNode, type PropType, type VNodeChild } from 'vue';\nimport type { MantineNode } from '@mantine-vue/core';\n\n/**\n * Prop que espelha um `React.ReactNode` do @jcdecor/ui: aceita VNode, texto ou função de render.\n * Sem validação de tipo em runtime (VNodeChild inclui string, número, array, objeto, função…).\n */\nexport const nodeProp = { type: null as unknown as PropType<MantineNode>, default: undefined };\n\n/** `true` se o conteúdo renderiza algo visível (ignora null, false, '', comentários e fragments vazios). */\nexport function hasContent(node: VNodeChild): boolean {\n if (node === null || node === undefined || node === false || node === true || node === '') return false;\n if (Array.isArray(node)) return node.some((child) => hasContent(child as VNodeChild));\n if (isVNode(node)) {\n if (node.type === Comment) return false;\n if (node.type === Text) return node.children !== '' && node.children != null;\n if (node.type === Fragment) return hasContent(node.children as VNodeChild);\n }\n return true;\n}\n","/* @generated sincronizado de packages/ui/src/chat/Chat.module.css — edite lá e rode `npm run sync -w @jcdecor/vue` */\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/* variante plain: sem bolha, largura total (respostas longas com markdown/tabelas) */\n.message[data-variant='plain'] {\n align-items: flex-start;\n}\n.message[data-variant='plain'] .bubbleColumn {\n flex: 1;\n max-width: 100%;\n}\n.message[data-variant='plain'] .bubble {\n width: 100%;\n padding: 0;\n background: none;\n border: 0;\n box-shadow: none;\n white-space: normal;\n overflow-wrap: break-word;\n}\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.composerSection {\n display: flex;\n align-items: center;\n gap: var(--sp-4);\n flex-shrink: 0;\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.conversationItem {\n display: flex;\n align-items: center;\n border-bottom: 1px solid var(--ds-border-soft);\n transition: background 0.15s;\n}\n.conversationItem:hover {\n background: var(--ds-surface-2);\n}\n.conversationItem[data-active] {\n background: var(--ds-primary-soft);\n box-shadow: inset 3px 0 0 var(--ds-primary);\n}\n.conversation {\n all: unset;\n box-sizing: border-box;\n display: flex;\n gap: var(--sp-12);\n align-items: center;\n flex: 1;\n min-width: 0;\n padding: var(--sp-12) var(--sp-16);\n cursor: pointer;\n}\n.conversation:focus-visible {\n outline: 2px solid var(--ds-primary);\n outline-offset: -2px;\n}\n.conversationIcon {\n display: grid;\n place-items: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--ds-surface-2);\n color: var(--ds-text-2);\n}\n.conversationItem[data-active] .conversationIcon {\n background: var(--ds-surface);\n color: var(--ds-primary);\n}\n.conversationActions {\n flex-shrink: 0;\n padding-right: var(--sp-8);\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.conversationItem[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.conversationItem[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 { defineComponent, h, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { Avatar, Box, Loader, type BoxProps } from '@mantine-vue/core';\nimport { Tooltip } from '../theme/themeDefaults';\nimport { IconAlertCircle, IconCheck, IconChecks, IconFile } from '@tabler/icons-vue';\nimport { formatBytes, initialsName, toDate, type GroupPosition } from './utils';\nimport type { ChatAttachment, ChatMessageStatus, ChatUser } from './types';\nimport { hasContent } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatMessageProps extends 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 /** `plain` = sem bolha e em largura total (respostas de assistente com markdown/tabelas) @default 'bubble' */\n variant?: 'bubble' | 'plain';\n}\n\nexport interface ChatMessageSlots {\n /** Conteúdo da bolha (texto, links, cards…). Sem conteúdo, a bolha não é renderizada. */\n default?: () => VNodeChild;\n /** Avatar customizado (ex.: ícone do assistente); substitui o avatar do `author` */\n avatar?: () => VNodeChild;\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: ChatMessageStatus) {\n if (status === 'sending') return h(Loader as any, { size: 10, color: 'gray' });\n if (status === 'sent') return h(IconCheck, { size: 14 });\n if (status === 'error') return h(IconAlertCircle, { size: 14, color: 'var(--ds-error)' });\n return h(IconChecks, { size: 14 });\n}\n\nconst timeFormat = new Intl.DateTimeFormat('pt-BR', { hour: '2-digit', minute: '2-digit' });\n\n/** Bolha de mensagem de chat. O slot padrão é o conteúdo da bolha; atributos vão para o Box raiz. */\nexport const ChatMessage = defineComponent({\n name: 'ChatMessage',\n props: {\n own: { type: Boolean, default: false },\n author: { type: Object as PropType<ChatUser>, default: undefined },\n showAuthor: { type: Boolean, default: false },\n showAvatar: { type: Boolean, default: true },\n createdAt: { type: [Date, String, Number] as PropType<Date | string | number>, default: undefined },\n status: { type: String as PropType<ChatMessageStatus>, default: undefined },\n attachments: { type: Array as PropType<ChatAttachment[]>, default: undefined },\n position: { type: String as PropType<GroupPosition>, default: 'single' },\n system: { type: Boolean, default: false },\n variant: { type: String as PropType<'bubble' | 'plain'>, default: 'bubble' },\n },\n setup(props, { slots }) {\n return () => {\n const children = slots.default?.();\n\n if (props.system) {\n return h(Box as any, { class: classes.system, role: 'status' }, () => children);\n }\n\n const { own, author, status, position, createdAt, attachments } = props;\n const isLast = position === 'single' || position === 'last';\n const isFirst = position === 'single' || position === 'first';\n const time = createdAt ? timeFormat.format(toDate(createdAt)) : null;\n\n const avatarSlot = !own\n ? h(\n 'div',\n { class: classes.avatarSlot },\n props.showAvatar && (props.variant === 'plain' ? isFirst : isLast) && slots.avatar\n ? slots.avatar()\n : props.showAvatar && (props.variant === 'plain' ? isFirst : isLast) && author\n ? [\n h(Avatar as any, {\n src: author.avatar,\n name: initialsName(author.name),\n color: author.color ?? 'initials',\n size: 32,\n alt: author.name,\n }),\n ]\n : undefined,\n )\n : null;\n\n const attachmentList =\n attachments && attachments.length > 0\n ? h(\n 'div',\n { class: classes.attachments },\n attachments.map((file, index) =>\n file.type === 'image' && file.url\n ? h('a', { key: index, href: file.url, target: '_blank', rel: 'noreferrer' }, [\n h('img', { class: classes.imageAttachment, src: file.url, alt: file.name }),\n ])\n : h(\n 'a',\n {\n key: index,\n class: classes.fileAttachment,\n href: file.url,\n target: '_blank',\n rel: 'noreferrer',\n download: file.name,\n },\n [\n h(IconFile, { size: 18 }),\n h('span', { class: classes.fileName }, file.name),\n file.size !== undefined ? h('span', { class: classes.fileSize }, formatBytes(file.size)) : null,\n ],\n ),\n ),\n )\n : null;\n\n const meta =\n isLast && (time || (own && status))\n ? h('div', { class: classes.meta, 'data-read': status === 'read' || undefined }, [\n time && createdAt\n ? h('time', { datetime: toDate(createdAt).toISOString() }, time)\n : null,\n own && status\n ? h(\n Tooltip as any,\n { label: statusLabel[status], withArrow: true },\n () =>\n h(\n 'span',\n { class: classes.status, 'aria-label': statusLabel[status], style: { display: 'inline-flex' } },\n [statusIcon(status)],\n ),\n )\n : null,\n ])\n : null;\n\n return h(Box as any, { class: classes.message, mod: { own, position, status }, 'data-variant': props.variant === 'plain' ? 'plain' : undefined }, () => [\n avatarSlot,\n h('div', { class: classes.bubbleColumn }, [\n props.showAuthor && isFirst && author && !own ? h('div', { class: classes.author }, author.name) : null,\n attachmentList,\n hasContent(children) ? h('div', { class: classes.bubble }, children) : null,\n meta,\n ]),\n ]);\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatMessageProps, {}, SlotsType<ChatMessageSlots>>;\n","import { defineComponent, h, type DefineSetupFnComponent, type PropType } from 'vue';\nimport { Box, type BoxProps } from '@mantine-vue/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…\". Atributos e style props (`mt`, `ml`…) vão para o Box raiz. */\nexport const TypingIndicator = defineComponent({\n name: 'TypingIndicator',\n props: {\n names: { type: Array as PropType<string[]>, default: () => [] },\n },\n setup(props) {\n return () => {\n const label = typingLabel(props.names);\n return h(\n Box as any,\n { class: classes.typing, role: 'status', 'aria-live': 'polite', 'aria-label': label || 'Digitando' },\n () => [\n h('span', { class: classes.dots, 'aria-hidden': 'true' }, [\n h('span', { class: classes.dot }),\n h('span', { class: classes.dot }),\n h('span', { class: classes.dot }),\n ]),\n label ? h('span', label) : null,\n ],\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<TypingIndicatorProps>;\n","import {\n computed,\n defineComponent,\n h,\n onBeforeUnmount,\n onMounted,\n watch,\n type DefineSetupFnComponent,\n type PropType,\n type SlotsType,\n type VNodeChild,\n} from 'vue';\nimport { Box, ScrollArea, resolveNode, type MantineNode, type ScrollAreaProps } from '@mantine-vue/core';\nimport { ChatMessage } from './ChatMessage';\nimport { TypingIndicator } from './TypingIndicator';\nimport { buildThread } from './utils';\nimport type { ChatMessageData, ChatUser } from './types';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatThreadProps extends ScrollAreaProps {\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. O slot `#empty` tem prioridade. */\n empty?: MantineNode;\n /** Renderização customizada do conteúdo da bolha. O slot `#content=\"{ message }\"` tem prioridade. */\n renderContent?: (message: ChatMessageData) => VNodeChild;\n /** Conteúdo ao fim da conversa, antes do \"digitando\" (ex.: status de uma resposta em andamento). O slot `#footer` tem prioridade. */\n footer?: MantineNode;\n /** Altura da área rolável @default '100%' */\n h?: ScrollAreaProps['h'];\n}\n\nexport interface ChatThreadSlots {\n /** Conteúdo exibido quando não há mensagens */\n empty?: () => VNodeChild;\n /** Conteúdo customizado da bolha de cada mensagem */\n content?: (props: { message: ChatMessageData }) => VNodeChild;\n /** Avatar customizado por mensagem (ex.: ícone do assistente) */\n avatar?: (props: { message: ChatMessageData }) => VNodeChild;\n /** Conteúdo ao fim da conversa, antes do \"digitando\" */\n footer?: () => VNodeChild;\n}\n\n/** Distância (px) do fim abaixo da qual o usuário é considerado \"no fim\" da conversa. */\nconst NEAR_BOTTOM = 80;\n\n/**\n * Lista rolável de mensagens com separadores de data, agrupamento e auto-scroll.\n * Atributos e props do ScrollArea (`type`, `scrollbarSize`…) vão para o ScrollArea raiz.\n */\nexport const ChatThread = defineComponent({\n name: 'ChatThread',\n props: {\n messages: { type: Array as PropType<ChatMessageData[]>, required: true },\n currentUserId: { type: String, required: true },\n users: { type: [Array, Object] as PropType<ChatUser[] | Record<string, ChatUser>>, default: () => [] },\n typing: { type: Array as PropType<string[]>, default: undefined },\n showAuthors: { type: Boolean, default: false },\n autoScroll: { type: Boolean, default: true },\n groupWindow: { type: Number, default: undefined },\n empty: nodeProp,\n footer: nodeProp,\n renderContent: { type: Function as PropType<(message: ChatMessageData) => VNodeChild>, default: undefined },\n h: { type: [String, Number] as PropType<ScrollAreaProps['h']>, default: '100%' },\n },\n setup(props, { slots }) {\n let viewport: HTMLDivElement | null = null;\n let content: HTMLElement | null = null;\n let nearBottom = true;\n let mounted = false;\n let observer: ResizeObserver | null = null;\n\n const userMap = computed<Record<string, ChatUser>>(() =>\n Array.isArray(props.users) ? Object.fromEntries(props.users.map((u) => [u.id, u])) : props.users,\n );\n const items = computed(() => buildThread(props.messages, props.groupWindow));\n\n function scrollToEnd() {\n const el = viewport;\n if (!el || !props.autoScroll) return;\n const lastMessage = props.messages[props.messages.length - 1];\n if (nearBottom || lastMessage?.authorId === props.currentUserId) {\n // Primeira renderização: pula direto para o fim; depois, rolagem suave.\n el.scrollTo({ top: el.scrollHeight, behavior: mounted ? 'smooth' : 'auto' });\n }\n mounted = true;\n }\n\n // Conteúdo que cresce depois (imagens, fontes) mantém o fim visível se o usuário já estava lá.\n function observeContent() {\n observer?.disconnect();\n observer = null;\n const el = viewport;\n if (!el || !content || !props.autoScroll || typeof ResizeObserver === 'undefined') return;\n observer = new ResizeObserver(() => {\n if (nearBottom) el.scrollTo({ top: el.scrollHeight });\n });\n observer.observe(content);\n }\n\n onMounted(() => {\n scrollToEnd();\n observeContent();\n });\n\n watch(\n () => [\n props.messages.length,\n props.messages[props.messages.length - 1]?.id,\n props.typing?.length,\n props.autoScroll,\n props.currentUserId,\n ],\n scrollToEnd,\n { flush: 'post' },\n );\n watch(() => props.autoScroll, observeContent, { flush: 'post' });\n\n onBeforeUnmount(() => observer?.disconnect());\n\n // O ScrollArea do Mantine Vue não tem `viewportRef`: o elemento chega via `viewportProps.rootRef`.\n const viewportProps = {\n rootRef: (node: Element | null) => {\n viewport = node as HTMLDivElement | null;\n },\n };\n const setContent = (node: Element | null) => {\n content = node as HTMLElement | null;\n };\n const onScrollPositionChange = ({ y }: { x: number; y: number }) => {\n const el = viewport;\n if (el) nearBottom = el.scrollHeight - el.clientHeight - y < NEAR_BOTTOM;\n };\n\n return () => {\n const renderBubble = (message: ChatMessageData): VNodeChild =>\n slots.content\n ? slots.content({ message })\n : props.renderContent\n ? props.renderContent(message)\n : message.text;\n\n const emptyNode =\n props.messages.length === 0\n ? slots.empty\n ? slots.empty()\n : typeof props.empty === 'function'\n ? props.empty()\n : props.empty\n : null;\n\n const children = items.value.map((item) => {\n if (item.type === 'separator') {\n return h('div', { key: item.key, class: classes.separator, role: 'separator' }, item.label);\n }\n const { message, position } = item;\n const own = message.authorId === props.currentUserId;\n const bubble = renderBubble(message);\n return h(\n ChatMessage as any,\n {\n key: item.key,\n own,\n system: message.system,\n variant: message.variant,\n author: userMap.value[message.authorId],\n showAuthor: props.showAuthors,\n createdAt: message.createdAt,\n status: own ? message.status : undefined,\n attachments: message.attachments,\n position,\n 'data-group-start': position === 'single' || position === 'first' ? true : undefined,\n },\n slots.avatar\n ? { default: () => bubble, avatar: () => slots.avatar!({ message }) }\n : { default: () => bubble },\n );\n });\n\n return h(\n ScrollArea as any,\n { h: props.h, viewportProps, onScrollPositionChange },\n () =>\n h(\n Box as any,\n {\n rootRef: setContent,\n class: classes.thread,\n role: 'log',\n 'aria-live': 'polite',\n 'aria-relevant': 'additions',\n },\n () => [\n emptyNode,\n ...children,\n slots.footer || props.footer != null ? resolveNode(props.footer, slots.footer) : null,\n props.typing && props.typing.length > 0\n ? h(TypingIndicator as any, { names: props.typing, mt: 'sm', ml: 40 })\n : null,\n ],\n ),\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatThreadProps, {}, SlotsType<ChatThreadSlots>>;\n","import { computed, defineComponent, h, ref, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { ActionIcon, Box, FileButton, Pill, Textarea, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { Tooltip } from '../theme/themeDefaults';\nimport { useUncontrolled } from '@mantine-vue/hooks';\nimport { IconPaperclip, IconPlayerStopFilled, IconSend2 } from '@tabler/icons-vue';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatComposerSendPayload {\n text: string;\n files: File[];\n}\n\nexport interface ChatComposerProps extends BoxProps {\n /** Texto controlado (`v-model`). Sem `v-model`, o campo é não controlado. */\n modelValue?: string;\n /** Texto inicial no modo não controlado */\n defaultValue?: string;\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). O slot `#leftSection` tem prioridade. */\n leftSection?: MantineNode;\n /** Elementos extras à direita do campo, antes do botão de enviar. O slot `#rightSection` tem prioridade. */\n rightSection?: MantineNode;\n sendLabel?: string;\n /**\n * Uma resposta está em andamento: o envio fica bloqueado (o texto digitado é mantido e o campo\n * continua editável). Com um ouvinte `@stop`, o botão de enviar vira o botão de interromper.\n */\n loading?: boolean;\n stopLabel?: string;\n /** Bloqueia o envio sem desabilitar o campo (ex.: integração ainda não conectada) */\n sendDisabled?: boolean;\n}\n\nexport type ChatComposerEmits = {\n /** Ao enviar (Enter ou botão). O campo é limpo em seguida. */\n send: (payload: ChatComposerSendPayload) => void;\n /** `v-model` do texto */\n 'update:modelValue': (value: string) => void;\n /** Botão de interromper (visível enquanto `loading`) */\n stop: () => void;\n}\n\nexport interface ChatComposerSlots {\n /** Elementos extras à esquerda (ex.: emoji picker, respostas rápidas) */\n leftSection?: () => VNodeChild;\n /** Elementos extras à direita do campo, antes do botão de enviar */\n rightSection?: () => VNodeChild;\n}\n\n/** Campo de mensagem: Enter envia, Shift+Enter quebra linha, anexos opcionais. */\nexport const ChatComposer = defineComponent({\n name: 'ChatComposer',\n props: {\n modelValue: { type: String, default: undefined },\n defaultValue: { type: String, default: undefined },\n placeholder: { type: String, default: 'Digite uma mensagem…' },\n disabled: { type: Boolean, default: false },\n allowAttachments: { type: Boolean, default: false },\n accept: { type: String, default: undefined },\n maxRows: { type: Number, default: 5 },\n leftSection: nodeProp,\n sendLabel: { type: String, default: 'Enviar' },\n rightSection: nodeProp,\n loading: { type: Boolean, default: false },\n stopLabel: { type: String, default: 'Interromper' },\n sendDisabled: { type: Boolean, default: false },\n onStop: { type: Function as PropType<() => void>, default: undefined },\n },\n emits: {\n send: (_payload: ChatComposerSendPayload) => true,\n 'update:modelValue': (_value: string) => true,\n },\n setup(props, { emit, slots }) {\n const [text, setText] = useUncontrolled<string>({\n value: () => props.modelValue,\n defaultValue: props.defaultValue,\n finalValue: '',\n onChange: (value) => emit('update:modelValue', value),\n });\n const files = ref<File[]>([]);\n const canSend = computed(\n () => !props.disabled && !props.sendDisabled && !props.loading && (text.value.trim().length > 0 || files.value.length > 0),\n );\n const hasLeft = () => props.leftSection != null || !!slots.leftSection;\n const hasRight = () => props.rightSection != null || !!slots.rightSection;\n\n const send = () => {\n if (!canSend.value) return;\n emit('send', { text: text.value.trim(), files: files.value });\n setText('');\n files.value = [];\n };\n\n const onKeydown = (event: KeyboardEvent) => {\n if (event.key === 'Enter' && !event.shiftKey && !event.isComposing) {\n event.preventDefault();\n send();\n }\n };\n\n const removeFile = (index: number) => {\n files.value = files.value.filter((_, i) => i !== index);\n };\n\n const addFiles = (selected: File | File[] | null) => {\n files.value = [...files.value, ...(Array.isArray(selected) ? selected : selected ? [selected] : [])];\n };\n\n return () =>\n h(Box as any, null, () => [\n files.value.length > 0\n ? h(\n 'div',\n { class: classes.composerFiles },\n files.value.map((file, index) =>\n h(\n Pill as any,\n { key: `${file.name}-${index}`, withRemoveButton: true, onRemove: () => removeFile(index) },\n () => file.name,\n ),\n ),\n )\n : null,\n h('div', { class: classes.composer }, [\n hasLeft() ? h('div', { class: classes.composerSection }, [resolveNode(props.leftSection, slots.leftSection)]) : null,\n props.allowAttachments\n ? h(\n FileButton as any,\n { multiple: true, accept: props.accept, disabled: props.disabled, onChange: addFiles },\n {\n default: (triggerProps: Record<string, unknown>) =>\n h(Tooltip as any, { label: 'Anexar arquivo' }, () =>\n h(\n ActionIcon as any,\n { ...triggerProps, size: 'lg', variant: 'subtle', 'aria-label': 'Anexar arquivo' },\n () => h(IconPaperclip, { size: 20 }),\n ),\n ),\n },\n )\n : null,\n h(Textarea as any, {\n class: classes.composerInput,\n modelValue: text.value,\n 'onUpdate:modelValue': (value: string) => setText(value),\n onKeydown,\n placeholder: props.placeholder,\n disabled: props.disabled,\n autosize: true,\n minRows: 1,\n maxRows: props.maxRows,\n radius: 'lg',\n 'aria-label': props.placeholder,\n }),\n hasRight() ? h('div', { class: classes.composerSection }, [resolveNode(props.rightSection, slots.rightSection)]) : null,\n props.loading && props.onStop\n ? h(Tooltip as any, { label: props.stopLabel }, () =>\n h(\n ActionIcon as any,\n { size: 44, radius: 'xl', variant: 'light', color: 'danger', onClick: () => props.onStop?.(), 'aria-label': props.stopLabel },\n () => h(IconPlayerStopFilled, { size: 18 }),\n ),\n )\n : h(\n ActionIcon as any,\n { size: 44, radius: 'xl', variant: 'filled', onClick: send, disabled: !canSend.value, 'aria-label': props.sendLabel },\n () => h(IconSend2, { size: 20 }),\n ),\n ]),\n ]);\n },\n}) as unknown as DefineSetupFnComponent<ChatComposerProps, ChatComposerEmits, SlotsType<ChatComposerSlots>>;\n","import { computed, defineComponent, h, ref, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { Avatar, Badge, Box, Indicator, ScrollArea, TextInput, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { IconSearch } from '@tabler/icons-vue';\nimport { initialsName, shortTimeLabel } from './utils';\nimport type { Conversation } from './types';\nimport { nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ConversationListProps extends BoxProps {\n conversations: Conversation[];\n activeId?: string | null;\n /** Exibe campo de busca @default true */\n searchable?: boolean;\n searchPlaceholder?: string;\n /** Conteúdo quando a lista (ou a busca) fica vazia. O slot `#empty` tem prioridade. */\n empty?: MantineNode;\n}\n\nexport type ConversationListEmits = {\n /** Clique em uma conversa */\n select: (conversation: Conversation) => void;\n}\n\nexport interface ConversationListSlots {\n /** Conteúdo quando a lista (ou a busca) fica vazia */\n empty?: () => VNodeChild;\n /** Ícone no lugar do avatar com iniciais (ex.: conversas com um assistente) */\n icon?: (props: { conversation: Conversation }) => VNodeChild;\n /** Ações por conversa (ex.: menu renomear/excluir), à direita do item */\n actions?: (props: { conversation: Conversation }) => VNodeChild;\n}\n\n/** Lista de conversas com busca, não lidas, presença e última mensagem. */\nexport const ConversationList = defineComponent({\n name: 'ConversationList',\n props: {\n conversations: { type: Array as PropType<Conversation[]>, required: true },\n activeId: { type: String as PropType<string | null>, default: undefined },\n searchable: { type: Boolean, default: true },\n searchPlaceholder: { type: String, default: 'Buscar conversas' },\n empty: nodeProp,\n },\n emits: {\n select: (_conversation: Conversation) => true,\n },\n setup(props, { emit, slots }) {\n const query = ref('');\n const filtered = computed(() => {\n const q = query.value.trim().toLocaleLowerCase('pt-BR');\n if (!q) return props.conversations;\n return props.conversations.filter(\n (c) => c.name.toLocaleLowerCase('pt-BR').includes(q) || c.lastMessage?.toLocaleLowerCase('pt-BR').includes(q),\n );\n });\n\n const renderItem = (c: Conversation) => {\n const unread = c.unread ?? 0;\n const active = c.id === props.activeId;\n const button = h(\n 'button',\n {\n type: 'button',\n class: classes.conversation,\n 'aria-current': active ? 'true' : undefined,\n onClick: () => emit('select', c),\n },\n [\n h(\n Indicator as any,\n { color: 'evergreen', position: 'bottom-end', offset: 5, size: 10, withBorder: true, disabled: !c.online },\n () =>\n slots.icon\n ? h('span', { class: classes.conversationIcon }, slots.icon({ conversation: c }))\n : h(Avatar as any, { src: c.avatar, name: initialsName(c.name), color: c.color ?? 'initials', size: 40 }),\n ),\n h('div', { class: classes.conversationBody }, [\n h('div', { class: classes.conversationTop }, [\n h('span', { class: classes.conversationName }, c.name),\n c.lastMessageAt !== undefined\n ? h('span', { class: classes.conversationTime }, shortTimeLabel(c.lastMessageAt))\n : null,\n ]),\n h('div', { class: classes.conversationBottom }, [\n h('span', { class: classes.conversationPreview }, c.lastMessage),\n unread > 0\n ? h(\n Badge as any,\n { size: 'sm', variant: 'filled', circle: unread < 10, 'aria-label': `${c.unread} não lidas` },\n () => (unread > 99 ? '99+' : String(unread)),\n )\n : null,\n ]),\n c.tag ? h(Badge as any, { size: 'xs', color: 'obsidian', mt: 6 }, () => c.tag) : null,\n ]),\n ],\n );\n return h(\n 'div',\n {\n key: c.id,\n role: 'listitem',\n class: classes.conversationItem,\n 'data-active': active || undefined,\n 'data-unread': unread > 0 || undefined,\n },\n [button, slots.actions ? h('div', { class: classes.conversationActions }, slots.actions({ conversation: c })) : null],\n );\n };\n\n return () =>\n h(Box as any, { class: classes.conversations }, () => [\n props.searchable\n ? h('div', { class: classes.conversationSearch }, [\n h(\n TextInput as any,\n {\n size: 'sm',\n placeholder: props.searchPlaceholder,\n modelValue: query.value,\n 'onUpdate:modelValue': (value: string) => (query.value = value),\n 'aria-label': props.searchPlaceholder,\n },\n { leftSection: () => h(IconSearch, { size: 16 }) },\n ),\n ])\n : null,\n h(ScrollArea as any, { style: { flex: 1 }, type: 'auto' }, () =>\n h('div', { role: 'list' }, [\n filtered.value.length === 0\n ? h(Box as any, { p: 'lg', ta: 'center', c: 'var(--ds-text-3)', fz: 'sm' }, () =>\n slots.empty || props.empty != null ? resolveNode(props.empty, slots.empty) : 'Nenhuma conversa encontrada',\n )\n : null,\n ...filtered.value.map(renderItem),\n ]),\n ),\n ]);\n },\n}) as unknown as DefineSetupFnComponent<ConversationListProps, ConversationListEmits, SlotsType<ConversationListSlots>>;\n","import { defineComponent, h, type DefineSetupFnComponent, type PropType, type SlotsType, type VNodeChild } from 'vue';\nimport { ActionIcon, Avatar, Box, Indicator, resolveNode, type BoxProps, type MantineNode } from '@mantine-vue/core';\nimport { IconArrowLeft } from '@tabler/icons-vue';\nimport { initialsName } from './utils';\nimport { hasContent, nodeProp } from './node';\nimport classes from './Chat.module.css';\n\nexport interface ChatHeaderProps extends BoxProps {\n /** Nome da conversa. O slot `#title` tem prioridade. */\n title?: MantineNode;\n /** Status/descrição abaixo do título. O slot `#subtitle` tem prioridade. */\n subtitle?: MantineNode;\n avatar?: string;\n /** Nome para iniciais do avatar */\n avatarName?: string;\n online?: boolean;\n /** Ações à direita (botões, menu). O slot `#actions` tem prioridade. */\n actions?: MantineNode;\n}\n\nexport type ChatHeaderEmits = {\n /** Clique no botão voltar. O botão (mobile) só aparece quando há um listener `@back`. */\n back: () => void;\n}\n\nexport interface ChatHeaderSlots {\n title?: () => VNodeChild;\n subtitle?: () => VNodeChild;\n actions?: () => VNodeChild;\n}\n\n/**\n * Cabeçalho da conversa: avatar, nome, status e ações.\n *\n * O botão voltar aparece só com `@back` (equivalente à presença de `onBack` no @jcdecor/ui):\n * `onBack` é declarado como prop — padrão do Vue para detectar se o pai escutou o evento.\n */\nexport const ChatHeader = defineComponent({\n name: 'ChatHeader',\n props: {\n title: nodeProp,\n subtitle: nodeProp,\n avatar: { type: String, default: undefined },\n avatarName: { type: String, default: undefined },\n online: { type: Boolean, default: false },\n actions: nodeProp,\n onBack: { type: [Function, Array] as PropType<(() => void) | Array<() => void>>, default: undefined },\n },\n setup(props, { slots }) {\n const back = () => {\n const handlers = props.onBack;\n if (Array.isArray(handlers)) handlers.forEach((fn) => fn());\n else handlers?.();\n };\n\n return () => {\n const subtitle = resolveNode(props.subtitle, slots.subtitle);\n return h(Box as any, { class: classes.header }, () => [\n props.onBack\n ? h(ActionIcon as any, { class: classes.backButton, variant: 'subtle', onClick: back, 'aria-label': 'Voltar' }, () =>\n h(IconArrowLeft, { size: 20 }),\n )\n : null,\n props.avatar || props.avatarName\n ? h(\n Indicator as any,\n { color: 'evergreen', position: 'bottom-end', offset: 5, size: 10, withBorder: true, disabled: !props.online },\n () =>\n h(Avatar as any, {\n src: props.avatar,\n name: props.avatarName ? initialsName(props.avatarName) : undefined,\n color: 'initials',\n size: 40,\n }),\n )\n : null,\n h('div', { class: classes.headerBody }, [\n h('div', { class: classes.headerTitle }, [resolveNode(props.title, slots.title)]),\n hasContent(subtitle) ? h('div', { class: classes.headerSubtitle }, [subtitle]) : null,\n ]),\n resolveNode(props.actions, slots.actions),\n ]);\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatHeaderProps, ChatHeaderEmits, SlotsType<ChatHeaderSlots>>;\n\nexport interface ChatLayoutProps extends BoxProps {\n /** Lista de conversas (coluna esquerda) — omita para um chat único. O slot `#sidebar` tem prioridade. */\n sidebar?: MantineNode;\n /** Normalmente um <ChatHeader />. O slot `#header` tem prioridade. */\n header?: MantineNode;\n /** Normalmente um <ChatComposer />. O slot `#composer` tem prioridade. */\n composer?: MantineNode;\n /** Altura total @default 600 */\n height?: number | string;\n /** @default 320 */\n sidebarWidth?: number | string;\n /** Em telas pequenas mostra só a lista ou só a conversa @default 'thread' */\n mobileView?: 'list' | 'thread';\n}\n\nexport interface ChatLayoutSlots {\n /** Normalmente um <ChatThread /> */\n default?: () => VNodeChild;\n sidebar?: () => VNodeChild;\n header?: () => VNodeChild;\n composer?: () => VNodeChild;\n}\n\nconst toCss = (v: number | string) => (typeof v === 'number' ? `${v}px` : v);\n\n/** Layout de chat: conversas + cabeçalho + mensagens + composer (responsivo). */\nexport const ChatLayout = defineComponent({\n name: 'ChatLayout',\n props: {\n sidebar: nodeProp,\n header: nodeProp,\n composer: nodeProp,\n height: { type: [Number, String], default: 600 },\n sidebarWidth: { type: [Number, String], default: 320 },\n mobileView: { type: String as PropType<'list' | 'thread'>, default: 'thread' },\n },\n setup(props, { slots }) {\n return () => {\n const sidebar = resolveNode(props.sidebar, slots.sidebar);\n const withSidebar = hasContent(sidebar);\n // O wrapper é o contêiner das container queries: o layout se adapta ao espaço disponível, não à tela.\n // Estilos do usuário (`style` em atributos) são mesclados depois destas variáveis.\n return h(\n Box as any,\n {\n class: classes.layoutContainer,\n style: { '--chat-height': toCss(props.height), '--chat-sidebar-width': toCss(props.sidebarWidth) },\n },\n () =>\n h(\n 'div',\n {\n class: classes.layout,\n 'data-mobile-view': withSidebar ? props.mobileView : undefined,\n 'data-single': withSidebar ? undefined : true,\n },\n [\n withSidebar ? h('aside', { class: classes.layoutSidebar }, [sidebar]) : null,\n h('section', { class: classes.layoutMain }, [\n resolveNode(props.header, slots.header),\n h('div', { class: classes.layoutMessages }, slots.default?.()),\n resolveNode(props.composer, slots.composer),\n ]),\n ],\n ),\n );\n };\n },\n}) as unknown as DefineSetupFnComponent<ChatLayoutProps, {}, SlotsType<ChatLayoutSlots>>;\n"],"mappings":"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"}
|
|
@@ -1,29 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
title?: MantineNode;
|
|
12
|
-
/** Também aceita o slot `description` (o slot tem prioridade). */
|
|
13
|
-
description?: MantineNode;
|
|
14
|
-
/** Botões à direita do título. Também aceita o slot `actions` (o slot tem prioridade). */
|
|
15
|
-
actions?: MantineNode;
|
|
16
|
-
breadcrumbs?: PageHeaderBreadcrumb[];
|
|
17
|
-
/** Componente dos links do breadcrumb (ex.: `RouterLink` do vue-router) @default 'a' */
|
|
18
|
-
linkComponent?: string | Component;
|
|
19
|
-
/** `display` usa display-small (página principal); `headline` usa headline-large @default 'headline' */
|
|
20
|
-
size?: 'display' | 'headline';
|
|
21
|
-
}
|
|
22
|
-
export interface PageHeaderSlots {
|
|
23
|
-
kicker?: () => VNodeChild;
|
|
24
|
-
title?: () => VNodeChild;
|
|
25
|
-
description?: () => VNodeChild;
|
|
26
|
-
actions?: () => VNodeChild;
|
|
27
|
-
}
|
|
28
|
-
/** Cabeçalho de página: breadcrumbs, kicker, título, descrição e ações. */
|
|
29
|
-
export declare const PageHeader: DefineSetupFnComponent<PageHeaderProps, {}, SlotsType<PageHeaderSlots>>;
|
|
1
|
+
/** Cabeçalho de página: breadcrumbs, ícone, kicker, título, descrição e ações. */
|
|
2
|
+
export declare const PageHeader: import("@mantine-vue/core").MantineComponent<{
|
|
3
|
+
props: import("./PageHeader.types").PageHeaderProps;
|
|
4
|
+
ref: HTMLDivElement;
|
|
5
|
+
slots: import("./PageHeader.types").PageHeaderSlots;
|
|
6
|
+
element: "div";
|
|
7
|
+
stylesNames: import("./PageHeader.types").PageHeaderStylesNames;
|
|
8
|
+
vars: import("./PageHeader.types").PageHeaderCssVariables;
|
|
9
|
+
}>;
|
|
10
|
+
export type { PageHeaderBreadcrumb, PageHeaderCssVariables, PageHeaderFactory, PageHeaderOwnProps, PageHeaderProps, PageHeaderSlots, PageHeaderStylesNames, } from './PageHeader.types';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Component, VNodeChild } from 'vue';
|
|
2
|
+
import type { BoxProps, ClassNames, Factory, MantineNode, Styles, Vars } from '@mantine-vue/core';
|
|
3
|
+
export interface PageHeaderBreadcrumb {
|
|
4
|
+
label: MantineNode;
|
|
5
|
+
href?: string;
|
|
6
|
+
}
|
|
7
|
+
export type PageHeaderStylesNames = 'root' | 'breadcrumbs' | 'header' | 'main' | 'icon' | 'body' | 'kicker' | 'title' | 'description' | 'actions';
|
|
8
|
+
export type PageHeaderCssVariables = {
|
|
9
|
+
root: '--page-header-icon-size';
|
|
10
|
+
};
|
|
11
|
+
/** Props declaradas pelo próprio `PageHeader`. Veja `PageHeaderProps` para o tipo público completo. */
|
|
12
|
+
export interface PageHeaderOwnProps {
|
|
13
|
+
/** Classes por parte (`root`, `breadcrumbs`, `header`, `main`, `icon`, `body`, `kicker`, `title`, `description`, `actions`) */
|
|
14
|
+
classNames?: ClassNames<PageHeaderFactory>;
|
|
15
|
+
/** Estilos inline por parte */
|
|
16
|
+
styles?: Styles<PageHeaderFactory>;
|
|
17
|
+
/** Variáveis CSS por parte (`--page-header-icon-size`) */
|
|
18
|
+
vars?: Vars<PageHeaderFactory>;
|
|
19
|
+
/** Remove as classes do PageHeader (classNames/styles continuam valendo) @default false */
|
|
20
|
+
unstyled?: boolean;
|
|
21
|
+
/** Também aceita o slot `kicker` (o slot tem prioridade). */
|
|
22
|
+
kicker?: MantineNode;
|
|
23
|
+
/** Também aceita o slot `title` (o slot tem prioridade). */
|
|
24
|
+
title?: MantineNode;
|
|
25
|
+
/** Também aceita o slot `description` (o slot tem prioridade). */
|
|
26
|
+
description?: MantineNode;
|
|
27
|
+
/** Ícone à esquerda do título, num quadro suave na cor primária. Também aceita o slot `icon`. */
|
|
28
|
+
icon?: MantineNode;
|
|
29
|
+
/** Tamanho do quadro do ícone (px ou CSS) @default 48 */
|
|
30
|
+
iconSize?: number | string;
|
|
31
|
+
/** Botões à direita do título. Também aceita o slot `actions` (o slot tem prioridade). */
|
|
32
|
+
actions?: MantineNode;
|
|
33
|
+
breadcrumbs?: PageHeaderBreadcrumb[];
|
|
34
|
+
/** Componente dos links do breadcrumb (ex.: `RouterLink` do vue-router) @default 'a' */
|
|
35
|
+
linkComponent?: string | Component;
|
|
36
|
+
/** `display` usa display-small (página principal); `headline` usa headline-large @default 'headline' */
|
|
37
|
+
size?: 'display' | 'headline';
|
|
38
|
+
}
|
|
39
|
+
export interface PageHeaderProps extends Omit<BoxProps, keyof PageHeaderOwnProps>, PageHeaderOwnProps {
|
|
40
|
+
}
|
|
41
|
+
export interface PageHeaderSlots {
|
|
42
|
+
kicker?: () => VNodeChild;
|
|
43
|
+
title?: () => VNodeChild;
|
|
44
|
+
description?: () => VNodeChild;
|
|
45
|
+
icon?: () => VNodeChild;
|
|
46
|
+
actions?: () => VNodeChild;
|
|
47
|
+
}
|
|
48
|
+
export type PageHeaderFactory = Factory<{
|
|
49
|
+
props: PageHeaderProps;
|
|
50
|
+
ref: HTMLDivElement;
|
|
51
|
+
slots: PageHeaderSlots;
|
|
52
|
+
element: 'div';
|
|
53
|
+
stylesNames: PageHeaderStylesNames;
|
|
54
|
+
vars: PageHeaderCssVariables;
|
|
55
|
+
}>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Component, type VNodeChild } from 'vue';
|
|
2
|
+
import type { PageHeaderBreadcrumb, PageHeaderOwnProps, PageHeaderSlots } from './PageHeader.types';
|
|
3
|
+
type __VLS_Slots = PageHeaderSlots;
|
|
4
|
+
declare const __VLS_base: import("vue").DefineComponent<PageHeaderOwnProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<PageHeaderOwnProps> & Readonly<{}>, {
|
|
5
|
+
size: "display" | "headline";
|
|
6
|
+
title: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}> | import("vue").VNodeArrayChildren | (() => VNodeChild) | null;
|
|
9
|
+
vars: import("@mantine-vue/core").FactoryPartialVarsResolver<{
|
|
10
|
+
props: import("./PageHeader.types").PageHeaderProps;
|
|
11
|
+
ref: HTMLDivElement;
|
|
12
|
+
slots: PageHeaderSlots;
|
|
13
|
+
element: "div";
|
|
14
|
+
stylesNames: import("./PageHeader.types").PageHeaderStylesNames;
|
|
15
|
+
vars: import("./PageHeader.types").PageHeaderCssVariables;
|
|
16
|
+
} & import("@mantine-vue/core").FactoryPayload>;
|
|
17
|
+
icon: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}> | import("vue").VNodeArrayChildren | (() => VNodeChild) | null;
|
|
20
|
+
classNames: Partial<Record<"title" | "body" | "header" | "main" | "icon" | "description" | "kicker" | "actions" | "root" | "breadcrumbs", string>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./PageHeader.types").PageHeaderProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"title" | "body" | "header" | "main" | "icon" | "description" | "kicker" | "actions" | "root" | "breadcrumbs", string>>);
|
|
21
|
+
styles: Partial<Record<"title" | "body" | "header" | "main" | "icon" | "description" | "kicker" | "actions" | "root" | "breadcrumbs", import("vue").CSSProperties>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./PageHeader.types").PageHeaderProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"title" | "body" | "header" | "main" | "icon" | "description" | "kicker" | "actions" | "root" | "breadcrumbs", import("vue").CSSProperties>>);
|
|
22
|
+
unstyled: boolean;
|
|
23
|
+
description: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}> | import("vue").VNodeArrayChildren | (() => VNodeChild) | null;
|
|
26
|
+
kicker: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
27
|
+
[key: string]: any;
|
|
28
|
+
}> | import("vue").VNodeArrayChildren | (() => VNodeChild) | null;
|
|
29
|
+
actions: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
}> | import("vue").VNodeArrayChildren | (() => VNodeChild) | null;
|
|
32
|
+
iconSize: string | number;
|
|
33
|
+
breadcrumbs: PageHeaderBreadcrumb[];
|
|
34
|
+
linkComponent: string | Component;
|
|
35
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
36
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
37
|
+
declare const _default: typeof __VLS_export;
|
|
38
|
+
export default _default;
|
|
39
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
40
|
+
new (): {
|
|
41
|
+
$slots: S;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Component, VNodeChild } from 'vue';
|
|
2
2
|
import type { BoxProps, ClassNames, Factory, MantineNode, Styles, Vars } from '@mantine-vue/core';
|
|
3
|
-
export type TopNavStylesNames = 'root' | 'brand' | 'links' | 'link' | 'right' | 'burger' | 'mobileLinks';
|
|
3
|
+
export type TopNavStylesNames = 'root' | 'brand' | 'links' | 'link' | 'linkSection' | 'linkLabel' | 'right' | 'burger' | 'mobileLinks';
|
|
4
4
|
export interface TopNavLink {
|
|
5
5
|
/** Texto (ou conteúdo) do link */
|
|
6
6
|
label: MantineNode;
|
|
@@ -8,12 +8,20 @@ export interface TopNavLink {
|
|
|
8
8
|
href?: string;
|
|
9
9
|
/** Marca o link da página atual (`data-active` + `aria-current="page"`) */
|
|
10
10
|
active?: boolean;
|
|
11
|
+
/** Ícone/conteúdo antes do texto (como `leftSection` do Mantine) */
|
|
12
|
+
leftSection?: MantineNode;
|
|
13
|
+
/** Conteúdo depois do texto (ex.: contador, chevron) */
|
|
14
|
+
rightSection?: MantineNode;
|
|
15
|
+
/** Desabilita o link (sem navegação nem `onClick`) */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Nome acessível, necessário quando o link mostra só um ícone */
|
|
18
|
+
'aria-label'?: string;
|
|
11
19
|
/** Chamado no clique (antes de fechar o menu mobile) */
|
|
12
20
|
onClick?: (event: MouseEvent) => void;
|
|
13
21
|
}
|
|
14
22
|
/** Props declaradas pelo próprio `TopNav`. Veja `TopNavProps` para o tipo público completo. */
|
|
15
23
|
export interface TopNavOwnProps {
|
|
16
|
-
/** Classes por parte (`root`, `brand`, `links`, `link`, `right`, `burger`, `mobileLinks`) */
|
|
24
|
+
/** Classes por parte (`root`, `brand`, `links`, `link`, `linkSection`, `linkLabel`, `right`, `burger`, `mobileLinks`) */
|
|
17
25
|
classNames?: ClassNames<TopNavFactory>;
|
|
18
26
|
/** Estilos inline por parte */
|
|
19
27
|
styles?: Styles<TopNavFactory>;
|
|
@@ -10,8 +10,8 @@ declare const __VLS_base: import("vue").DefineComponent<TopNavOwnProps, {}, {},
|
|
|
10
10
|
element: "div";
|
|
11
11
|
stylesNames: import("./TopNav.types").TopNavStylesNames;
|
|
12
12
|
} & import("@mantine-vue/core").FactoryPayload>;
|
|
13
|
-
classNames: Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "mobileLinks", string>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./TopNav.types").TopNavProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "mobileLinks", string>>);
|
|
14
|
-
styles: Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "mobileLinks", import("vue").CSSProperties>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./TopNav.types").TopNavProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "mobileLinks", import("vue").CSSProperties>>);
|
|
13
|
+
classNames: Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "linkSection" | "linkLabel" | "mobileLinks", string>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./TopNav.types").TopNavProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "linkSection" | "linkLabel" | "mobileLinks", string>>);
|
|
14
|
+
styles: Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "linkSection" | "linkLabel" | "mobileLinks", import("vue").CSSProperties>> | ((theme: import("@mantine-vue/core").MantineTheme, props: import("./TopNav.types").TopNavProps & Record<string, any>, ctx: Record<string, any>) => Partial<Record<"right" | "link" | "links" | "root" | "burger" | "brand" | "linkSection" | "linkLabel" | "mobileLinks", import("vue").CSSProperties>>);
|
|
15
15
|
unstyled: boolean;
|
|
16
16
|
rightSection: string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
17
17
|
[key: string]: any;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import './styles/global.css';
|
|
2
|
+
import './styles/vue-fixes.css';
|
|
2
3
|
export * from '@mantine-vue/core';
|
|
3
|
-
export {
|
|
4
|
+
export { Transition as MantineTransition } from '@mantine-vue/core';
|
|
5
|
+
export { Autocomplete, Card, Cascader, CheckboxIndicator, Combobox, FileInput, FloatingWindow, MultiSelect, NativeSelect, NumberFormatter, NumberInput, PillsInput, RollingNumber, Select, Table, TagsInput, Timeline, Tooltip, TreeSelect, withThemeDefaults, } from './theme/themeDefaults';
|
|
4
6
|
export { jcTheme } from './theme/theme';
|
|
5
7
|
export { jcCssVariablesResolver } from './theme/cssVariablesResolver';
|
|
6
8
|
export { jcVariantColorResolver } from './theme/variantColorResolver';
|