@softize/opus 18.4.0 → 19.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/docs/adr/0020-icon-tiles-are-explicitly-composed.md +46 -0
  3. package/docs/adr/0021-content-carries-structure-and-semantic-element.md +65 -0
  4. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +293 -0
  5. package/package.json +1 -1
  6. package/src/ui/components/patterns/content-header.tsx +169 -50
  7. package/src/ui/components/patterns/heading-level.ts +16 -0
  8. package/src/ui/components/patterns/list.tsx +76 -27
  9. package/src/ui/components/patterns/pane.tsx +294 -0
  10. package/src/ui/components/patterns/presentation.tsx +29 -17
  11. package/src/ui/components/patterns/sidebar.tsx +13 -72
  12. package/src/ui/components/patterns/split.tsx +8 -35
  13. package/src/ui/components/patterns/surface-anatomy.ts +219 -0
  14. package/src/ui/components/patterns/surface-header.tsx +51 -6
  15. package/src/ui/components/patterns/surface-media.tsx +69 -0
  16. package/src/ui/components/primitives/card.tsx +91 -29
  17. package/src/ui/components/primitives/command.tsx +12 -4
  18. package/src/ui/components/primitives/detail.tsx +10 -13
  19. package/src/ui/components/primitives/dialog.tsx +138 -35
  20. package/src/ui/components/primitives/drawer.tsx +122 -23
  21. package/src/ui/components/primitives/empty.tsx +2 -1
  22. package/src/ui/components/primitives/field.tsx +38 -16
  23. package/src/ui/components/primitives/icon-tile.tsx +91 -0
  24. package/src/ui/components/primitives/item.tsx +2 -1
  25. package/src/ui/components/primitives/metric-card.tsx +98 -39
  26. package/src/ui/components/primitives/table.tsx +1 -1
  27. package/src/ui/docs/content/action-list.md +61 -52
  28. package/src/ui/docs/content/card.md +221 -30
  29. package/src/ui/docs/content/content.md +90 -8
  30. package/src/ui/docs/content/dialog.md +115 -7
  31. package/src/ui/docs/content/drawer.md +136 -9
  32. package/src/ui/docs/content/field.md +78 -30
  33. package/src/ui/docs/content/icon-tile.md +47 -0
  34. package/src/ui/docs/content/metric-card.md +57 -18
  35. package/src/ui/docs/content/page.md +8 -0
  36. package/src/ui/docs/content/pane.md +250 -0
  37. package/src/ui/docs/content/sidebar.md +22 -43
  38. package/src/ui/docs/content/skeleton.md +7 -3
  39. package/src/ui/docs/content/split.md +16 -21
  40. package/src/ui/docs/registry.tsx +14 -2
  41. package/src/ui/meta.ts +17 -5
  42. package/src/ui/react.tsx +31 -0
