@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,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 |
@@ -125,6 +125,7 @@ import actionSearchDialogMd from "./content/action-list-dialog.md?raw";
125
125
  import pageMd from "./content/page.md?raw";
126
126
  import presentationMd from "./content/presentation.md?raw";
127
127
  import contentMd from "./content/content.md?raw";
128
+ import paneMd from "./content/pane.md?raw";
128
129
  import sidebarMd from "./content/sidebar.md?raw";
129
130
  import splitMd from "./content/split.md?raw";
130
131
  import routerMd from "./content/router.md?raw";
@@ -418,8 +419,13 @@ export const UI_SECTIONS: DocSection[] = [
418
419
  pages: [
419
420
  {
420
421
  slug: "split",
421
- title: "Split & Pane",
422
- 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),
423
429
  },
424
430
  {
425
431
  slug: "sidebar",
package/src/ui/meta.ts CHANGED
@@ -327,13 +327,19 @@ export const componentMeta = {
327
327
  name: "split",
328
328
  ancestry: "opus",
329
329
  whenToUse:
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/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.",
331
331
  },
332
332
  sidebar: {
333
333
  name: "sidebar",
334
334
  ancestry: "opus",
335
335
  whenToUse:
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, 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.",
337
343
  },
338
344
  "scroll-area": {
339
345
  name: "scroll-area",
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,
@@ -522,7 +532,11 @@ export type {
522
532
 
523
533
  // Barra lateral composicional: header/conteúdo/footer e navegação, sem possuir o layout.
524
534
  export {
535
+ PaneActions,
536
+ PaneDescription,
525
537
  PaneHeader,
538
+ PaneMedia,
539
+ PaneTitle,
526
540
  PaneBody,
527
541
  PaneContent,
528
542
  PaneFooter,