@softize/opus 18.5.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 (32) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +293 -0
  3. package/package.json +1 -1
  4. package/src/ui/components/patterns/content-header.tsx +1 -9
  5. package/src/ui/components/patterns/heading-level.ts +16 -0
  6. package/src/ui/components/patterns/list.tsx +34 -15
  7. package/src/ui/components/patterns/pane.tsx +294 -0
  8. package/src/ui/components/patterns/presentation.tsx +29 -17
  9. package/src/ui/components/patterns/sidebar.tsx +13 -72
  10. package/src/ui/components/patterns/split.tsx +8 -35
  11. package/src/ui/components/patterns/surface-anatomy.ts +219 -0
  12. package/src/ui/components/patterns/surface-header.tsx +51 -6
  13. package/src/ui/components/patterns/surface-media.tsx +69 -0
  14. package/src/ui/components/primitives/card.tsx +91 -29
  15. package/src/ui/components/primitives/command.tsx +12 -4
  16. package/src/ui/components/primitives/detail.tsx +10 -13
  17. package/src/ui/components/primitives/dialog.tsx +138 -35
  18. package/src/ui/components/primitives/drawer.tsx +122 -23
  19. package/src/ui/components/primitives/metric-card.tsx +98 -39
  20. package/src/ui/docs/content/card.md +221 -30
  21. package/src/ui/docs/content/content.md +6 -0
  22. package/src/ui/docs/content/dialog.md +115 -7
  23. package/src/ui/docs/content/drawer.md +136 -9
  24. package/src/ui/docs/content/metric-card.md +57 -18
  25. package/src/ui/docs/content/page.md +8 -0
  26. package/src/ui/docs/content/pane.md +250 -0
  27. package/src/ui/docs/content/sidebar.md +22 -43
  28. package/src/ui/docs/content/skeleton.md +7 -3
  29. package/src/ui/docs/content/split.md +16 -21
  30. package/src/ui/docs/registry.tsx +8 -2
  31. package/src/ui/meta.ts +8 -2
  32. package/src/ui/react.tsx +14 -0
@@ -1,8 +1,9 @@
1
1
  ## Painel lateral de detalhe
2
2
 
3
3
  Use `Drawer` para abrir detalhes ou edição junto a uma borda, preservando a referência visual da
4
- tela ao fundo. Para conteúdo curto e centralizado, prefira `Dialog`. Com `side="right"`, o painel
5
- entra pela direita e pode ser fechado por Esc, pelo overlay ou pelo botão de fechamento.
4
+ tela ao fundo. Para conteúdo curto e centralizado, prefira [`Dialog`](/ui/dialog). Com
5
+ `side="right"`, o painel entra pela direita e pode ser fechado por Esc, pelo overlay ou pelo botão
6
+ de fechamento.
6
7
 
7
8
  ```tsx preview
8
9
  <Drawer>
@@ -23,11 +24,10 @@ entra pela direita e pode ser fechado por Esc, pelo overlay ou pelo botão de fe
23
24
 
24
25
  ## Edição com ações persistentes
25
26
 
26
- `side` aceita `top`, `right`, `bottom` e `left`. `DrawerFooter` mantém as ações no rodapé;
27
- `DrawerClose` fecha o painel sem exigir controle manual de estado. Como no `Dialog`, o cabeçalho
28
- recebe um divisor inferior e o rodapé usa um divisor superior. O corpo preserva o mesmo alinhamento
29
- horizontal entre título, conteúdo e ações. O fechamento do cabeçalho usa uma ação circular neutra
30
- e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
27
+ `side` aceita `top`, `right`, `bottom` e `left`. `DrawerFooter` mantém as ações no rodapé e
28
+ `DrawerClose` fecha o painel sem exigir controle manual de estado. O corpo preserva o mesmo
29
+ alinhamento horizontal do cabeçalho e do rodapé. O fechamento do cabeçalho usa uma ação circular
30
+ neutra e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
31
31
 
32
32
  ```tsx preview
33
33
  <Drawer>
@@ -54,6 +54,86 @@ e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
54
54
  </Drawer>