@@ -0,0 +1,250 @@
1
+ ## A coluna encaixada em um layout
2
+
3
+ Use `Pane` para a coluna ou faixa que ocupa uma das posições de um [`Split`](/ui/split): a
4
+ navegação à esquerda, o inspetor à direita, a lista que abre o detalhe ao lado. O `Split` decide a
5
+ relação espacial e o redimensionamento; o `Pane` decide o que existe dentro da área recebida.
6
+
7
+ No formato mais simples, o pane é uma caixa com recuo e o conteúdo é seu.
8
+
9
+ ```tsx preview
10
+ render(
11
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
12
+ <Split>
13
+ <Pane initialSize="16rem" className="bg-muted/30">
14
+ <p className="text-sm text-muted-foreground">Navegação.</p>
15
+ </Pane>
16
+ <Pane grow>
17
+ <p className="text-sm text-muted-foreground">Conteúdo.</p>
18
+ </Pane>
19
+ </Split>
20
+ </div>,
21
+ );
22
+ ```
23
+
24
+ ## Cabeçalho, corpo e rodapé
25
+
26
+ Quando a área precisa de regiões fixas e de um corpo que rola, componha os slots. `PaneHeader`
27
+ nomeia a região, `PaneBody` ocupa o espaço restante e concentra a rolagem vertical, e `PaneFooter`
28
+ mantém à vista o que não pode sair da tela.
29
+
30
+ ```tsx preview
31
+ render(
32
+ <div className="h-72 overflow-hidden rounded-lg border border-border">
33
+ <Split>
34
+ <Pane initialSize="18rem">
35
+ <PaneHeader>
36
+ <PaneMedia>
37
+ <MessagesSquare />
38
+ </PaneMedia>
39
+ <PaneTitle>Conversas</PaneTitle>
40
+ <PaneActions>
41
+ <Button variant="ghost" size="icon-sm" aria-label="Nova conversa">
42
+ <SquarePen />
43
+ </Button>
44
+ </PaneActions>
45
+ </PaneHeader>
46
+ <PaneBody className="space-y-1">
47
+ <p className="text-sm">Revisão do contrato</p>
48
+ <p className="text-sm text-muted-foreground">Importação de clientes</p>
49
+ <p className="text-sm text-muted-foreground">Fila de execuções</p>
50
+ </PaneBody>
51
+ <PaneFooter>
52
+ <Button variant="ghost" size="sm">Ver arquivadas</Button>
53
+ </PaneFooter>
54
+ </Pane>
55
+ <Pane grow>
56
+ <p className="text-sm text-muted-foreground">Conversa selecionada.</p>
57
+ </Pane>
58
+ </Split>
59
+ </div>,
60
+ );
61
+ ```
62
+
63
+ O cabeçalho reconhece quatro slots — `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions` —
64
+ e monta a linha sozinho: mídia, título e descrição de um lado, ações no extremo oposto. A ordem em
65
+ que os slots são escritos não altera a posição, e um nó que não seja um desses slots interrompe a
66
+ renderização com um erro. Conteúdo livre vai para `PaneBody`.
67
+
68
+ Título e descrição truncam quando a coluna encolhe, em vez de quebrar a altura da faixa.
69
+
70
+ ## Escolher entre seccionado e contínuo
71
+
72
+ `layout` declara a anatomia da superfície inteira e fica no root; os slots leem esse valor e não o
73
+ recebem. Cada valor é um pacote de recuo, ritmo vertical e divisores.
74
+
75
+ | Aspecto | `sectioned` (padrão) | `continuous` |
76
+ | --- | --- | --- |
77
+ | Divisores | entre as regiões vizinhas | nenhum |
78
+ | Faixa do cabeçalho | altura fixa de `3.75rem` | acompanha o conteúdo |
79
+ | Ritmo vertical | regiões simétricas | o cabeçalho se aproxima do corpo |
80
+
81
+ O padrão `sectioned` é o pane de operação: a faixa do cabeçalho tem a mesma altura da barra do
82
+ [`PageShell`](/ui/page), então uma navegação lateral e o chrome da página se alinham na mesma
83
+ linha. Use `continuous` quando o pane for um bloco de leitura contínuo, sem fronteiras a marcar.
84
+
85
+ Os divisores são derivados da vizinhança: cada região desenha a linha quando há uma região do outro
86
+ lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um rodapé produz uma
87
+ linha só. A tela não escreve `border-b` nem `border-t`.
88
+
89
+ ## Recuo do pane e recuo das regiões
90
+
91
+ `inset` é o respiro interno do pane sem slots: `md` por padrão, com `none`, `sm` e `lg`
92
+ disponíveis. Quando `PaneHeader`, `PaneBody` ou `PaneFooter` são filhos **diretos** do pane, o recuo
93
+ passa a ser de cada região e o `inset` vira `none` sozinho — é o que permite ao divisor alcançar a
94
+ borda da coluna.
95
+
96
+ O ajuste enxerga só o primeiro nível. Se os slots vierem dentro de outro componente — é o caso da
97
+ `Sidebar`, que traz a superfície e o colapso —, o pane não sabe que eles existem: declare
98
+ `inset="none"` você mesmo, ou a coluna ganha um recuo que empurra o divisor para dentro.
99
+
100
+ ## A matriz de layouts e slots
101
+
102
+ As regiões são independentes e o recuo se ajusta a cada combinação, nos dois layouts.
103
+
104
+ ```tsx preview col
105
+ render(
106
+ <div className="grid gap-4 md:grid-cols-2">
107
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
108
+ <Pane className="h-full">
109
+ <PaneHeader>
110
+ <PaneTitle>Seccionado</PaneTitle>
111
+ <PaneActions>
112
+ <Button variant="ghost" size="icon-sm" aria-label="Mais ações">
113
+ <Ellipsis />
114
+ </Button>
115
+ </PaneActions>
116
+ </PaneHeader>
117
+ <PaneBody>
118
+ <p className="text-sm text-muted-foreground">Cabeçalho, corpo e rodapé.</p>
119
+ </PaneBody>
120
+ <PaneFooter>
121
+ <Button variant="ghost" size="sm">Configurações</Button>
122
+ </PaneFooter>
123
+ </Pane>
124
+ </div>
125
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
126
+ <Pane layout="continuous" className="h-full">
127
+ <PaneHeader>
128
+ <PaneMedia>
129
+ <ChartNoAxesColumn />
130
+ </PaneMedia>
131
+ <PaneTitle>Contínuo com mídia</PaneTitle>
132
+ <PaneDescription>O espaço separa as regiões.</PaneDescription>
133
+ </PaneHeader>
134
+ <PaneBody>
135
+ <p className="text-sm text-muted-foreground">Sem rodapé e sem divisores.</p>
136
+ </PaneBody>
137
+ </Pane>
138
+ </div>
139
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
140
+ <Pane className="h-full">
141
+ <PaneBody>
142
+ <p className="text-sm text-muted-foreground">Só corpo: nenhuma fronteira a marcar.</p>
143
+ </PaneBody>
144
+ </Pane>
145
+ </div>
146
+ <div className="h-56 overflow-hidden rounded-lg border border-border">
147
+ <Pane className="h-full">
148
+ <PaneHeader>
149
+ <PaneMedia context="primary">
150
+ <Folder />
151
+ </PaneMedia>
152
+ <PaneTitle>Sem corpo</PaneTitle>
153
+ </PaneHeader>
154
+ <PaneFooter>
155
+ <Button variant="ghost" size="sm">Ação persistente</Button>
156
+ </PaneFooter>
157
+ </Pane>
158
+ </div>
159
+ </div>,
160
+ );
161
+ ```
162
+
163
+ ## Onde termina o Pane
164
+
165
+ Três componentes dividem a responsabilidade de uma coluna, e trocá-los de papel é a origem mais
166
+ comum de layout duplicado:
167
+
168
+ | Componente | Responsabilidade |
169
+ | --- | --- |
170
+ | [`Split`](/ui/split) | Relação espacial entre as áreas e, com `resizable`, o arraste da fronteira. |
171
+ | `Pane` | Tamanho recebido, recuo e as regiões internas: cabeçalho, corpo e rodapé. |
172
+ | [`Sidebar`](/ui/sidebar) | Superfície da navegação lateral e o estado de colapso dos destinos. |
173
+
174
+ Tamanho inicial, limites e persistência do layout são declarados no `Pane` e operados pelo `Split`
175
+ — a [página do Split](/ui/split) trata deles. Uma `Sidebar` é composta dentro de um `Pane` e usa os
176
+ mesmos slots para o próprio cabeçalho e rodapé; ela não tem regiões próprias.
177
+
178
+ ## Hierarquia do título
179
+
180
+ `PaneTitle` é um heading. `level` escolhe a hierarquia do documento, de `1` a `6`, com `2` como
181
+ padrão. O tamanho visual vem da família e do `layout`, não do nível, então corrigir a estrutura do
182
+ documento não muda a aparência da faixa.
183
+
184
+ ## Propriedades de Pane
185
+
186
+ | Propriedade | Tipo | Padrão | Descrição |
187
+ |---|---|---|---|
188
+ | `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. |
189
+ | `inset` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'`, ou `'none'` com slots diretos | Respiro interno do pane sem slots; slots dentro de outro componente exigem declarar. |
190
+ | `id` | `string` | | Identidade estável usada pelo layout redimensionável e persistido. |
191
+ | `initialSize` | `number \| string` | | Tamanho inicial; número é porcentagem, string aceita `%`, `rem`, `em`, `vh`, `vw` e `px`. |
192
+ | `minSize` / `maxSize` | `number \| string` | | Limites aplicados pelo `Split` quando ele é redimensionável. |
193
+ | `grow` | `boolean` | `false` | Ocupa o espaço remanescente no layout simples. |
194
+ | `className` | `string` | | Ajusta a coluna. |
195
+ | `children` | `ReactNode` | | Conteúdo direto ou os slots estruturais. |
196
+
197
+ ## Propriedades de PaneHeader
198
+
199
+ | Propriedade | Tipo | Padrão | Descrição |
200
+ |---|---|---|---|
201
+ | `children` | `ReactNode` | | Somente `PaneMedia`, `PaneTitle`, `PaneDescription` e `PaneActions`, no máximo um de cada. |
202
+ | `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
203
+
204
+ ## Propriedades de PaneMedia
205
+
206
+ | Propriedade | Tipo | Padrão | Descrição |
207
+ |---|---|---|---|
208
+ | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'xs'` | Tamanho da moldura e do glifo na escala compartilhada. |
209
+ | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
210
+ | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
211
+ | `className` | `string` | | Ajusta a moldura sem substituir a escala. |
212
+ | `asChild` | `boolean` | `false` | Cede o elemento para uma marca com visual próprio — um orbe, um logotipo — que ocupa o slot sem ganhar a moldura por cima. |
213
+
214
+ ## Propriedades de PaneTitle
215
+
216
+ | Propriedade | Tipo | Padrão | Descrição |
217
+ |---|---|---|---|
218
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
219
+ | `className` | `string` | | Ajusta o título, que trunca quando não cabe. |
220
+
221
+ ## Propriedades de PaneDescription
222
+
223
+ | Propriedade | Tipo | Padrão | Descrição |
224
+ |---|---|---|---|
225
+ | `children` | `ReactNode` | | Frase curta de apoio sob o título; trunca quando não cabe. |
226
+ | `className` | `string` | | Ajusta a descrição. |
227
+
228
+ ## Propriedades de PaneActions
229
+
230
+ | Propriedade | Tipo | Padrão | Descrição |
231
+ |---|---|---|---|
232
+ | `children` | `ReactNode` | | Controles no extremo oposto ao título. |
233
+ | `className` | `string` | | Ajusta o agrupamento das ações. |
234
+
235
+ ## Propriedades de PaneBody
236
+
237
+ | Propriedade | Tipo | Padrão | Descrição |
238
+ |---|---|---|---|
239
+ | `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
240
+ | `className` | `string` | | Ajusta a região flexível e rolável. |
241
+
242
+ `PaneContent` continua exportado como alias depreciado de `PaneBody` e sai em uma próxima versão
243
+ maior. Código novo usa `PaneBody`.
244
+
245
+ ## Propriedades de PaneFooter
246
+
247
+ | Propriedade | Tipo | Padrão | Descrição |
248
+ |---|---|---|---|
249
+ | `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
250
+ | `className` | `string` | | Ajusta a faixa fixa sobre o recuo do layout. |
@@ -1,8 +1,8 @@
1
1
  ## Navegação dentro de um layout
