@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
@@ -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`. |
@@ -25,7 +25,9 @@ vincula ao controle, ligue-o ao controle por `aria-labelledby` para manter o nom
25
25
  <Field orientation="horizontal">
26
26
  <FieldContent>
27
27
  <FieldTitle id="auto-review-title">Acionar o revisor no handoff</FieldTitle>
28
- <FieldDescription>Ao abrir o handoff, o agente reviewer entra na sessão automaticamente.</FieldDescription>
28
+ <FieldDescription>
29
+ Ao abrir o handoff, o agente reviewer entra na sessão automaticamente.
30
+ </FieldDescription>
29
31
  </FieldContent>
30
32
  <Switch id="auto-review" aria-labelledby="auto-review-title" defaultChecked />
31
33
  </Field>
@@ -33,22 +35,23 @@ vincula ao controle, ligue-o ao controle por `aria-labelledby` para manter o nom
33
35
 
34
36
  ## Conjunto com legenda e erro
35
37
 
36
- `FieldSet` agrupa campos sob uma `FieldLegend`; `FieldGroup` define o espaçamento e
37
- `FieldSeparator` marca uma divisão. `FieldError` aparece somente quando existe mensagem de erro.
38
- Declare `data-invalid="true"` no `Field` para que o rótulo acompanhe o contexto de erro;
39
- `aria-invalid` marca apenas o controle. O `ActionForm` define os dois atributos sozinho.
40
-
41
- Quando vários conjuntos compartilham a mesma superfície, use `variant="section"` para separar cada
42
- grupo com uma linha leve. `icon` acrescenta uma referência visual antes do título sem transformar o
43
- conjunto em outro card; escolha um ícone que represente a dimensão do grupo, não uma ação.
38
+ Use `Content as="fieldset"` para nomear um conjunto de campos relacionados. O título vira a legenda
39
+ nativa, enquanto `FieldGroup` define o espaçamento e `FieldSeparator` marca uma divisão.
40
+ `FieldError` aparece somente quando existe mensagem de erro. Declare `data-invalid="true"` no
41
+ `Field` para que o rótulo acompanhe o contexto de erro; `aria-invalid` marca apenas o controle. O
42
+ `ActionForm` define os dois atributos sozinho.
44
43
 
45
44
  ```tsx preview col md
46
- <FieldSet>
47
- <FieldLegend>Novo agente</FieldLegend>
45
+ <Content as="fieldset" title="Novo agente">
48
46
  <FieldGroup>
49
47
  <Field data-invalid="true">
50
48
  <FieldLabel htmlFor="agent-name">Nome</FieldLabel>
51
- <Input id="agent-name" defaultValue="" aria-invalid placeholder="Ex.: developer" />
49
+ <Input
50
+ id="agent-name"
51
+ defaultValue=""
52
+ aria-invalid
53
+ placeholder="Ex.: developer"
54
+ />
52
55
  <FieldError>Informe um nome para o agente.</FieldError>
53
56
  </Field>
54
57
  <FieldSeparator />
@@ -57,38 +60,83 @@ conjunto em outro card; escolha um ícone que represente a dimensão do grupo, n
57
60
  <span>Prompt</span>
58
61
  <LabelHelp help="A síntese vira a primeira mensagem da sessão." />
59
62
  </FieldLabel>
60
- <Textarea id="agent-prompt" rows={3} defaultValue="Veste o papel de developer no workspace Empresa X." />
63
+ <Textarea
64
+ id="agent-prompt"
65
+ rows={3}
66
+ defaultValue="Veste o papel de developer no workspace Empresa X."
67
+ />
68
+ </Field>
69
+ </FieldGroup>
70
+ </Content>
71
+ ```
72
+
73
+ ## Conjunto com moldura
74
+
75
+ Use a moldura para distinguir grupos vizinhos que continuem pertencendo ao mesmo formulário. O
76
+ título permanece uma legenda semântica. A moldura é contínua por padrão; quando o agrupamento pedir
77
+ o acabamento compacto com a legenda integrada à borda, declare `frameVariant="legend"`.
78
+
79
+ ```tsx preview col md
80
+ <Content
81
+ as="fieldset"
82
+ framed
83
+ frameVariant="legend"
84
+ title={
85
+ <span className="flex items-center gap-2">
86
+ <IconTile size="xs">
87
+ <CarFront />
88
+ </IconTile>
89
+ Veículo
90
+ </span>
91
+ }
92
+ >
93
+ <FieldGroup>
94
+ <Field>
95
+ <FieldLabel htmlFor="vehicle-plate">Placa</FieldLabel>
96
+ <Input id="vehicle-plate" defaultValue="GBR1A03" />
61
97
  </Field>
62
98
  </FieldGroup>