55
55
  ```
56
56
 
57
+ O rodapé usa a mesma faixa sutil do [`Dialog`](/ui/dialog). As duas famílias compartilham a
58
+ anatomia, então um fluxo que começa em um modal e continua em um painel não muda de aparência no
59
+ meio.
60
+
61
+ ## Regiões separadas ou contínuas
62
+
63
+ `layout` declara a anatomia da superfície inteira e fica em `DrawerContent`; os slots leem esse
64
+ valor e não o recebem. O padrão é `sectioned`: recuo igual nas três regiões, faixa no rodapé e
65
+ divisor entre as regiões vizinhas. É o painel de trabalho, em que o corpo rola sob um cabeçalho e um
66
+ rodapé fixos.
67
+
68
+ Use `layout="continuous"` quando o painel apresentar um único bloco de leitura e a linha virar
69
+ ruído.
70
+
71
+ ```tsx preview
72
+ <Drawer>
73
+ <DrawerTrigger asChild>
74
+ <Button variant="outline">Ver resumo</Button>
75
+ </DrawerTrigger>
76
+ <DrawerContent layout="continuous">
77
+ <DrawerHeader>
78
+ <DrawerTitle>Resumo da execução</DrawerTitle>
79
+ </DrawerHeader>
80
+ <DrawerBody>
81
+ <p className="text-sm text-muted-foreground">
82
+ Concluída em 2 minutos, sem falhas registradas.
83
+ </p>
84
+ </DrawerBody>
85
+ </DrawerContent>
86
+ </Drawer>
87
+ ```
88
+
89
+ Os divisores do modo seccionado são derivados da vizinhança: cada região desenha a linha quando há
90
+ uma região do outro lado. Um cabeçalho sem corpo não fecha nada, e um cabeçalho seguido direto de um
91
+ rodapé produz uma linha só. A tela não escreve `border-b` nem `border-t`.
92
+
93
+ ## Mídia, ações e navegação no cabeçalho
94
+
95
+ O cabeçalho reconhece quatro slots — `DrawerNavigation`, `DrawerMedia`, `DrawerTitle` e
96
+ `DrawerActions` — e posiciona cada um: a navegação abre a linha, a mídia vem antes do título, o
97
+ título ocupa a folga, as ações vão para o extremo oposto e o botão de fechar entra depois delas. Um
98
+ nó que não seja um desses slots interrompe a renderização com um erro; contexto e conteúdo
99
+ pertencem ao `DrawerBody`.
100
+
101
+ `DrawerMedia` é a moldura de ícone da casa — o mesmo [`IconTile`](/ui/icon-tile) —, útil para
102
+ identificar o tipo do recurso aberto. `DrawerNavigation` carrega o retorno de um fluxo encadeado,
103
+ em que o painel permanece aberto e troca de etapa.
104
+
105
+ ```tsx preview
106
+ <Drawer>
107
+ <DrawerTrigger asChild>
108
+ <Button variant="outline">Abrir veículo</Button>
109
+ </DrawerTrigger>
110
+ <DrawerContent side="right">
111
+ <DrawerHeader>
112
+ <DrawerNavigation>
113
+ <Button variant="ghost" size="icon-sm" aria-label="Voltar para a lista">
114
+ <ArrowLeft />
115
+ </Button>
116
+ </DrawerNavigation>
117
+ <DrawerMedia context="primary">
118
+ <CarFront />
119
+ </DrawerMedia>
120
+ <DrawerTitle>ABC-1D23</DrawerTitle>
121
+ <DrawerActions>
122
+ <Button variant="ghost" size="icon-sm" aria-label="Mais ações">
123
+ <Ellipsis />
124
+ </Button>
125
+ </DrawerActions>
126
+ </DrawerHeader>
127
+ <DrawerBody>
128
+ <p className="text-sm text-muted-foreground">Histórico de passagens na oficina.</p>
129
+ </DrawerBody>
130
+ </DrawerContent>
131
+ </Drawer>
132
+ ```
133
+
134
+ O cabeçalho não recebe descrição: o painel já nomeia o recurso, e o contexto que ajuda a decidir
135
+ aparece no começo do corpo, como texto ou `Alert`.
136
+
57
137
  ## Propriedades de Drawer
58
138
 
59
139
  | Propriedade | Tipo | Padrão | Descrição |
@@ -66,12 +146,59 @@ e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
66
146
  | Propriedade | Tipo | Padrão | Descrição |
67
147
  | ----------------- | ---------------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
68
148
  | `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'right'` | Borda de onde o painel aparece. |
69
- | `showCloseButton` | `boolean` | `true` | Inclui a ação de fechamento no header. Desative quando o painel exigir uma ação explícita. |
149
+ | `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. |
150
+ | `showCloseButton` | `boolean` | `true` | Inclui a ação de fechamento no cabeçalho. Desative quando o painel exigir uma ação explícita. |
70
151
  | `closeDisabled` | `boolean` | `false` | Mantém a ação visível e impede fechar enquanto o painel está bloqueado. |
