@iclips/ui 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +246 -0
- package/README.md +4 -0
- package/dist/components/ui/data/comment-thread.cjs +2 -0
- package/dist/components/ui/data/comment-thread.cjs.map +1 -0
- package/dist/components/ui/data/comment-thread.js +617 -0
- package/dist/components/ui/data/comment-thread.js.map +1 -0
- package/dist/components/ui/primitives/badge.cjs +1 -1
- package/dist/components/ui/primitives/badge.cjs.map +1 -1
- package/dist/components/ui/primitives/badge.js +16 -13
- package/dist/components/ui/primitives/badge.js.map +1 -1
- package/dist/editor.cjs +1 -1
- package/dist/editor.d.cts +27 -2
- package/dist/editor.d.ts +27 -2
- package/dist/editor.js +6 -6
- package/dist/index.css +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/preset.css +90 -0
- package/docs/README.md +44 -0
- package/docs/acoes.md +103 -0
- package/docs/escolha.md +171 -0
- package/docs/formularios.md +137 -0
- package/docs/mensagens.md +114 -0
- package/docs/overlays.md +192 -0
- package/docs/status.md +71 -0
- package/package.json +20 -9
- package/dist/components/ui/data/comment-system.cjs +0 -2
- package/dist/components/ui/data/comment-system.cjs.map +0 -1
- package/dist/components/ui/data/comment-system.js +0 -492
- package/dist/components/ui/data/comment-system.js.map +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Onde a mensagem aparece
|
|
2
|
+
|
|
3
|
+
Três eixos decidem: **é resposta a uma ação** ou uma condição persistente? **Precisa de confirmação** ou só informa? **É sobre um campo** ou sobre a tela toda?
|
|
4
|
+
|
|
5
|
+
## Tabela de decisão
|
|
6
|
+
|
|
7
|
+
| Mensagem | Escolha quando | Evite quando |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Erro de campo | a mensagem é sobre um input específico: formato inválido, obrigatório, indisponível. Fica abaixo do campo, com `aria-invalid`. | o problema é do formulário inteiro (ex: "falha de conexão ao salvar") → `Alert` no topo do form. |
|
|
10
|
+
| Alert (inline) | uma condição que permanece enquanto o usuário olha a tela: aviso de plano expirando, banner de modo somente-leitura, resumo de erros de validação. | é reação efêmera a um clique que acabou de acontecer → `Toast`. |
|
|
11
|
+
| Toast | confirmação passageira de uma ação: "Salvo", "E-mail enviado", "Falha ao publicar". Some sozinho. Pode ter ação "Desfazer". | a informação é importante e não pode passar batida, ou exige decisão → `Alert` ou `AlertDialog`. |
|
|
12
|
+
| AlertDialog | a ação é destrutiva e você precisa que o usuário pare e confirme antes. Trava a tela. (Ver [Overlays](overlays.md).) | a ação é desfazível → execute e ofereça "Desfazer" num `Toast`. |
|
|
13
|
+
|
|
14
|
+
## Trilha rápida
|
|
15
|
+
|
|
16
|
+
1. **A mensagem é sobre um campo específico do formulário?** Sim: erro de campo abaixo do input + `aria-invalid` no input. Não: continue.
|
|
17
|
+
2. **Você precisa bloquear a tela até o usuário decidir?** Sim: `AlertDialog`. Não: continue.
|
|
18
|
+
3. **É reação imediata a um clique e pode desaparecer em segundos?** Sim: Toast (`toast.success` / `toast.error` / …). Se a ação for reversível, inclua "Desfazer". Não: continue.
|
|
19
|
+
4. **A condição continua verdadeira enquanto a tela estiver aberta?** Sim: `Alert` inline, ancorado onde a condição importa. Não: provavelmente não precisa de mensagem — reavalie.
|
|
20
|
+
|
|
21
|
+
## Exemplos
|
|
22
|
+
|
|
23
|
+
### Toast — confirmação efêmera
|
|
24
|
+
|
|
25
|
+
Dispare no callback da ação. Prefira `toast.success`/`toast.error` a texto neutro. Monte um único `<Toaster />` na raiz do app.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Button, toast } from "@iclips/ui";
|
|
29
|
+
|
|
30
|
+
<Button variant="outline" onClick={() => toast.success("Campanha salva")}>Salvar</Button>
|
|
31
|
+
<Button
|
|
32
|
+
variant="outline"
|
|
33
|
+
onClick={() =>
|
|
34
|
+
toast("Item removido", {
|
|
35
|
+
action: { label: "Desfazer", onClick: () => toast("Restaurado") },
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
>
|
|
39
|
+
Remover (com desfazer)
|
|
40
|
+
</Button>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Alert — condição persistente
|
|
44
|
+
|
|
45
|
+
Fica na tela. Título curto + o que fazer a respeito.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { Alert, AlertDescription, AlertTitle } from "@iclips/ui";
|
|
49
|
+
import { TriangleAlert } from "lucide-react";
|
|
50
|
+
|
|
51
|
+
<Alert variant="warning" className="max-w-sm">
|
|
52
|
+
<TriangleAlert />
|
|
53
|
+
<AlertTitle>Plano expira em 3 dias</AlertTitle>
|
|
54
|
+
<AlertDescription>Renove para não perder o acesso às automações.</AlertDescription>
|
|
55
|
+
</Alert>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Erro de campo
|
|
59
|
+
|
|
60
|
+
A mensagem vive com o input, não no topo da tela. Em formulário com react-hook-form, `FormMessage` faz isso sozinho (ver [Formulários](formularios.md)).
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
import { Input, Label } from "@iclips/ui";
|
|
64
|
+
|
|
65
|
+
<div className="grid w-full max-w-xs gap-1.5">
|
|
66
|
+
<Label htmlFor="email">E-mail</Label>
|
|
67
|
+
<Input id="email" defaultValue="ana@" aria-invalid aria-describedby="email-erro" />
|
|
68
|
+
<p id="email-erro" className="text-sm text-destructive-emphasis">
|
|
69
|
+
Informe um e-mail válido.
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Alert — resumo de validação
|
|
75
|
+
|
|
76
|
+
Erros do formulário inteiro sobem para um `Alert` no topo, com foco.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { Alert, AlertDescription, AlertTitle } from "@iclips/ui";
|
|
80
|
+
import { Info } from "lucide-react";
|
|
81
|
+
|
|
82
|
+
<Alert variant="destructive" className="max-w-sm" role="alert">
|
|
83
|
+
<Info />
|
|
84
|
+
<AlertTitle>Não foi possível salvar</AlertTitle>
|
|
85
|
+
<AlertDescription>Revise os 2 campos destacados abaixo.</AlertDescription>
|
|
86
|
+
</Alert>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Faça / evite
|
|
90
|
+
|
|
91
|
+
#### Faça
|
|
92
|
+
|
|
93
|
+
- Monte um único `<Toaster />` na raiz do app. Chame `toast(...)` de qualquer lugar.
|
|
94
|
+
- Toast de erro: diga o que falhou e o próximo passo ("Falha ao publicar — verifique a conexão").
|
|
95
|
+
- Ação reversível → execute já e ofereça "Desfazer" no toast, em vez de um `AlertDialog` antes.
|
|
96
|
+
- Toast de erro fica até o usuário fechar (padrão do `toast.error`); sucesso some em 4s, aviso em 6s, informação em 5s.
|
|
97
|
+
- Escuro = ação com prazo: `toast(msg, { action })` sem tipo vem em superfície invertida e dura 8s. Use só para Desfazer.
|
|
98
|
+
- Progresso: `toast.promise` — o toast de processando se transforma em sucesso ou erro, em vez de somar outro na pilha.
|
|
99
|
+
- Ação em lote gera um toast agregado ("12 jobs atualizados"), não doze.
|
|
100
|
+
- Erro de campo: ligue input e mensagem com `aria-describedby` + `aria-invalid`.
|
|
101
|
+
- Aviso: `text-warning-emphasis`, nunca `text-warning`, e sempre com ícone — amarelo sozinho não identifica um aviso.
|
|
102
|
+
|
|
103
|
+
#### Evite
|
|
104
|
+
|
|
105
|
+
- Toast para algo que o usuário precisa ler com atenção — ele some antes.
|
|
106
|
+
- Alert de sucesso permanente que nunca some e vira ruído visual.
|
|
107
|
+
- `AlertDialog` "Salvo com sucesso! [OK]" — é o caso clássico de Toast.
|
|
108
|
+
- Empilhar 5 toasts de uma vez; agrupe ou resuma.
|
|
109
|
+
|
|
110
|
+
**Toast:** `toast` (sonner) é a única API — `toast.success` / `toast.error` / `toast.promise`, `action`, `duration`, `toast.dismiss`. Monte um `<Toaster />` na raiz do app. Superfície neutra sempre, cor só no ícone; o único toast roxo é o de processando.
|
|
111
|
+
|
|
112
|
+
**Cor de status em texto:** texto e ícone sobre superfície neutra usam a camada `-emphasis` (`text-destructive-emphasis`, `text-success-emphasis`…), que passa AA nos dois temas. Nunca `text-destructive` cru para texto. Idioma: `bg-warning/10 text-warning-emphasis border-warning/20`.
|
|
113
|
+
|
|
114
|
+
**Acessibilidade:** o `Toaster` já anuncia via `aria-live`. Para um `Alert` que *aparece* em resposta a uma ação (resumo de validação), adicione `role="alert"` e mova o foco para ele.
|
package/docs/overlays.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Qual overlay usar
|
|
2
|
+
|
|
3
|
+
Todos flutuam acima da página, mas resolvem problemas diferentes. A pergunta que separa é: **o conteúdo exige uma ação do usuário** (então prende o foco) ou **só complementa** (então some ao clicar fora)?
|
|
4
|
+
|
|
5
|
+
## Tabela de decisão
|
|
6
|
+
|
|
7
|
+
| Overlay | Escolha quando | Evite quando |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Tooltip | rótulo curto para um ícone ou controle sem texto — aparece no hover/foco, some sozinho. | o conteúdo tem link, botão ou texto que o usuário precise ler com calma → `Popover`. |
|
|
10
|
+
| HoverCard | prévia rica e não-essencial de algo já visível (card de usuário, resumo de projeto). | a informação é necessária para a tarefa, ou o gatilho é em mobile (não há hover) → `Popover`. |
|
|
11
|
+
| Popover | um pequeno painel ancorado a um botão: filtro, seletor de cor, mini-formulário, menu de ações. | há mais de ~6 campos ou o fluxo tem etapas → `Dialog` ou `Sheet`. |
|
|
12
|
+
| Dialog | uma tarefa focada que interrompe o fluxo: editar um registro, confirmar com contexto, wizard curto. | é só um aviso de sim/não destrutivo → `AlertDialog`. Formulário longo → `Sheet`. |
|
|
13
|
+
| AlertDialog | confirmação de ação destrutiva ou irreversível (excluir, cancelar assinatura). Trava até o usuário decidir. | a ação não é perigosa e é desfazível → use um Toast com "Desfazer". |
|
|
14
|
+
| Sheet | painel lateral para formulário longo, detalhes ou configurações sem perder o contexto da lista atrás. | a tarefa é curta e centrada → `Dialog`. Navegação primária do app → não é overlay. |
|
|
15
|
+
| Drawer | o mesmo papel do Sheet, mas puxado de baixo — padrão em telas de toque/mobile. | desktop com espaço de sobra → `Sheet` lateral. |
|
|
16
|
+
|
|
17
|
+
## Trilha rápida
|
|
18
|
+
|
|
19
|
+
Responda de cima para baixo e pare na primeira que servir.
|
|
20
|
+
|
|
21
|
+
1. **A ação é destrutiva/irreversível e precisa de confirmação?** Sim: `AlertDialog`. Não: continue.
|
|
22
|
+
2. **É só um rótulo de texto para um ícone?** Sim: `Tooltip`. Não: continue.
|
|
23
|
+
3. **É uma prévia opcional de algo que já está na tela?** Sim: `HoverCard` (desktop) — mas garanta um caminho por clique no mobile. Não: continue.
|
|
24
|
+
4. **Cabe em um painelzinho de até ~6 campos ancorado ao gatilho?** Sim: `Popover`. Não: continue.
|
|
25
|
+
5. **O usuário precisa ver a lista/tela de trás enquanto preenche?** Sim: `Sheet` (desktop) ou `Drawer` (toque). Não: `Dialog`.
|
|
26
|
+
|
|
27
|
+
## Exemplos
|
|
28
|
+
|
|
29
|
+
### Tooltip — rótulo de ícone
|
|
30
|
+
|
|
31
|
+
Some ao tirar o mouse ou o foco. Sem conteúdo interativo dentro. Balão neutro invertido: é informação, não ação. Precisa de um `TooltipProvider` acima (uma vez, na raiz).
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { Button, Tooltip, TooltipContent, TooltipTrigger } from "@iclips/ui";
|
|
35
|
+
import { Archive } from "lucide-react";
|
|
36
|
+
|
|
37
|
+
<Tooltip>
|
|
38
|
+
<TooltipTrigger asChild>
|
|
39
|
+
<Button variant="outline" size="icon" aria-label="Arquivar">
|
|
40
|
+
<Archive className="size-4" />
|
|
41
|
+
</Button>
|
|
42
|
+
</TooltipTrigger>
|
|
43
|
+
<TooltipContent>Arquivar</TooltipContent>
|
|
44
|
+
</Tooltip>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Popover — mini-formulário
|
|
48
|
+
|
|
49
|
+
Fecha ao clicar fora. Bom para 1–6 campos.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { Button, Input, Label, Popover, PopoverContent, PopoverTrigger } from "@iclips/ui";
|
|
53
|
+
|
|
54
|
+
<Popover>
|
|
55
|
+
<PopoverTrigger asChild>
|
|
56
|
+
<Button variant="outline">Renomear</Button>
|
|
57
|
+
</PopoverTrigger>
|
|
58
|
+
<PopoverContent className="w-64 space-y-2">
|
|
59
|
+
<Label htmlFor="novo-nome">Novo nome</Label>
|
|
60
|
+
<Input id="novo-nome" defaultValue="Campanha Q3" />
|
|
61
|
+
</PopoverContent>
|
|
62
|
+
</Popover>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Dialog — tarefa focada
|
|
66
|
+
|
|
67
|
+
Prende o foco. Título + descrição + ações.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import {
|
|
71
|
+
Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle,
|
|
72
|
+
DialogTrigger, Input, Label,
|
|
73
|
+
} from "@iclips/ui";
|
|
74
|
+
|
|
75
|
+
<Dialog>
|
|
76
|
+
<DialogTrigger asChild>
|
|
77
|
+
<Button>Editar perfil</Button>
|
|
78
|
+
</DialogTrigger>
|
|
79
|
+
<DialogContent>
|
|
80
|
+
<DialogHeader>
|
|
81
|
+
<DialogTitle>Editar perfil</DialogTitle>
|
|
82
|
+
<DialogDescription>As alterações são salvas ao confirmar.</DialogDescription>
|
|
83
|
+
</DialogHeader>
|
|
84
|
+
<div className="grid gap-1.5 py-2">
|
|
85
|
+
<Label htmlFor="nome">Nome</Label>
|
|
86
|
+
<Input id="nome" defaultValue="Ana Souza" />
|
|
87
|
+
</div>
|
|
88
|
+
<DialogFooter>
|
|
89
|
+
<DialogClose asChild><Button variant="outline">Cancelar</Button></DialogClose>
|
|
90
|
+
<Button>Salvar</Button>
|
|
91
|
+
</DialogFooter>
|
|
92
|
+
</DialogContent>
|
|
93
|
+
</Dialog>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### AlertDialog — confirmação destrutiva
|
|
97
|
+
|
|
98
|
+
Sem clicar-fora-para-fechar. Uma escolha explícita.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import {
|
|
102
|
+
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription,
|
|
103
|
+
AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button,
|
|
104
|
+
} from "@iclips/ui";
|
|
105
|
+
|
|
106
|
+
<AlertDialog>
|
|
107
|
+
<AlertDialogTrigger asChild>
|
|
108
|
+
<Button variant="destructive">Excluir projeto</Button>
|
|
109
|
+
</AlertDialogTrigger>
|
|
110
|
+
<AlertDialogContent>
|
|
111
|
+
<AlertDialogHeader>
|
|
112
|
+
<AlertDialogTitle>Excluir “Campanha Q3”?</AlertDialogTitle>
|
|
113
|
+
<AlertDialogDescription>
|
|
114
|
+
Esta ação não pode ser desfeita. Todos os itens do projeto serão removidos.
|
|
115
|
+
</AlertDialogDescription>
|
|
116
|
+
</AlertDialogHeader>
|
|
117
|
+
<AlertDialogFooter>
|
|
118
|
+
<AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
119
|
+
<AlertDialogAction>Excluir</AlertDialogAction>
|
|
120
|
+
</AlertDialogFooter>
|
|
121
|
+
</AlertDialogContent>
|
|
122
|
+
</AlertDialog>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Sheet — formulário longo
|
|
126
|
+
|
|
127
|
+
A lista de trás continua visível como contexto.
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import {
|
|
131
|
+
Button, Input, Label, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle,
|
|
132
|
+
SheetTrigger,
|
|
133
|
+
} from "@iclips/ui";
|
|
134
|
+
|
|
135
|
+
<Sheet>
|
|
136
|
+
<SheetTrigger asChild>
|
|
137
|
+
<Button variant="outline">Nova tarefa</Button>
|
|
138
|
+
</SheetTrigger>
|
|
139
|
+
<SheetContent className="w-full sm:max-w-md">
|
|
140
|
+
<SheetHeader>
|
|
141
|
+
<SheetTitle>Nova tarefa</SheetTitle>
|
|
142
|
+
<SheetDescription>Preencha os campos e salve.</SheetDescription>
|
|
143
|
+
</SheetHeader>
|
|
144
|
+
<div className="grid gap-3 px-4">
|
|
145
|
+
<div className="grid gap-1.5"><Label htmlFor="titulo">Título</Label><Input id="titulo" /></div>
|
|
146
|
+
<div className="grid gap-1.5"><Label htmlFor="responsavel">Responsável</Label><Input id="responsavel" /></div>
|
|
147
|
+
<div className="grid gap-1.5"><Label htmlFor="prazo">Prazo</Label><Input id="prazo" type="date" /></div>
|
|
148
|
+
</div>
|
|
149
|
+
<SheetFooter>
|
|
150
|
+
<Button>Criar tarefa</Button>
|
|
151
|
+
<SheetClose asChild><Button variant="outline">Cancelar</Button></SheetClose>
|
|
152
|
+
</SheetFooter>
|
|
153
|
+
</SheetContent>
|
|
154
|
+
</Sheet>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### HoverCard — prévia opcional
|
|
158
|
+
|
|
159
|
+
Só complementa. Nunca coloque aqui algo que a tarefa exige.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from "@iclips/ui";
|
|
163
|
+
|
|
164
|
+
<HoverCard>
|
|
165
|
+
<HoverCardTrigger asChild>
|
|
166
|
+
<Button variant="link">@ana.souza</Button>
|
|
167
|
+
</HoverCardTrigger>
|
|
168
|
+
<HoverCardContent className="text-sm">
|
|
169
|
+
<p className="font-medium">Ana Souza</p>
|
|
170
|
+
<p className="text-muted-foreground">Designer · entrou em 2023</p>
|
|
171
|
+
</HoverCardContent>
|
|
172
|
+
</HoverCard>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Faça / evite
|
|
176
|
+
|
|
177
|
+
#### Faça
|
|
178
|
+
|
|
179
|
+
- Dê a todo `Dialog`/`Sheet`/`AlertDialog` um título — é o rótulo que o leitor de tela anuncia.
|
|
180
|
+
- Reserve o `AlertDialog` para o que dói desfazer; para o resto, aja e ofereça "Desfazer" num Toast.
|
|
181
|
+
- No mobile, troque `Sheet` lateral por `Drawer` e `HoverCard` por `Popover` (toque não tem hover).
|
|
182
|
+
- Empilhe no máximo um overlay. Se precisar de outro, provavelmente o fluxo deveria ser uma página.
|
|
183
|
+
|
|
184
|
+
#### Evite
|
|
185
|
+
|
|
186
|
+
- Colocar um formulário de 15 campos num `Popover`.
|
|
187
|
+
- Usar `Tooltip` para conteúdo que tem link ou botão — o usuário não consegue alcançar.
|
|
188
|
+
- Pintar o `Tooltip` com a cor da marca (`bg-primary`) — overlay informativo não se veste de ação.
|
|
189
|
+
- Abrir um `Dialog` dentro de outro `Dialog`.
|
|
190
|
+
- Trocar toast de sucesso por `Dialog` "OK" — interrompe sem motivo.
|
|
191
|
+
|
|
192
|
+
**Acessibilidade:** `Dialog`, `AlertDialog`, `Sheet` e `Popover` vêm do Radix — prendem o foco, fecham no Esc e devolvem o foco ao gatilho de graça. O que você precisa garantir: um `Title` em todo painel modal, e `aria-label` em gatilhos que são só ícone.
|
package/docs/status.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Status
|
|
2
|
+
|
|
3
|
+
Status é **estado**: feedback, nunca decoração. Vermelho, verde, amarelo e azul só aparecem para dizer algo sobre um item. Status nunca é roxo (roxo é ação) e sempre vem com ícone ou rótulo — cor sozinha não identifica nada.
|
|
4
|
+
|
|
5
|
+
A receita é sempre suave: `bg-<status>/10 text-<status>-emphasis border-<status>/20`. Os componentes já a aplicam — use o componente em vez de montar as classes.
|
|
6
|
+
|
|
7
|
+
| Status | `variant` | Quando |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Sucesso | `success` | aprovado, pago, concluído, ativo |
|
|
10
|
+
| Aviso | `warning` | pendente, aguardando, vence em breve |
|
|
11
|
+
| Informação | `info` | em análise, rascunho enviado, novidade |
|
|
12
|
+
| Erro | `destructive` | recusado, vencido, falhou, cancelado |
|
|
13
|
+
|
|
14
|
+
## Status de um item
|
|
15
|
+
|
|
16
|
+
Use `Badge` com a variante do status e um ícone ou rótulo claro.
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
import { Badge } from "@iclips/ui";
|
|
20
|
+
import { Check, CircleAlert, Clock, Info } from "lucide-react";
|
|
21
|
+
|
|
22
|
+
<Badge variant="success"><Check /> Aprovado</Badge>
|
|
23
|
+
<Badge variant="warning"><Clock /> Aguardando</Badge>
|
|
24
|
+
<Badge variant="info"><Info /> Em análise</Badge>
|
|
25
|
+
<Badge variant="destructive"><CircleAlert /> Recusado</Badge>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Status que vem da API vira um mapa, não um `if` por status:
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { Badge } from "@iclips/ui";
|
|
32
|
+
|
|
33
|
+
const STATUS = {
|
|
34
|
+
pago: { variant: "success", rotulo: "Pago" },
|
|
35
|
+
pendente: { variant: "warning", rotulo: "Pendente" },
|
|
36
|
+
vencido: { variant: "destructive", rotulo: "Vencido" },
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
function StatusParcela({ status }: { status: keyof typeof STATUS }) {
|
|
40
|
+
const { variant, rotulo } = STATUS[status];
|
|
41
|
+
return <Badge variant={variant}>{rotulo}</Badge>;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Status neutro (sem juízo de bom ou ruim, como "Arquivado" ou "Inativo") usa `variant="muted"` ou `variant="outline"`, não uma cor de estado.
|
|
46
|
+
|
|
47
|
+
## Valor positivo ou negativo
|
|
48
|
+
|
|
49
|
+
Número que sobe ou desce (variação, saldo, meta) usa o texto `-emphasis` do status, com sinal ou ícone — nunca só a cor.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { TrendingDown, TrendingUp } from "lucide-react";
|
|
53
|
+
|
|
54
|
+
<span className="inline-flex items-center gap-1 text-success-emphasis">
|
|
55
|
+
<TrendingUp className="size-4" aria-hidden="true" /> +12,4%
|
|
56
|
+
</span>
|
|
57
|
+
<span className="inline-flex items-center gap-1 text-destructive-emphasis">
|
|
58
|
+
<TrendingDown className="size-4" aria-hidden="true" /> −3,1%
|
|
59
|
+
</span>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Condição que vale para a tela toda
|
|
63
|
+
|
|
64
|
+
Se o status é da página e não de um item (plano expirando, modo somente-leitura), é um `Alert` — ver [Mensagens](mensagens.md).
|
|
65
|
+
|
|
66
|
+
## Regras
|
|
67
|
+
|
|
68
|
+
- Status **nunca é roxo** e nunca é `text-<status>` cru em texto: use `text-<status>-emphasis`, que passa contraste AA nos dois temas.
|
|
69
|
+
- Não pinte **fundo de seção** ou card com cor de estado — o badge carrega o estado, não o card.
|
|
70
|
+
- Nunca fundo sólido de estado (`bg-success` sem `/10`): a cor cheia fica só no ícone e na borda de campo inválido.
|
|
71
|
+
- Mais regras de cor em [Linguagem visual](../DESIGN.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iclips/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Design system da iClips — componentes React sobre Radix UI + Tailwind v4, paleta #7F26BF",
|
|
5
5
|
"author": "iclipsbr",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"LICENSE",
|
|
50
50
|
"MIGRATION-v2.md",
|
|
51
51
|
"MIGRATION-v3.md",
|
|
52
|
+
"DESIGN.md",
|
|
53
|
+
"docs",
|
|
52
54
|
"scripts/check-colors.mjs"
|
|
53
55
|
],
|
|
54
56
|
"peerDependencies": {
|
|
@@ -92,14 +94,23 @@
|
|
|
92
94
|
"@radix-ui/react-toggle": "^1.1.2",
|
|
93
95
|
"@radix-ui/react-toggle-group": "^1.1.2",
|
|
94
96
|
"@radix-ui/react-tooltip": "^1.1.8",
|
|
95
|
-
"@tiptap/extension-
|
|
96
|
-
"@tiptap/extension-
|
|
97
|
-
"@tiptap/extension-
|
|
98
|
-
"@tiptap/extension-
|
|
99
|
-
"@tiptap/extension-
|
|
100
|
-
"@tiptap/extension-
|
|
101
|
-
"@tiptap/
|
|
102
|
-
"@tiptap/
|
|
97
|
+
"@tiptap/extension-details": "^3.31.3",
|
|
98
|
+
"@tiptap/extension-drag-handle-react": "^3.31.3",
|
|
99
|
+
"@tiptap/extension-file-handler": "^3.31.3",
|
|
100
|
+
"@tiptap/extension-highlight": "^3.31.3",
|
|
101
|
+
"@tiptap/extension-image": "^3.31.3",
|
|
102
|
+
"@tiptap/extension-link": "^3.31.3",
|
|
103
|
+
"@tiptap/extension-mention": "^3.31.3",
|
|
104
|
+
"@tiptap/extension-placeholder": "^3.31.3",
|
|
105
|
+
"@tiptap/extension-table": "^3.31.3",
|
|
106
|
+
"@tiptap/extension-task-item": "^3.31.3",
|
|
107
|
+
"@tiptap/extension-task-list": "^3.31.3",
|
|
108
|
+
"@tiptap/extension-text-align": "^3.31.3",
|
|
109
|
+
"@tiptap/extension-typography": "^3.31.3",
|
|
110
|
+
"@tiptap/extension-underline": "^3.31.3",
|
|
111
|
+
"@tiptap/extensions": "^3.31.3",
|
|
112
|
+
"@tiptap/react": "^3.31.3",
|
|
113
|
+
"@tiptap/starter-kit": "^3.31.3",
|
|
103
114
|
"class-variance-authority": "^0.7.1",
|
|
104
115
|
"clsx": "^2.1.1",
|
|
105
116
|
"cmdk": "^1.1.1",
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),E=require("react"),I=require("date-fns"),z=require("date-fns/locale"),u=require("../utils.cjs"),N=require("../primitives/avatar.cjs"),c=require("../primitives/button.cjs"),T=require("../primitives/badge.cjs"),g=require("../overlays/tooltip.cjs"),S=require("../primitives/separator.cjs"),a=require("lucide-react"),w=require("@tiptap/react"),M=require("@tiptap/starter-kit"),O=require("@tiptap/extension-underline"),U=require("@tiptap/extension-link"),_=require("@tiptap/extension-image"),H=require("@tiptap/extension-mention"),D=require("tippy.js"),F=require("./mention-list.cjs"),y=require("emoji-picker-react"),f=require("../overlays/popover.cjs");function K(t){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const o in t)if(o!=="default"){const r=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(l,o,r.get?r:{enumerable:!0,get:()=>t[o]})}}return l.default=t,Object.freeze(l)}const v=K(E),W=({role:t})=>{const l={internal:{label:"Equipe iClips",className:"bg-primary/10 text-primary border-primary/20"},client:{label:"Cliente",className:"bg-info/10 text-info-emphasis border-info/20"},freelancer:{label:"Freelancer",className:"bg-warning/10 text-warning-emphasis border-warning/20"}},{label:o,className:r}=l[t];return e.jsx(T.Badge,{variant:"outline",className:u.cn("text-[10px] h-5 px-1.5 font-medium border-0",r),children:o})},X=({editor:t,visible:l})=>!t||!l?null:e.jsxs("div",{className:"flex items-center gap-1 border-b p-1 bg-muted/20 animate-in slide-in-from-bottom-2 duration-200",children:[e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bold")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBold().run(),title:"Negrito (Ctrl+B)","aria-label":"Negrito",children:e.jsx(a.Bold,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("italic")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleItalic().run(),title:"Itálico (Ctrl+I)","aria-label":"Itálico",children:e.jsx(a.Italic,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("underline")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleUnderline().run(),title:"Sublinhado (Ctrl+U)","aria-label":"Sublinhado",children:e.jsx(a.Underline,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bulletList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBulletList().run(),"aria-label":"Lista com marcadores",children:e.jsx(a.List,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("orderedList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleOrderedList().run(),"aria-label":"Lista numerada",children:e.jsx(a.ListOrdered,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("link")&&"bg-muted text-primary"),"aria-label":"Inserir link",onClick:()=>{const o=t.getAttributes("link").href,r=window.prompt("URL",o);if(r!==null){if(r===""){t.chain().focus().extendMarkRange("link").unsetLink().run();return}t.chain().focus().extendMarkRange("link").setLink({href:r}).run()}},children:e.jsx(a.Link,{className:"h-3.5 w-3.5"})})]}),G=({currentUser:t,onSubmit:l,users:o=[]})=>{const[r,b]=v.useState(!1),[p,i]=v.useState(!1),[h,m]=v.useState([]),[R,C]=v.useState(!0),k=v.useRef(null),A=v.useMemo(()=>[M.configure({link:!1,underline:!1}),O,U.configure({openOnClick:!1}),_,H.configure({HTMLAttributes:{class:"bg-secondary text-secondary-foreground rounded-sm px-1 py-0.5 font-medium decoration-clone"},suggestion:{items:({query:s})=>o.filter(n=>n.name.toLowerCase().startsWith(s.toLowerCase())).slice(0,5),render:()=>{let s=null,n=null;return{onStart:d=>{s=new w.ReactRenderer(F.MentionList,{props:d,editor:d.editor}),d.clientRect&&(n=D("body",{getReferenceClientRect:d.clientRect,appendTo:()=>document.body,content:s.element,showOnCreate:!0,interactive:!0,trigger:"manual",placement:"bottom-start"}))},onUpdate(d){s==null||s.updateProps(d),d.clientRect&&(n==null||n[0].setProps({getReferenceClientRect:d.clientRect}))},onKeyDown(d){var j,q;return d.event.key==="Escape"?(n==null||n[0].hide(),!0):!!((q=(j=s==null?void 0:s.ref)==null?void 0:j.onKeyDown)!=null&&q.call(j,d))},onExit(){n==null||n[0].destroy(),s==null||s.destroy()}}}}})],[o]),x=w.useEditor({extensions:A,content:"",onUpdate:({editor:s})=>{C(s.isEmpty)},editorProps:{attributes:{class:"prose prose-sm dark:prose-invert max-w-none focus:outline-none min-h-[80px] px-3 py-2 text-sm"}}}),B=s=>{if(s.target.files&&s.target.files[0]){const n=s.target.files[0],d=URL.createObjectURL(n),j={id:Math.random().toString(36).substr(2,9),name:n.name,url:d,type:n.type.startsWith("image/")?"image":"file"};m([...h,j]),s.target.value=""}},L=s=>{m(h.filter(n=>n.id!==s))},P=()=>{!x||x.isEmpty&&h.length===0||(l({content:x.getHTML(),isRevisionRequest:r,attachments:h}),x.commands.clearContent(),m([]),b(!1),C(!0))};return e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-1 border",children:[e.jsx(N.AvatarImage,{src:t.avatar}),e.jsx(N.AvatarFallback,{children:t.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:u.cn("flex-1 rounded-xl border bg-background shadow-sm overflow-hidden transition-colors",r&&"border-destructive/40 ring-1 ring-destructive/20"),children:[e.jsx(X,{editor:x,visible:p}),e.jsx(w.EditorContent,{editor:x}),h.length>0&&e.jsx("div",{className:"px-3 pb-2 flex flex-wrap gap-2",children:h.map(s=>e.jsxs("div",{className:"relative group",children:[s.type==="image"?e.jsx("div",{className:"h-16 w-16 rounded-md border overflow-hidden relative",children:e.jsx("img",{src:s.url,alt:s.name,className:"h-full w-full object-cover"})}):e.jsx("div",{className:"h-16 w-16 rounded-md border bg-muted flex items-center justify-center",children:e.jsx(a.Paperclip,{className:"h-6 w-6 text-muted-foreground"})}),e.jsx("button",{onClick:()=>L(s.id),className:"absolute -top-1.5 -right-1.5 border bg-background text-destructive-emphasis hover:bg-destructive/10 rounded-full p-0.5 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity shadow-sm",children:e.jsx(a.X,{className:"h-3 w-3"})})]},s.id))}),e.jsxs("div",{className:"flex items-center justify-between p-2 bg-muted/10 border-t",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-8 w-8 transition-colors",p?"bg-muted text-foreground":"text-muted-foreground hover:text-foreground"),onClick:()=>i(!p),"aria-label":"Opções de formatação",children:e.jsx(a.Type,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Opções de formatação"})]}),e.jsx("div",{className:"w-px h-4 bg-border mx-1"}),e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground","aria-label":"Inserir emoji",children:e.jsx(a.Smile,{className:"h-4 w-4"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",side:"top",children:e.jsx(y,{onEmojiClick:s=>x==null?void 0:x.chain().focus().insertContent(s.emoji).run(),width:300,height:350})})]}),e.jsx("input",{type:"file",className:"hidden",ref:k,onChange:B,accept:"image/*,.pdf,.doc,.docx"}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground",onClick:()=>{var s;return(s=k.current)==null?void 0:s.click()},"aria-label":"Anexar arquivo",children:e.jsx(a.Paperclip,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Anexar arquivo"})]}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsxs(c.Button,{variant:"ghost",size:"sm",className:u.cn("h-8 gap-2 px-2 transition-colors",r?"bg-destructive/10 text-destructive-emphasis hover:bg-destructive/20":"text-muted-foreground hover:text-foreground"),onClick:()=>b(!r),children:[e.jsx(a.AlertCircle,{className:"h-4 w-4"}),e.jsx("span",{className:"text-xs font-medium",children:"Solicitar Refação"})]})}),e.jsx(g.TooltipContent,{children:"Marca este comentário como um pedido de alteração"})]})]}),e.jsxs(c.Button,{size:"sm",onClick:P,disabled:R&&h.length===0,children:["Enviar ",e.jsx(a.Send,{className:"h-3.5 w-3.5 ml-2"})]})]})]})]})},J=({comment:t,currentUserId:l,onDelete:o,onReact:r})=>{var p;const b=t.isRevisionRequest;return e.jsxs("div",{className:u.cn("group flex gap-3 p-4 rounded-lg transition-colors",b?"bg-destructive/5 border border-destructive/20":"hover:bg-muted/20"),children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-0.5 border",children:[e.jsx(N.AvatarImage,{src:t.user.avatar}),e.jsx(N.AvatarFallback,{children:t.user.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"font-semibold text-sm text-foreground",children:t.user.name}),e.jsx(W,{role:t.user.role}),e.jsx("span",{className:"text-xs text-foreground/65",children:I.format(t.createdAt,"dd MMM 'às' HH:mm",{locale:z.ptBR})})]}),e.jsxs("div",{className:"flex items-center gap-2 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity has-[[data-state=open]]:opacity-100",children:[e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-foreground","aria-label":"Reagir com emoji",children:e.jsx(a.Smile,{className:"h-3.5 w-3.5","aria-hidden":"true"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"end",children:e.jsx(y,{onEmojiClick:i=>r==null?void 0:r(t.id,i.emoji),width:300,height:350})})]}),o&&e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-destructive-emphasis",onClick:()=>o(t.id),"aria-label":"Excluir comentário",children:e.jsx(a.Trash2,{className:"h-3.5 w-3.5","aria-hidden":"true"})})]})]}),b&&e.jsxs("div",{className:"inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-destructive/10 text-destructive-emphasis text-xs font-medium mb-2",children:[e.jsx(a.AlertCircle,{className:"h-3.5 w-3.5"}),"Solicitação de Refação"]}),e.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none text-sm text-foreground/90 leading-relaxed break-words [&_p]:mb-2 [&_p:last-child]:mb-0",dangerouslySetInnerHTML:{__html:t.content}}),t.attachments&&t.attachments.length>0&&e.jsx("div",{className:"mt-3 flex flex-wrap gap-2",children:t.attachments.map(i=>e.jsx("div",{className:"group/att relative rounded-lg border overflow-hidden bg-background hover:shadow-sm transition-all cursor-pointer",onClick:()=>window.open(i.url,"_blank"),children:i.type==="image"?e.jsxs("div",{className:"h-32 aspect-[4/3]",children:[e.jsx("img",{src:i.url,alt:i.name,className:"h-full w-full object-cover"}),e.jsx("div",{className:"absolute inset-0 bg-foreground/0 group-hover/att:bg-foreground/10 transition-colors"})]}):e.jsxs("div",{className:"h-12 px-3 flex items-center gap-2 bg-muted/30",children:[e.jsx(a.Paperclip,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"text-xs font-medium truncate max-w-[150px]",children:i.name})]})},i.id))}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2 items-center",children:[(p=t.reactions)==null?void 0:p.map(i=>e.jsxs("button",{onClick:()=>r==null?void 0:r(t.id,i.emoji),className:u.cn("inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium border transition-colors",i.users.includes(l)?"bg-secondary border-secondary text-secondary-foreground":"bg-muted/30 border-transparent hover:bg-muted/50 text-muted-foreground"),children:[e.jsx("span",{children:i.emoji}),e.jsx("span",{children:i.users.length})]},i.emoji)),r&&e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted/50","aria-label":"Adicionar reação",children:e.jsx(a.Plus,{className:"h-3.5 w-3.5"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",children:e.jsx(y,{onEmojiClick:i=>r(t.id,i.emoji),width:300,height:350})})]})]})]})]})},Q=({comments:t,currentUser:l,users:o=[],onAddComment:r,onDeleteComment:b,onReact:p,className:i})=>{const h=m=>{r({content:m.content,user:l,isRevisionRequest:m.isRevisionRequest,attachments:m.attachments})};return e.jsxs("div",{className:u.cn("flex flex-col h-full",i),children:[e.jsxs("div",{className:"px-4 py-3 border-b flex items-center justify-between bg-card",children:[e.jsx("h3",{className:"font-semibold text-base",children:"Comentários da Atividade"}),e.jsx(T.Badge,{variant:"secondary",className:"bg-muted text-muted-foreground hover:bg-muted",children:t.length})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-2 bg-muted/5",children:t.length===0?e.jsxs("div",{className:"h-full flex flex-col items-center justify-center text-muted-foreground min-h-[200px]",children:[e.jsx("div",{className:"bg-muted rounded-full p-3 mb-3",children:e.jsx(a.MoreHorizontal,{className:"h-6 w-6","aria-hidden":"true"})}),e.jsx("p",{className:"text-sm font-medium text-foreground/70",children:"Nenhum comentário ainda"}),e.jsx("p",{className:"text-xs",children:"Inicie a conversa sobre esta tarefa"})]}):t.map(m=>e.jsx(J,{comment:m,currentUserId:l.id,onDelete:b,onReact:p},m.id))}),e.jsx("div",{className:"p-4 border-t bg-card",children:e.jsx(G,{currentUser:l,onSubmit:h,users:o})})]})};exports.CommentSystem=Q;
|
|
2
|
-
//# sourceMappingURL=comment-system.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"comment-system.cjs","sources":["../../../../src/components/ui/data/comment-system.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { format } from \"date-fns\";\nimport { ptBR } from \"date-fns/locale\";\nimport { cn } from \"../utils\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"../primitives/avatar\";\nimport { Button } from \"../primitives/button\";\nimport { Badge } from \"../primitives/badge\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"../overlays/tooltip\";\nimport { Separator } from \"../primitives/separator\";\nimport { \n Bold, \n Italic, \n Underline as UnderlineIcon, \n List, \n ListOrdered, \n Link as LinkIcon,\n Send,\n Paperclip,\n AlertCircle,\n MoreHorizontal,\n Trash2,\n Type,\n X,\n Smile,\n Plus\n} from \"lucide-react\";\n\n// Tiptap Imports\nimport { useEditor, EditorContent, Editor, ReactRenderer } from '@tiptap/react';\nimport type { Instance as TippyInstance } from 'tippy.js';\nimport StarterKit from '@tiptap/starter-kit';\nimport Underline from '@tiptap/extension-underline';\nimport Link from '@tiptap/extension-link';\nimport TiptapImage from '@tiptap/extension-image';\nimport Mention from '@tiptap/extension-mention';\nimport tippy from 'tippy.js';\nimport { MentionList } from './mention-list';\nimport Picker from 'emoji-picker-react';\nimport { Popover, PopoverContent, PopoverTrigger } from \"../overlays/popover\";\n\n// ==========================================\n// TIPOS\n// ==========================================\n\nexport type UserRole = 'internal' | 'client' | 'freelancer';\n\nexport interface CommentUser {\n id: string;\n name: string;\n avatar?: string;\n role: UserRole;\n}\n\nexport interface CommentReaction {\n emoji: string;\n users: string[]; // Array de IDs de usuários que reagiram\n}\n\nexport interface CommentAttachment {\n id: string;\n url: string;\n name: string;\n type: 'image' | 'file';\n}\n\nexport interface Comment {\n id: string;\n content: string; // Conteúdo HTML\n user: CommentUser;\n createdAt: Date;\n isRevisionRequest?: boolean;\n attachments?: CommentAttachment[];\n reactions?: CommentReaction[];\n}\n\nexport interface CommentSystemProps {\n comments: Comment[];\n currentUser: CommentUser;\n users?: CommentUser[]; // Lista de usuários mencionáveis\n onAddComment: (comment: Omit<Comment, \"id\" | \"createdAt\">) => void;\n onDeleteComment?: (id: string) => void;\n onReact?: (commentId: string, emoji: string) => void;\n className?: string;\n}\n\n// ==========================================\n// COMPONENTES AUXILIARES\n// ==========================================\n\nconst RoleBadge = ({ role }: { role: UserRole }) => {\n const config = {\n internal: { label: \"Equipe iClips\", className: \"bg-primary/10 text-primary border-primary/20\" },\n client: { label: \"Cliente\", className: \"bg-info/10 text-info-emphasis border-info/20\" },\n freelancer: { label: \"Freelancer\", className: \"bg-warning/10 text-warning-emphasis border-warning/20\" },\n };\n\n const { label, className } = config[role];\n\n return (\n <Badge variant=\"outline\" className={cn(\"text-[10px] h-5 px-1.5 font-medium border-0\", className)}>\n {label}\n </Badge>\n );\n};\n\n// ==========================================\n// EDITOR TOOLBAR\n// ==========================================\n\nconst EditorToolbar = ({ editor, visible }: { editor: Editor | null, visible: boolean }) => {\n if (!editor) return null;\n\n if (!visible) return null;\n\n return (\n <div className=\"flex items-center gap-1 border-b p-1 bg-muted/20 animate-in slide-in-from-bottom-2 duration-200\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('bold') && \"bg-muted text-primary\")}\n onClick={() => editor.chain().focus().toggleBold().run()}\n title=\"Negrito (Ctrl+B)\"\n aria-label=\"Negrito\"\n >\n <Bold className=\"h-3.5 w-3.5\" />\n </Button>\n \n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('italic') && \"bg-muted text-primary\")}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n title=\"Itálico (Ctrl+I)\"\n aria-label=\"Itálico\"\n >\n <Italic className=\"h-3.5 w-3.5\" />\n </Button>\n\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('underline') && \"bg-muted text-primary\")}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n title=\"Sublinhado (Ctrl+U)\"\n aria-label=\"Sublinhado\"\n >\n <UnderlineIcon className=\"h-3.5 w-3.5\" />\n </Button>\n\n <Separator orientation=\"vertical\" className=\"h-4 mx-1\" />\n\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('bulletList') && \"bg-muted text-primary\")}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n aria-label=\"Lista com marcadores\"\n >\n <List className=\"h-3.5 w-3.5\" />\n </Button>\n\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('orderedList') && \"bg-muted text-primary\")}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n aria-label=\"Lista numerada\"\n >\n <ListOrdered className=\"h-3.5 w-3.5\" />\n </Button>\n\n <Separator orientation=\"vertical\" className=\"h-4 mx-1\" />\n\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", editor.isActive('link') && \"bg-muted text-primary\")}\n aria-label=\"Inserir link\"\n onClick={() => {\n const previousUrl = editor.getAttributes('link').href;\n const url = window.prompt('URL', previousUrl);\n if (url === null) return;\n if (url === '') {\n editor.chain().focus().extendMarkRange('link').unsetLink().run();\n return;\n }\n editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run();\n }}\n >\n <LinkIcon className=\"h-3.5 w-3.5\" />\n </Button>\n </div>\n );\n};\n\n// ==========================================\n// COMMENT EDITOR AREA\n// ==========================================\n\nconst CommentEditor = ({ \n currentUser, \n onSubmit,\n users = []\n}: { \n currentUser: CommentUser;\n onSubmit: (data: { content: string; isRevisionRequest: boolean; attachments: CommentAttachment[] }) => void;\n users?: CommentUser[];\n}) => {\n const [isRevision, setIsRevision] = React.useState(false);\n const [showToolbar, setShowToolbar] = React.useState(false);\n const [attachments, setAttachments] = React.useState<CommentAttachment[]>([]);\n const [isEditorEmpty, setIsEditorEmpty] = React.useState(true);\n const fileInputRef = React.useRef<HTMLInputElement>(null);\n\n const extensions = React.useMemo(() => [\n StarterKit.configure({\n // @ts-ignore\n link: false,\n // @ts-ignore\n underline: false,\n }),\n Underline,\n Link.configure({\n openOnClick: false,\n }),\n TiptapImage,\n Mention.configure({\n HTMLAttributes: {\n class: 'bg-secondary text-secondary-foreground rounded-sm px-1 py-0.5 font-medium decoration-clone',\n },\n suggestion: {\n items: ({ query }: { query: string }) => {\n return users\n .filter(item => item.name.toLowerCase().startsWith(query.toLowerCase()))\n .slice(0, 5);\n },\n render: () => {\n // Formato mínimo dos props do ciclo de vida do suggestion do tiptap\n // (o tipo completo `SuggestionProps` vive em `@tiptap/suggestion`, dep\n // transitiva sem entrada direta no package.json).\n type MentionRenderProps = {\n editor: Editor;\n clientRect?: (() => DOMRect | null) | null;\n event?: KeyboardEvent;\n };\n let component: ReactRenderer | null = null;\n let popup: TippyInstance[] | null = null;\n\n return {\n onStart: (props: MentionRenderProps) => {\n component = new ReactRenderer(MentionList, {\n props,\n editor: props.editor,\n });\n\n if (!props.clientRect) {\n return;\n }\n\n popup = tippy('body', {\n // tiptap tipa como `() => DOMRect | null`; tippy exige não-nulo.\n getReferenceClientRect: props.clientRect as () => DOMRect,\n appendTo: () => document.body,\n content: component.element,\n showOnCreate: true,\n interactive: true,\n trigger: 'manual',\n placement: 'bottom-start',\n });\n },\n onUpdate(props: MentionRenderProps) {\n component?.updateProps(props);\n\n if (!props.clientRect) {\n return;\n }\n\n popup?.[0].setProps({\n getReferenceClientRect: props.clientRect as () => DOMRect,\n });\n },\n onKeyDown(props: { event: KeyboardEvent }) {\n if (props.event.key === 'Escape') {\n popup?.[0].hide();\n\n return true;\n }\n\n return Boolean(\n (component?.ref as { onKeyDown?: (p: { event: KeyboardEvent }) => boolean } | undefined)?.onKeyDown?.(props),\n );\n },\n onExit() {\n popup?.[0].destroy();\n component?.destroy();\n },\n };\n },\n },\n }),\n ], [users]);\n\n const editor = useEditor({\n extensions,\n content: '',\n onUpdate: ({ editor }) => {\n setIsEditorEmpty(editor.isEmpty);\n },\n editorProps: {\n attributes: {\n class: 'prose prose-sm dark:prose-invert max-w-none focus:outline-none min-h-[80px] px-3 py-2 text-sm',\n },\n },\n });\n\n const handleAttach = (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files[0]) {\n const file = e.target.files[0];\n const url = URL.createObjectURL(file);\n \n const newAttachment: CommentAttachment = {\n id: Math.random().toString(36).substr(2, 9),\n name: file.name,\n url,\n type: file.type.startsWith('image/') ? 'image' : 'file'\n };\n \n setAttachments([...attachments, newAttachment]);\n // Reset input\n e.target.value = '';\n }\n };\n\n const removeAttachment = (id: string) => {\n setAttachments(attachments.filter(a => a.id !== id));\n };\n\n const handleSubmit = () => {\n if (!editor || (editor.isEmpty && attachments.length === 0)) return;\n \n onSubmit({\n content: editor.getHTML(),\n isRevisionRequest: isRevision,\n attachments\n });\n\n editor.commands.clearContent();\n setAttachments([]);\n setIsRevision(false);\n // Manually reset empty state since clearContent triggers update but we want to be sure\n setIsEditorEmpty(true);\n };\n\n return (\n <div className=\"flex gap-3 items-start\">\n <Avatar className=\"h-8 w-8 mt-1 border\">\n <AvatarImage src={currentUser.avatar} />\n <AvatarFallback>{currentUser.name.substring(0, 2).toUpperCase()}</AvatarFallback>\n </Avatar>\n\n <div className={cn(\n \"flex-1 rounded-xl border bg-background shadow-sm overflow-hidden transition-colors\",\n isRevision && \"border-destructive/40 ring-1 ring-destructive/20\"\n )}>\n <EditorToolbar editor={editor} visible={showToolbar} />\n \n <EditorContent editor={editor} />\n\n {/* Attachments Preview Area */}\n {attachments.length > 0 && (\n <div className=\"px-3 pb-2 flex flex-wrap gap-2\">\n {attachments.map(att => (\n <div key={att.id} className=\"relative group\">\n {att.type === 'image' ? (\n <div className=\"h-16 w-16 rounded-md border overflow-hidden relative\">\n <img src={att.url} alt={att.name} className=\"h-full w-full object-cover\" />\n </div>\n ) : (\n <div className=\"h-16 w-16 rounded-md border bg-muted flex items-center justify-center\">\n <Paperclip className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n )}\n <button \n onClick={() => removeAttachment(att.id)}\n className=\"absolute -top-1.5 -right-1.5 border bg-background text-destructive-emphasis hover:bg-destructive/10 rounded-full p-0.5 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity shadow-sm\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"flex items-center justify-between p-2 bg-muted/10 border-t\">\n <div className=\"flex items-center gap-2\">\n <Tooltip>\n <TooltipTrigger asChild>\n <Button \n variant=\"ghost\" \n size=\"icon\" \n className={cn(\n \"h-8 w-8 transition-colors\",\n showToolbar ? \"bg-muted text-foreground\" : \"text-muted-foreground hover:text-foreground\"\n )}\n onClick={() => setShowToolbar(!showToolbar)}\n aria-label=\"Opções de formatação\"\n >\n <Type className=\"h-4 w-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Opções de formatação</TooltipContent>\n </Tooltip>\n\n <div className=\"w-px h-4 bg-border mx-1\" />\n\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground hover:text-foreground\"\n aria-label=\"Inserir emoji\"\n >\n <Smile className=\"h-4 w-4\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0 border-none\" align=\"start\" side=\"top\">\n <Picker \n onEmojiClick={(emojiData) => editor?.chain().focus().insertContent(emojiData.emoji).run()}\n width={300}\n height={350}\n />\n </PopoverContent>\n </Popover>\n\n <input \n type=\"file\" \n className=\"hidden\" \n ref={fileInputRef}\n onChange={handleAttach}\n accept=\"image/*,.pdf,.doc,.docx\"\n />\n <Tooltip>\n <TooltipTrigger asChild>\n <Button \n variant=\"ghost\" \n size=\"icon\" \n className=\"h-8 w-8 text-muted-foreground hover:text-foreground\"\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Anexar arquivo\"\n >\n <Paperclip className=\"h-4 w-4\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Anexar arquivo</TooltipContent>\n </Tooltip>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button \n variant=\"ghost\" \n size=\"sm\" \n className={cn(\n \"h-8 gap-2 px-2 transition-colors\",\n isRevision\n ? \"bg-destructive/10 text-destructive-emphasis hover:bg-destructive/20\"\n : \"text-muted-foreground hover:text-foreground\"\n )}\n onClick={() => setIsRevision(!isRevision)}\n >\n <AlertCircle className=\"h-4 w-4\" />\n <span className=\"text-xs font-medium\">Solicitar Refação</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>Marca este comentário como um pedido de alteração</TooltipContent>\n </Tooltip>\n </div>\n\n <Button\n size=\"sm\"\n onClick={handleSubmit}\n disabled={isEditorEmpty && attachments.length === 0}\n >\n Enviar <Send className=\"h-3.5 w-3.5 ml-2\" />\n </Button>\n </div>\n </div>\n </div>\n );\n};\n\n// ==========================================\n// SINGLE COMMENT ITEM\n// ==========================================\n\nconst CommentItem = ({ \n comment, \n currentUserId,\n onDelete, \n onReact \n}: { \n comment: Comment; \n currentUserId: string;\n onDelete?: (id: string) => void;\n onReact?: (commentId: string, emoji: string) => void;\n}) => {\n const isRevision = comment.isRevisionRequest;\n\n return (\n <div className={cn(\n \"group flex gap-3 p-4 rounded-lg transition-colors\",\n isRevision ? \"bg-destructive/5 border border-destructive/20\" : \"hover:bg-muted/20\"\n )}>\n <Avatar className=\"h-8 w-8 mt-0.5 border\">\n <AvatarImage src={comment.user.avatar} />\n <AvatarFallback>{comment.user.name.substring(0, 2).toUpperCase()}</AvatarFallback>\n </Avatar>\n\n <div className=\"flex-1 min-w-0\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-1.5\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"font-semibold text-sm text-foreground\">{comment.user.name}</span>\n <RoleBadge role={comment.user.role} />\n <span className=\"text-xs text-foreground/65\">\n {format(comment.createdAt, \"dd MMM 'às' HH:mm\", { locale: ptBR })}\n </span>\n </div>\n \n <div className=\"flex items-center gap-2 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity has-[[data-state=open]]:opacity-100\">\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-foreground\"\n aria-label=\"Reagir com emoji\"\n >\n <Smile className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0 border-none\" align=\"end\">\n <Picker \n onEmojiClick={(emojiData) => onReact?.(comment.id, emojiData.emoji)}\n width={300}\n height={350}\n />\n </PopoverContent>\n </Popover>\n\n {onDelete && (\n <Button \n variant=\"ghost\" \n size=\"icon\" \n className=\"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-destructive-emphasis\"\n onClick={() => onDelete(comment.id)}\n aria-label=\"Excluir comentário\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </Button>\n )}\n </div>\n </div>\n\n {/* Revision Badge */}\n {isRevision && (\n <div className=\"inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-destructive/10 text-destructive-emphasis text-xs font-medium mb-2\">\n <AlertCircle className=\"h-3.5 w-3.5\" />\n Solicitação de Refação\n </div>\n )}\n\n {/* Content */}\n <div \n className=\"prose prose-sm dark:prose-invert max-w-none text-sm text-foreground/90 leading-relaxed break-words [&_p]:mb-2 [&_p:last-child]:mb-0\"\n dangerouslySetInnerHTML={{ __html: comment.content }}\n />\n\n {/* Attachments Display */}\n {comment.attachments && comment.attachments.length > 0 && (\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {comment.attachments.map(att => (\n <div \n key={att.id} \n className=\"group/att relative rounded-lg border overflow-hidden bg-background hover:shadow-sm transition-all cursor-pointer\"\n onClick={() => window.open(att.url, '_blank')}\n >\n {att.type === 'image' ? (\n <div className=\"h-32 aspect-[4/3]\">\n <img src={att.url} alt={att.name} className=\"h-full w-full object-cover\" />\n <div className=\"absolute inset-0 bg-foreground/0 group-hover/att:bg-foreground/10 transition-colors\" />\n </div>\n ) : (\n <div className=\"h-12 px-3 flex items-center gap-2 bg-muted/30\">\n <Paperclip className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-xs font-medium truncate max-w-[150px]\">{att.name}</span>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n\n {/* Reactions */}\n <div className=\"mt-3 flex flex-wrap gap-2 items-center\">\n {comment.reactions?.map((reaction) => (\n <button\n key={reaction.emoji}\n onClick={() => onReact?.(comment.id, reaction.emoji)}\n className={cn(\n \"inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium border transition-colors\",\n reaction.users.includes(currentUserId)\n ? \"bg-secondary border-secondary text-secondary-foreground\"\n : \"bg-muted/30 border-transparent hover:bg-muted/50 text-muted-foreground\"\n )}\n >\n <span>{reaction.emoji}</span>\n <span>{reaction.users.length}</span>\n </button>\n ))}\n \n {onReact && (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-6 w-6 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted/50\"\n aria-label=\"Adicionar reação\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0 border-none\" align=\"start\">\n <Picker \n onEmojiClick={(emojiData) => onReact(comment.id, emojiData.emoji)}\n width={300}\n height={350}\n />\n </PopoverContent>\n </Popover>\n )}\n </div>\n </div>\n </div>\n );\n};\n\n// ==========================================\n// MAIN COMPONENT\n// ==========================================\n\nexport const CommentSystem = ({ \n comments, \n currentUser, \n users = [],\n onAddComment, \n onDeleteComment,\n onReact,\n className \n}: CommentSystemProps) => {\n \n const handleSubmit = (data: { content: string; isRevisionRequest: boolean; attachments: CommentAttachment[] }) => {\n onAddComment({\n content: data.content,\n user: currentUser,\n isRevisionRequest: data.isRevisionRequest,\n attachments: data.attachments\n });\n };\n\n return (\n <div className={cn(\"flex flex-col h-full\", className)}>\n {/* Header */}\n <div className=\"px-4 py-3 border-b flex items-center justify-between bg-card\">\n <h3 className=\"font-semibold text-base\">Comentários da Atividade</h3>\n <Badge variant=\"secondary\" className=\"bg-muted text-muted-foreground hover:bg-muted\">\n {comments.length}\n </Badge>\n </div>\n\n {/* Scrollable List */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-2 bg-muted/5\">\n {comments.length === 0 ? (\n <div className=\"h-full flex flex-col items-center justify-center text-muted-foreground min-h-[200px]\">\n <div className=\"bg-muted rounded-full p-3 mb-3\">\n <MoreHorizontal className=\"h-6 w-6\" aria-hidden=\"true\" />\n </div>\n <p className=\"text-sm font-medium text-foreground/70\">Nenhum comentário ainda</p>\n <p className=\"text-xs\">Inicie a conversa sobre esta tarefa</p>\n </div>\n ) : (\n comments.map((comment) => (\n <CommentItem \n key={comment.id} \n comment={comment} \n currentUserId={currentUser.id}\n onDelete={onDeleteComment} \n onReact={onReact}\n />\n ))\n )}\n </div>\n\n {/* Input Area */}\n <div className=\"p-4 border-t bg-card\">\n <CommentEditor \n currentUser={currentUser} \n onSubmit={handleSubmit}\n users={users}\n />\n </div>\n </div>\n );\n};\n\nexport default CommentSystem;"],"names":["RoleBadge","role","config","label","className","jsx","Badge","cn","EditorToolbar","editor","visible","jsxs","Button","Bold","Italic","UnderlineIcon","Separator","List","ListOrdered","previousUrl","url","LinkIcon","CommentEditor","currentUser","onSubmit","users","isRevision","setIsRevision","React","showToolbar","setShowToolbar","attachments","setAttachments","isEditorEmpty","setIsEditorEmpty","fileInputRef","extensions","StarterKit","Underline","Link","TiptapImage","Mention","query","item","component","popup","props","ReactRenderer","MentionList","tippy","_b","_a","useEditor","handleAttach","e","file","newAttachment","removeAttachment","id","a","handleSubmit","Avatar","AvatarImage","AvatarFallback","EditorContent","att","Paperclip","X","Tooltip","TooltipTrigger","Type","TooltipContent","Popover","PopoverTrigger","Smile","PopoverContent","Picker","emojiData","AlertCircle","Send","CommentItem","comment","currentUserId","onDelete","onReact","format","ptBR","Trash2","reaction","Plus","CommentSystem","comments","onAddComment","onDeleteComment","data","MoreHorizontal"],"mappings":"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"}
|