2
2
 
3
3
  Use `Sidebar` para apresentar navegação global ou contextual em uma coluna lateral. O componente
4
- organiza a superfície e compartilha o estado de colapso; posição e largura pertencem a um `Pane`
5
- dentro de `Split`.
4
+ organiza a superfície e compartilha o estado de colapso; posição e largura pertencem a um
5
+ [`Pane`](/ui/pane) dentro de [`Split`](/ui/split).
6
6
 
7
7
  O exemplo canônico mantém a largura inicial em `rem`, deixa a navegação rolar e preserva o
8
8
  cabeçalho e o rodapé nas extremidades.
@@ -13,8 +13,8 @@ render(
13
13
  <Split>
14
14
  <Pane initialSize="16rem" inset="none">
15
15
  <Sidebar className="w-full">
16
- <PaneHeader className="px-4 py-3 text-sm font-semibold">
17
- Projeto
16
+ <PaneHeader>
17
+ <PaneTitle>Projeto</PaneTitle>
18
18
  </PaneHeader>
19
19
  <PaneBody>
20
20
  <SidebarNav
@@ -50,22 +50,17 @@ divisória ao lado do handle.
50
50
 
51
51
  ## Anatomia e responsabilidades
52
52
 
53
- Cada parte possui uma responsabilidade única. Essa separação permite reutilizar os mesmos slots em
54
- uma sidebar fixa, redimensionável ou recolhida.
53
+ A `Sidebar` não tem regiões próprias: o cabeçalho, o corpo rolável e o rodapé são os slots do
54
+ `Pane` que a hospeda, com a anatomia e as propriedades descritas na
55
+ [página da família](/ui/pane).
55
56
 
56
57
  | Parte | Responsabilidade |
57
58
  |---|---|
58
- | `Split` | Define a relação espacial e, opcionalmente, o redimensionamento. |
59
- | `Pane` | Define largura, limites e espaçamento interno da coluna. |
59
+ | [`Split`](/ui/split) | Define a relação espacial e, opcionalmente, o redimensionamento. |
60
+ | [`Pane`](/ui/pane) | Define largura e limites da coluna e estrutura suas regiões. |
60
61
  | `Sidebar` | Desenha a superfície lateral e fornece o estado de colapso. |
61
- | `PaneHeader` | Mantém identidade, contexto ou ações no topo. |
62
- | `PaneBody` | Ocupa o espaço restante e concentra a rolagem vertical. |
63
- | `PaneFooter` | Mantém ações persistentes no rodapé. |
64
62
  | `SidebarNav` | Apresenta e controla os destinos de navegação. |
65
63
 
66
- `PaneContent` continua exportado como alias depreciado de `PaneBody`. Código novo usa
67
- `PaneBody`.
68
-
69
64
  ## Escolher a navegação
70
65
 
71
66
  Use `SidebarNav` para grupos planos de destinos. Ele recebe dados e produz `SidebarItem` com a mesma
@@ -151,15 +146,17 @@ const [collapsed, setCollapsed] = useState(false)
151
146
  render(
152
147
  <div className="flex h-64 overflow-hidden rounded-lg border border-border">
153
148
  <Sidebar collapsed={collapsed}>
154
- <PaneHeader className="flex h-12 items-center justify-center">
155
- <Button
156
- variant="ghost"
157
- size="icon-sm"
158
- aria-label={collapsed ? 'Expandir navegação' : 'Recolher navegação'}
159
- onClick={() => setCollapsed((current) => !current)}
160
- >
161
- <PanelLeftClose className={collapsed ? 'rotate-180' : ''} />
162
- </Button>
149
+ <PaneHeader>
150
+ <PaneActions>
151
+ <Button
152
+ variant="ghost"
153
+ size="icon-sm"
154
+ aria-label={collapsed ? 'Expandir navegação' : 'Recolher navegação'}
155
+ onClick={() => setCollapsed((current) => !current)}
156
+ >
157
+ <PanelLeftClose className={collapsed ? 'rotate-180' : ''} />
158
+ </Button>
159
+ </PaneActions>
163
160
  </PaneHeader>
164
161
  <PaneBody>
165
162
  <SidebarNav
@@ -241,26 +238,8 @@ O driver de drag-and-drop continua externo e fornece seus atributos por `dragPro
241
238
  | `className` | `string` | | Ajusta a raiz. A largura externa deve continuar pertencendo ao `Pane`. |
242
239
  | `children` | `ReactNode` | | Cabeçalho, corpo, rodapé ou uma navegação que gerencie a própria estrutura. |
243
240
 
244
- ## Propriedades de PaneHeader
245
-
246
- | Propriedade | Tipo | Padrão | Descrição |
247
- |---|---|---|---|
248
- | `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
249
- | `children` | `ReactNode` | | Identidade, contexto ou ações apresentadas no topo. |
250
-
251
- ## Propriedades de PaneBody
252
-
253
- | Propriedade | Tipo | Padrão | Descrição |
254
- |---|---|---|---|
255
- | `className` | `string` | | Ajusta a região flexível e rolável. |
256
- | `children` | `ReactNode` | | Conteúdo que ocupa o espaço entre cabeçalho e rodapé. |
257
-
258
- ## Propriedades de PaneFooter
259
-
260
- | Propriedade | Tipo | Padrão | Descrição |
261
- |---|---|---|---|
262
- | `className` | `string` | | Ajusta a faixa fixa e seu espaçamento. |
263
- | `children` | `ReactNode` | | Navegação ou ações que precisam permanecer acessíveis no rodapé. |
241
+ `PaneHeader`, `PaneBody` e `PaneFooter` são os slots da família `Pane`; a referência deles está na
242
+ [página do Pane](/ui/pane).
264
243
 
265
244
  ## Propriedades de SidebarNav
266
245
 
@@ -15,15 +15,19 @@ avatares e barras com dimensões próximas às linhas de texto esperadas.
15
15
 
16
16
  ## Card em carregamento
17
17
 
18
- O esqueleto reproduz o layout final do card — título, descrição, conteúdo e ações — para a tela não pular quando os dados chegarem.
18
+ O esqueleto reproduz o layout final do card — título, conteúdo e ações — para a tela não pular
19
+ quando os dados chegarem. O título entra pelo slot do cabeçalho com `asChild`, porque o
20
+ `CardHeader` só aceita os próprios slots.
19
21
 
20
22
  ```tsx preview col
21
23
  <Card>
22
24
  <CardHeader>
23
- <Skeleton className="h-5 w-40" />
24
- <Skeleton className="h-4 w-56" />
25
+ <CardTitle asChild>
26
+ <Skeleton className="h-5 w-40" />
27
+ </CardTitle>
25
28
  </CardHeader>
26
29
  <CardBody className="space-y-2">
30
+ <Skeleton className="h-4 w-56" />
27
31
  <Skeleton className="h-4 w-full" />
28
32
  <Skeleton className="h-4 w-3/4" />
29
33
  </CardBody>
@@ -8,10 +8,10 @@ layout permanece estático.
8
8
  render(
9
9
  <div className="h-72 overflow-hidden rounded-lg border border-border">
10
10
  <Split resizable>
11
- <Pane initialSize={30} minSize={20} inset="none" className="bg-muted/30">
12
- <div className="p-4 text-sm">Navegação</div>
11
+ <Pane initialSize={30} minSize={20} className="bg-muted/30">
12
+ <p className="text-sm">Navegação</p>
13
13
  </Pane>
14
- <Pane grow inset="lg">
14
+ <Pane grow>
15
15
  <p className="text-sm text-muted-foreground">
16
16
  Conteúdo. Arraste a divisória.
17
17
  </p>
@@ -21,26 +21,31 @@ render(
21
21
  );
22
22
  ```
23
23
 
24
- ## Espaçamento interno
24
+ O `Split` responde pela relação espacial e pelo arraste. O que existe dentro de cada área — o
25
+ cabeçalho, o corpo rolável, o rodapé, o recuo e a escolha entre regiões seccionadas e contínuas —
26
+ pertence ao [`Pane`](/ui/pane) e está documentado na página da família.
25
27
 
26
- `Pane` usa `inset="md"` por padrão. Use `none` para navegação e conteúdo que já controla o próprio
27
- espaçamento; `sm` e `lg` atendem outras densidades.
28
+ ## Tamanho de cada pane
28
29
 
29
- ## Largura inicial
30
-
31
- Quando uma área precisa continuar legível em telas largas, use uma unidade absoluta para o tamanho inicial ou mínimo. Números continuam representando porcentagens; strings aceitam `%`, `rem`, `em`, `vh`, `vw` e `px`. Prefira `rem` para acompanhar a escala tipográfica configurada pela aplicação.
30
+ Cada `Pane` declara o próprio tamanho e o `Split` o aplica. Números representam porcentagens;
31
+ strings aceitam `%`, `rem`, `em`, `vh`, `vw` e `px`. Quando uma área precisa continuar legível em
32
+ telas largas, use uma unidade absoluta no tamanho inicial ou mínimo, de preferência `rem`, para
33
+ acompanhar a escala tipográfica configurada pela aplicação. `grow` entrega o espaço remanescente.
32
34
 
33
35
  ```tsx
34
36
  <Split resizable>
35
- <Pane grow inset="none">
37
+ <Pane grow>
36
38
  <Main />
37
39
  </Pane>
38
- <Pane initialSize="28rem" minSize="20rem" inset="none">
40
+ <Pane initialSize="28rem" minSize="20rem">
39
41
  <Inspector />
40
42
  </Pane>
41
43
  </Split>
42
44
  ```
43
45
 
46
+ `minSize` e `maxSize` valem enquanto o `Split` é redimensionável; sem `resizable` eles não limitam
47
+ nada.
48
+
44
49
  ## Persistência
45
50
 
46
51
  Um layout pode ser restaurado sem acoplar `Split` a um mecanismo específico de armazenamento. Dê um `id` estável a cada `Pane`, leia o valor salvo para formar `defaultLayout` e persista apenas mudanças concluídas pela pessoa.
@@ -135,16 +140,6 @@ render(<Example />);
135
140
  | `defaultLayout` | `Record<string, number>` | | Layout percentual restaurado, indexado pelos ids dos panes. |
136
141
  | `onLayoutChanged` | `(layout, { isUserInteraction }) => void` | | Chamado ao concluir uma mudança de layout; persista onde fizer sentido. |
137
142
 
138
- ## Propriedades de Pane
139
-
140
- | Propriedade | Tipo | Padrão | Descrição |
141
- | --------------------- | -------------------------------- | ------- | ----------------------------------------------------------------------------------------- |
142
- | `id` | `string` | | Identidade estável usada pelo layout redimensionável e persistido. |
143
- | `initialSize` | `number \| string` | | Tamanho inicial; número é porcentagem, string aceita `%`, `rem`, `em`, `vh`, `vw` e `px`. |
144
- | `minSize` / `maxSize` | `number \| string` | | Limites quando o split é redimensionável. |
145
- | `grow` | `boolean` | `false` | Ocupa o espaço remanescente no layout simples. |
146
- | `inset` | `'none' \| 'sm' \| 'md' \| 'lg'` | `'md'` | Respiro interno; `none` para chrome, navegação ou conteúdo com inset próprio. |
147
-
148
143
  ## Propriedades de SurfaceAssistant
149
144
 
150
145
  | Propriedade | Tipo | Padrão | Descrição |
@@ -68,6 +68,7 @@ import chatMd from "./content/chat.md?raw";
68
68
  import checkboxMd from "./content/checkbox.md?raw";
69
69
  import collapsibleMd from "./content/collapsible.md?raw";
70
70
  import iconPickerMd from "./content/icon-picker.md?raw";
71
+ import iconTileMd from "./content/icon-tile.md?raw";
71
72
  import commandMd from "./content/command.md?raw";
72
73
  import composerMd from "./content/composer.md?raw";
73
74
  import copyableMd from "./content/copyable.md?raw";
@@ -124,6 +125,7 @@ import actionSearchDialogMd from "./content/action-list-dialog.md?raw";
124
125
  import pageMd from "./content/page.md?raw";
125
126
  import presentationMd from "./content/presentation.md?raw";
126
127
  import contentMd from "./content/content.md?raw";
128
+ import paneMd from "./content/pane.md?raw";
127
129
  import sidebarMd from "./content/sidebar.md?raw";
128
130
  import splitMd from "./content/split.md?raw";
129
131
  import routerMd from "./content/router.md?raw";
@@ -417,8 +419,13 @@ export const UI_SECTIONS: DocSection[] = [
417
419
  pages: [
418
420
  {
419
421
  slug: "split",
420
- title: "Split & Pane",
421
- render: pattern("Split & Pane", "split", splitMd),
422
+ title: "Split",
423
+ render: pattern("Split", "split", splitMd),
424
+ },
425
+ {
426
+ slug: "pane",
427
+ title: "Pane",
428
+ render: pattern("Pane", "pane", paneMd),
422
429
  },
423
430
  {
424
431
  slug: "sidebar",
@@ -697,6 +704,11 @@ export const UI_SECTIONS: DocSection[] = [
697
704
  title: "Empty Value",
698
705
  render: comp("Empty Value", "empty-value", emptyValueMd),
699
706
  },
707
+ {
708
+ slug: "icon-tile",
709
+ title: "Icon Tile",
710
+ render: comp("Icon Tile", "icon-tile", iconTileMd),
711
+ },
700
712
  { slug: "item", title: "Item", render: comp("Item", "item", itemMd) },
701
713
  { slug: "kbd", title: "Kbd", render: comp("Kbd", "kbd", kbdMd) },
702
714
  {
package/src/ui/meta.ts CHANGED
@@ -53,6 +53,12 @@ export const componentMeta = {
53
53
  whenToUse:
54
54
  "Sinal compacto de estado. `context` declara neutral/primary/info/success/warning/danger e `variant` escolhe solid/outline. Passe `label` quando a cor reforçar informação; sem label, o ponto é decorativo. Para texto visível, use Badge.",
55
55
  },
56
+ "icon-tile": {
57
+ name: "icon-tile",
58
+ ancestry: "opus",
59
+ whenToUse:
60
+ "Dê suporte visual não interativo a um ícone com tamanho, contexto, preenchimento e arredondamento consistentes. Componha-o nos slots que pedem a moldura; passe o ícone diretamente quando ela não for necessária. Para uma ação, use Button com tamanho de ícone.",
61
+ },
56
62
  detail: {
57
63
  name: "detail",
58
64
  ancestry: "opus",
@@ -117,7 +123,7 @@ export const componentMeta = {
117
123
  name: "content-header",
118
124
  ancestry: "opus",
119
125
  whenToUse:
120
- "Estruture uma região de conteúdo com título, contador, descrição e ações. No caso comum, declare esses valores diretamente em Content; componha ContentHeader apenas quando precisar controlar a anatomia. Em coleções, Content mantém título e criação junto do ActionList. Contadores pertencem a Content e não ao título da Page.",
126
+ 'Estruture uma região com título e corpo. No caso comum, declare título, descrição, contador e ações diretamente em Content; use `framed` quando a região precisar de limite próprio, `as="fieldset"` para agrupar campos sob uma legenda nativa e `frameVariant="legend"` somente quando essa legenda precisar integrar visualmente a borda. Componha ContentHeader apenas quando precisar controlar a anatomia.',
121
127
  },
122
128
  copyable: {
123
129
  name: "copyable",
@@ -279,7 +285,7 @@ export const componentMeta = {
279
285
  name: "field",
280
286
  ancestry: "shadcn",
281
287
  whenToUse:
282
- "Componha rótulo, controle, ajuda e erro com espaçamento consistente. Use a orientação vertical no caso comum e a horizontal quando controle e texto precisarem ficar lado a lado; `responsive` continua aceito como alias da horizontal. Field cuida do layout; o formulário continua responsável por estado e validação.",
288
+ 'Componha rótulo, controle, ajuda e erro com espaçamento consistente. Use a orientação vertical no caso comum e a horizontal quando controle e texto precisarem ficar lado a lado. FieldGroup organiza vários campos; quando o conjunto precisar de título, componha-o dentro de Content com `as="fieldset"`.',
283
289
  },
284
290
  "input-otp": {
285
291
  name: "input-otp",
@@ -321,13 +327,19 @@ export const componentMeta = {
321
327
  name: "split",
322
328
  ancestry: "opus",
323
329
  whenToUse:
324
- "Divide uma área em panes em sequência horizontal ou vertical. Use `resizable` quando a pessoa deve ajustar a fronteira; o mesmo `<Split>` vira flex simples sem ele. Cada `<Pane>` declara tamanho inicial/mínimo e inset. SurfaceAssistant especializa esse layout para assistência contextual dentro de Page, Dialog ou Drawer.",
330
+ "Divide uma área em panes em sequência horizontal ou vertical. Use `resizable` quando a pessoa deve ajustar a fronteira; o mesmo `<Split>` vira flex simples sem ele. Cada `<Pane>` declara tamanho inicial e mínimo; a anatomia do pane tem página própria. SurfaceAssistant especializa esse layout para assistência contextual dentro de Page, Dialog ou Drawer.",
325
331
  },
326
332
  sidebar: {
327
333
  name: "sidebar",
328
334
  ancestry: "opus",
329
335
  whenToUse:
330
- "Organize navegação global ou contextual em uma coluna lateral. Split e Pane definem posição e largura; Sidebar fornece a superfície e o colapso, enquanto PaneHeader, PaneBody e PaneFooter estruturam as regiões fixa e rolável. Use SidebarNav para grupos planos e componha árvores com SidebarItem e SidebarTreeGroup; cabeçalho e rodapé próprios ficam em PaneHeader e PaneFooter.",
336
+ "Organize navegação global ou contextual em uma coluna lateral. Split e Pane definem posição e largura; Sidebar fornece a superfície e o colapso. Use SidebarNav para grupos planos e componha árvores com SidebarItem e SidebarTreeGroup; cabeçalho e rodapé próprios são do Pane que a hospeda.",
337
+ },
338
+ pane: {
339
+ name: "pane",
340
+ ancestry: "opus",
341
+ whenToUse:
342
+ "Estruture a coluna ou faixa encaixada num Split: PaneHeader nomeia a região e leva mídia e ações, PaneBody rola e PaneFooter fixa o que precisa ficar à vista. `layout` escolhe entre regiões seccionadas, com divisores derivados da vizinhança, e o fluxo contínuo. O `inset` vale para o pane sem slots; com eles, o recuo é de cada região.",
331
343
  },
332
344
  "scroll-area": {
333
345
  name: "scroll-area",
@@ -382,7 +394,7 @@ export const componentMeta = {
382
394
  name: "action-list",
383
395
  ancestry: "opus",
384
396
  whenToUse:
385
- "Apresente uma coleção pesquisável a partir de uma ListAction. O contrato declara colunas, filtros, período e busca; ActionList coordena visualizações, paginação, seleção em lote e sincronização com a URL. Em uma página, envolva a lista com Content para manter título e criação junto da coleção. Para um único recurso, use ActionView.",
397
+ "Apresente uma coleção pesquisável a partir de uma ListAction. O contrato declara colunas, filtros, período e busca; ActionList coordena visualizações, paginação, seleção em lote e sincronização com a URL. A tabela derivada não cria moldura própria. Em uma página, envolva a lista com Content para manter título e criação junto da coleção. Para um único recurso, use ActionView.",
386
398
  },
387
399
  "action-trigger": {
388
400
  name: "action-trigger",
package/src/ui/react.tsx CHANGED
@@ -93,6 +93,7 @@ export type {
93
93
  export {
94
94
  Card,
95
95
  CardHeader,
96
+ CardMedia,
96
97
  CardTitle,
97
98
  CardDescription,
98
99
  CardAction,
@@ -132,7 +133,9 @@ export {
132
133
  DialogOverlay,
133
134
  DialogContent,
134
135
  DialogHeader,
136
+ DialogNavigation,
135
137
  DialogMedia,
138
+ DialogActions,
136
139
  DialogBody,
137
140
  DialogFooter,
138
141
  DialogTitle,
@@ -143,6 +146,10 @@ export type {
143
146
  DialogMode,
144
147
  } from "./components/primitives/dialog.tsx";
145
148
 
149
+ // O pacote de anatomia que Card, Dialog, Drawer e Pane declaram no root (ADR 0023). As
150
+ // tabelas e o contexto continuam internos; o consumidor escolhe entre os dois nomes.
151
+ export type { SurfaceLayout } from "./components/patterns/surface-anatomy.ts";
152
+
146
153
  export {
147
154
  Menu,
148
155
  MenuTrigger,
@@ -270,10 +277,13 @@ export {
270
277
  } from "./components/primitives/collapsible.tsx";
271
278
  export {
272
279
  Drawer,
280
+ DrawerActions,
273
281
  DrawerClose,
274
282
  DrawerContent,
275
283
  DrawerFooter,
276
284
  DrawerHeader,
285
+ DrawerMedia,
286
+ DrawerNavigation,
277
287
  DrawerBody,
278
288
  DrawerTitle,
279
289
  DrawerTrigger,
@@ -298,6 +308,21 @@ export {
298
308
  FieldSet,
299
309
  FieldTitle,
300
310
  } from "./components/primitives/field.tsx";
311
+ export type {
312
+ FieldSetProps,
313
+ FieldSetVariant,
314
+ } from "./components/primitives/field.tsx";
315
+ export {
316
+ IconTile,
317
+ iconTileVariants,
318
+ resolveIconTileStyle,
319
+ } from "./components/primitives/icon-tile.tsx";
320
+ export type {
321
+ IconTileContext,
322
+ IconTileProps,
323
+ IconTileSize,
324
+ IconTileVariant,
325
+ } from "./components/primitives/icon-tile.tsx";
301
326
  export {
302
327
  InputGroup,
303
328
  InputGroupAddon,
@@ -471,6 +496,8 @@ export {
471
496
  export type {
472
497
  ContentProps,
473
498
  ContentHeaderProps,
499
+ ContentElement,
500
+ ContentFrameVariant,
474
501
  ContentHeaderLevel,
475
502
  ContentHeaderVariant,
476
503
  } from "./components/patterns/content-header.tsx";
@@ -505,7 +532,11 @@ export type {
505
532
 
506
533
  // Barra lateral composicional: header/conteúdo/footer e navegação, sem possuir o layout.
507
534
  export {
535
+ PaneActions,
536
+ PaneDescription,
508
537
  PaneHeader,
538
+ PaneMedia,
539
+ PaneTitle,
509
540
  PaneBody,
510
541
  PaneContent,
511
542
  PaneFooter,