71
- | `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
152
+ | `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
153
+
154
+ ## Propriedades de DrawerHeader
155
+
156
+ | Propriedade | Tipo | Padrão | Descrição |
157
+ | ----------- | ----------- | ------ | ------------------------------------------------------------------------------------------ |
158
+ | `className` | `string` | | Ajusta a faixa superior sobre o recuo do layout. |
159
+ | `children` | `ReactNode` | | Somente `DrawerNavigation`, `DrawerMedia`, `DrawerTitle` e `DrawerActions`, no máximo um de cada; o fechamento padrão entra depois deles. |
160
+
161
+ ## Propriedades de DrawerMedia
162
+
163
+ | Propriedade | Tipo | Padrão | Descrição |
164
+ | ----------- | ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------- |
165
+ | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'sm'` | Tamanho da moldura e do glifo na escala compartilhada. |
166
+ | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
167
+ | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
168
+ | `className` | `string` | | Ajusta a moldura sem substituir a escala. |
169
+ | `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. |
170
+
171
+ ## Propriedades de DrawerNavigation
172
+
173
+ | Propriedade | Tipo | Padrão | Descrição |
174
+ | ----------- | ----------- | ------ | ----------------------------------------------------- |
175
+ | `className` | `string` | | Ajusta a região introdutória da linha. |
176
+ | `children` | `ReactNode` | | Retorno de um fluxo encadeado, antes do título. |
177
+
178
+ ## Propriedades de DrawerActions
179
+
180
+ | Propriedade | Tipo | Padrão | Descrição |
181
+ | ----------- | ----------- | ------ | -------------------------------------------------------- |
182
+ | `className` | `string` | | Ajusta o agrupamento das ações. |
183
+ | `children` | `ReactNode` | | Ações da tela no cabeçalho, antes do botão de fechar. |
184
+
185
+ ## Propriedades de DrawerTitle
186
+
187
+ | Propriedade | Tipo | Padrão | Descrição |
188
+ | ----------- | ----------- | ------ | ------------------------------------------------------------------- |
189
+ | `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `h2` da primitiva | Hierarquia do documento. O tamanho vem do `layout`, nunca do nível. |
190
+ | `className` | `string` | | Ajusta o título, que trunca quando não cabe na linha. |
191
+ | `children` | `ReactNode` | | Nome do recurso ou da tarefa; nomeia a superfície para leitores de tela. |
72
192
 
73
193
  ## Propriedades de DrawerBody
74
194
 
75
195
  `DrawerBody` aceita os atributos de `HTMLDivElement` e ocupa a região flexível e rolável entre
76
196
  `DrawerHeader` e `DrawerFooter`. O cabeçalho nomeia o painel; contexto relevante entra no começo
77
197
  do corpo como texto ou `Alert`.
198
+
199
+ ## Propriedades de DrawerFooter
200
+
201
+ | Propriedade | Tipo | Padrão | Descrição |
202
+ | ----------- | ----------- | ------ | ---------------------------------------------------------------------- |
203
+ | `className` | `string` | | Ajusta a faixa do rodapé, inclusive a distribuição das ações. |
204
+ | `children` | `ReactNode` | | Ações persistentes, normalmente dentro de um `ButtonGroup`. |
@@ -45,7 +45,9 @@ apresente dois cards independentes.
45
45
  labelAction={
46
46
  <Tooltip>
47
47
  <TooltipTrigger asChild>
48
- <Button size="icon-xs" variant="ghost" aria-label="Sobre Agendados"><Info /></Button>
48
+ <Button size="icon-xs" variant="ghost" aria-label="Sobre Agendados">
49
+ <Info />
50
+ </Button>
49
51
  </TooltipTrigger>
50
52
  <TooltipContent>Eventos atualmente agendados.</TooltipContent>
51
53
  </Tooltip>
@@ -56,7 +58,9 @@ apresente dois cards independentes.
56
58
  labelAction: (
57
59
  <Tooltip>
58
60
  <TooltipTrigger asChild>
59
- <Button size="icon-xs" variant="ghost" aria-label="Sobre o período"><Info /></Button>
61
+ <Button size="icon-xs" variant="ghost" aria-label="Sobre o período">
62
+ <Info />
63
+ </Button>
60
64
  </TooltipTrigger>
61
65
  <TooltipContent>Agendamentos registrados dentro do período.</TooltipContent>
62
66
  </Tooltip>
@@ -65,6 +69,38 @@ apresente dois cards independentes.
65
69
  />
66
70
  ```