63
- </FieldSet>
99
+ </Content>
64
100
  ```
65
101
 
102
+ ## Compatibilidade de FieldSet
103
+
104
+ `FieldSet` e `FieldLegend` continuam disponíveis para composições existentes. Em código novo,
105
+ prefira `Content as="fieldset"`: ele reúne legenda, corpo e moldura na mesma anatomia usada pelas
106
+ demais regiões nomeadas. A migração pode ser gradual porque os controles e o `FieldGroup` não mudam.
107
+
108
+ ## Propriedades de FieldSet
109
+
110
+ | Propriedade | Tipo | Padrão | Descrição |
111
+ | ----------- | --------------------- | --------- | ----------------------------------------------------------------------------- |
112
+ | `variant` | `'plain' \| 'framed'` | `'plain'` | Mantém o conjunto aberto ou aplica a moldura com a legenda integrada à borda. |
113
+
66
114
  ## Propriedades de Field
67
115
 
68
- | Propriedade | Tipo | Padrão | Descrição |
69
- |---|---|---|---|
70
- | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Direção fixa do campo. `responsive` permanece como alias compatível de `horizontal`. |
71
- | `data-invalid` | `boolean \| 'true' \| 'false'` | | Com `true`, aplica o contexto de erro ao campo e ao rótulo. O `ActionForm` define o atributo sozinho. |
116
+ | Propriedade | Tipo | Padrão | Descrição |
117
+ | -------------- | -------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- |
118
+ | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Direção fixa do campo. `responsive` permanece como alias compatível de `horizontal`. |
119
+ | `data-invalid` | `boolean \| 'true' \| 'false'` | | Com `true`, aplica o contexto de erro ao campo e ao rótulo. O `ActionForm` define o atributo sozinho. |
72
120
 
73
121
  ## Propriedades de FieldLegend
74
122
 
75
- | Propriedade | Tipo | Padrão | Descrição |
76
- |---|---|---|---|
77
- | `variant` | `'legend' \| 'label' \| 'section'` | `'legend'` | Define o porte de título, de rótulo compacto ou de seção com divisor. |
78
- | `icon` | `ReactNode` | | Exibe um ícone decorativo antes da legenda. Na variante `section`, o ícone recebe um suporte tonal. |
123
+ | Propriedade | Tipo | Padrão | Descrição |
124
+ | ----------- | ---------------------------------- | ---------- | ------------------------------------------------------------------------------------------ |
125
+ | `variant` | `'legend' \| 'label' \| 'section'` | `'legend'` | Define o porte de título, de rótulo compacto ou de seção com divisor. |
126
+ | `icon` | `ReactNode` | | Exibe conteúdo decorativo antes da legenda; componha `IconTile` quando quiser uma moldura. |
79
127
 
80
128
  ## Propriedades de FieldError
81
129
 
82
- | Propriedade | Tipo | Padrão | Descrição |
83
- |---|---|---|---|
84
- | `errors` | `Array<{ message?: string } \| undefined>` | | Deduplica e apresenta uma lista de erros; sem conteúdo, o componente não renderiza. |
85
- | `children` | `ReactNode` | | Mensagem literal, com prioridade sobre `errors`. |
130
+ | Propriedade | Tipo | Padrão | Descrição |
131
+ | ----------- | ------------------------------------------ | ------ | ----------------------------------------------------------------------------------- |
132
+ | `errors` | `Array<{ message?: string } \| undefined>` | | Deduplica e apresenta uma lista de erros; sem conteúdo, o componente não renderiza. |
133
+ | `children` | `ReactNode` | | Mensagem literal, com prioridade sobre `errors`. |
86
134
 
87
135
  ## Propriedades de FieldSeparator
88
136
 
89
- | Propriedade | Tipo | Padrão | Descrição |
90
- |---|---|---|---|
91
- | `children` | `ReactNode` | | Rótulo opcional no centro da divisão; sem conteúdo, exibe somente a linha. |
137
+ | Propriedade | Tipo | Padrão | Descrição |
138
+ | ----------- | ----------- | ------ | -------------------------------------------------------------------------- |
139
+ | `children` | `ReactNode` | | Rótulo opcional no centro da divisão; sem conteúdo, exibe somente a linha. |
92
140
 
93
141
  `FieldSet`, `FieldGroup`, `FieldContent`, `FieldLabel`, `FieldTitle` e `FieldDescription` aceitam as
94
- props nativas dos respectivos elementos e não adicionam propriedades próprias.
142
+ props nativas dos respectivos elementos. `FieldSet` mantém `variant` durante a compatibilidade.
@@ -0,0 +1,47 @@
1
+ ## Moldura decorativa para ícone
2
+
3
+ Use `IconTile` quando um ícone precisar de uma pequena superfície para ganhar hierarquia em uma
4
+ legenda, item ou resumo. O componente não é interativo; para uma ação, use `Button` com tamanho de
5
+ ícone e um nome acessível.
6
+
7
+ ```tsx preview row
8
+ <IconTile size="xs"><UserRound /></IconTile>
9
+ <IconTile size="sm" variant="outline"><CarFront /></IconTile>
10
+ <IconTile context="primary"><CalendarDays /></IconTile>
11
+ ```
12
+
13
+ O tamanho controla a caixa e o glifo pela escala compartilhada dos controles. Um ícone que declara
14
+ uma classe `size-*` preserva a própria medida.
15
+
16
+ ## Em uma legenda
17
+
18
+ Slots de ícone não criam a moldura automaticamente. Componha `IconTile` somente quando ela fizer
19
+ sentido para a superfície.
20
+
21
+ ```tsx preview col md
22
+ <Content
23
+ as="fieldset"
24
+ framed
25
+ frameVariant="legend"
26
+ title={
27
+ <span className="flex items-center gap-2">
28
+ <IconTile size="xs">
29
+ <CarFront />
30
+ </IconTile>
31
+ Veículo
32
+ </span>
33
+ }
34
+ >
35
+ <FieldGroup />
36
+ </Content>
37
+ ```
38
+
39
+ ## Propriedades
40
+
41
+ | Propriedade | Tipo | Padrão | Descrição |
42
+ | ----------- | ------------------------------------------------------------------------ | ----------- | ------------------------------------------------------------- |
43
+ | `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
44
+ | `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'default'` | Define o tamanho da caixa e do glifo na escala compartilhada. |
45
+ | `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
46
+
47
+ As demais propriedades são as propriedades nativas de `span`.
@@ -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