67
71
 
72
+ ## Como filtro ou seleção
73
+
74
+ Use a ativação controlada quando escolher uma métrica também precisar aplicar um recorte. O card
75
+ expõe o estado por `aria-pressed` e preserva controles complementares, como o tooltip do rótulo,
76
+ sem disparar a ativação.
77
+
78
+ ```tsx preview col
79
+ const [metric, setMetric] = useState('scheduled')
80
+
81
+ render(
82
+ <div className="grid grid-cols-2 gap-3">
83
+ <MetricCard
84
+ label="Agendados"
85
+ value="18"
86
+ active={metric === 'scheduled'}
87
+ activationLabel="Filtrar por Agendados"
88
+ onActivate={() => setMetric('scheduled')}
89
+ />
90
+ <MetricCard
91
+ label="Efetivados"
92
+ value="11"
93
+ active={metric === 'completed'}
94
+ activationLabel="Filtrar por Efetivados"
95
+ onActivate={() => setMetric('completed')}
96
+ />
97
+ </div>,
98
+ )
99
+ ```
100
+
101
+ `active`, `activationLabel` e `onActivate` formam uma capacidade única: informe as três propriedades
102
+ para tornar o card ativável. O consumidor continua responsável pelo estado e pelo recorte aplicado.
103
+
68
104
  ## Carregamento
69
105
 
70
106
  `loading` preserva a geometria do card com placeholders e marca a superfície como ocupada para
@@ -76,22 +112,25 @@ tecnologias assistivas. O contêiner da coleção continua responsável pela men
76
112
 
77
113
  ## Propriedades de MetricCard
78
114
 
79
- | Propriedade | Tipo | Padrão | Descrição |
80
- |---|---|---|---|
81
- | `label` | `ReactNode` | | O nome da medida. |
82
- | `value` | `ReactNode` | | O valor já formatado pelo consumidor. |
83
- | `loading` | `boolean` | `false` | Substitui rótulo e valor por skeletons e marca `aria-busy`. |
84
- | `labelAction` | `ReactNode` | | Controle ao lado do rótulo, como uma explicação em tooltip. |
85
- | `description` | `ReactNode` | | Contexto que explica recorte, proporção ou significado do valor. |
86
- | `secondaryMetric` | `MetricCardSecondaryMetric` | | Medida complementar exibida como uma lâmina posterior sob o card principal. |
87
- | `icon` | `ReactNode` | | Ícone decorativo que identifica a natureza da medida. |
88
- | `context` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Contexto semântico do ícone; não altera a superfície do card. |
89
- | `action` | `ReactNode` | | Ação relacionada diretamente à medida. |
115
+ | Propriedade | Tipo | Padrão | Descrição |
116
+ | ----------------- | ----------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------- |
117
+ | `label` | `ReactNode` | | O nome da medida. |
118
+ | `value` | `ReactNode` | | O valor já formatado pelo consumidor. |
119
+ | `loading` | `boolean` | `false` | Substitui rótulo e valor por skeletons e marca `aria-busy`. |
120
+ | `labelAction` | `ReactNode` | | Controle ao lado do rótulo, como uma explicação em tooltip. |
121
+ | `description` | `ReactNode` | | Contexto que explica recorte, proporção ou significado do valor. |
122
+ | `secondaryMetric` | `MetricCardSecondaryMetric` | | Medida complementar exibida como uma lâmina posterior sob o card principal. |
123
+ | `icon` | `ReactNode` | | Ícone decorativo que identifica a natureza da medida. |
124
+ | `context` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Contexto semântico do ícone; não altera a superfície do card. |
125
+ | `action` | `ReactNode` | | Ação relacionada diretamente à medida. |
126
+ | `active` | `boolean` | | Indica se a medida controla o recorte atualmente aplicado. Exige `activationLabel` e `onActivate`. |
127
+ | `activationLabel` | `string` | | Nome acessível da ativação. Exige `active` e `onActivate`. |
128
+ | `onActivate` | `() => void` | | Ativa a medida como recorte ou seleção. Exige `active` e `activationLabel`. |
90
129
 
91
130
  ## Propriedades de MetricCardSecondaryMetric
92
131
 
93
- | Propriedade | Tipo | Padrão | Descrição |
94
- |---|---|---|---|
95
- | `label` | `ReactNode` | | O nome da medida complementar. |
96
- | `value` | `ReactNode` | | O valor complementar já formatado pelo consumidor. |
97
- | `labelAction` | `ReactNode` | | Controle próprio ao lado do rótulo, como uma explicação em tooltip. |
132
+ | Propriedade | Tipo | Padrão | Descrição |
133
+ | ------------- | ----------- | ------ | ------------------------------------------------------------------- |
134
+ | `label` | `ReactNode` | | O nome da medida complementar. |
135
+ | `value` | `ReactNode` | | O valor complementar já formatado pelo consumidor. |
136
+ | `labelAction` | `ReactNode` | | Controle próprio ao lado do rótulo, como uma explicação em tooltip. |
@@ -17,6 +17,13 @@ introdução podem ser omitidos quando o próprio conteúdo nomeia a página.
17
17
  Contadores e outros indicadores pertencem ao
18
18
  conteúdo que os explica.
19
19
 
20
+ A `Page` é contínua por definição: separar o chrome do conteúdo é trabalho do shell, e não há
21
+ divisores entre regiões para declarar. O `layout` da `Page` não é a anatomia de superfície de
22
+ [`Card`](/ui/card), [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer) e [`Pane`](/ui/pane) — aqui ele
23
+ decide a ocupação de altura. `flow`, o padrão, deixa a página crescer e a rolagem acontecer na
24
+ janela; `fill` limita a página à altura do shell para que uma região interna, como uma tabela longa
25
+ ou um split, assuma a rolagem.
26
+
20
27
  `PageShell` não inclui sidebar nem inventa breadcrumb. Ele ocupa o painel principal já delimitado e
21
28
  mantém a barra com `3.75rem`, mesmo quando o conteúdo muda de estado. Tabs ficam reservados a recortes
22
29
  da mesma superfície. Canvas e outros workspaces espaciais imersivos podem usar um shell próprio.
@@ -196,6 +203,7 @@ estado na própria seção com `DataState`, `ActionView`, `ActionList` ou `Alert
196
203
  | ----------- | ----------- | ----------- | ----------------------------------------------------------------------------------------------- |
197
204
  | `title` | `ReactNode` | | O h1 da página. |
198
205
  | `actions` | `ReactNode` | | Ações contextuais no extremo oposto do cabeçalho. |
206
+ | `layout` | `'flow' \| 'fill'` | `'flow'` | Ocupação de altura, não anatomia: `fill` limita a página ao shell para que uma região interna role. |
199
207
  | `className` | `string` | `max-w-7xl` | Classes do container para substituir o teto padrão de `80rem`. |
200
208
  | `children` | `ReactNode` | | O body da página — espaçamento e diagramação são seus. |
201
209
 
@@ -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. |