@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.
- package/CHANGELOG.md +82 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +293 -0
- package/package.json +1 -1
- package/src/ui/components/patterns/content-header.tsx +1 -9
- package/src/ui/components/patterns/heading-level.ts +16 -0
- package/src/ui/components/patterns/list.tsx +34 -15
- package/src/ui/components/patterns/pane.tsx +294 -0
- package/src/ui/components/patterns/presentation.tsx +29 -17
- package/src/ui/components/patterns/sidebar.tsx +13 -72
- package/src/ui/components/patterns/split.tsx +8 -35
- package/src/ui/components/patterns/surface-anatomy.ts +219 -0
- package/src/ui/components/patterns/surface-header.tsx +51 -6
- package/src/ui/components/patterns/surface-media.tsx +69 -0
- package/src/ui/components/primitives/card.tsx +91 -29
- package/src/ui/components/primitives/command.tsx +12 -4
- package/src/ui/components/primitives/detail.tsx +10 -13
- package/src/ui/components/primitives/dialog.tsx +138 -35
- package/src/ui/components/primitives/drawer.tsx +122 -23
- package/src/ui/components/primitives/metric-card.tsx +98 -39
- package/src/ui/docs/content/card.md +221 -30
- package/src/ui/docs/content/content.md +6 -0
- package/src/ui/docs/content/dialog.md +115 -7
- package/src/ui/docs/content/drawer.md +136 -9
- package/src/ui/docs/content/metric-card.md +57 -18
- package/src/ui/docs/content/page.md +8 -0
- package/src/ui/docs/content/pane.md +250 -0
- package/src/ui/docs/content/sidebar.md +22 -43
- package/src/ui/docs/content/skeleton.md +7 -3
- package/src/ui/docs/content/split.md +16 -21
- package/src/ui/docs/registry.tsx +8 -2
- package/src/ui/meta.ts +8 -2
- 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
|
|
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
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
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
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
<
|
|
24
|
-
|
|
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}
|
|
12
|
-
<
|
|
11
|
+
<Pane initialSize={30} minSize={20} className="bg-muted/30">
|
|
12
|
+
<p className="text-sm">Navegação</p>
|
|
13
13
|
</Pane>
|
|
14
|
-
<Pane grow
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
espaçamento; `sm` e `lg` atendem outras densidades.
|
|
28
|
+
## Tamanho de cada pane
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
37
|
+
<Pane grow>
|
|
36
38
|
<Main />
|
|
37
39
|
</Pane>
|
|
38
|
-
<Pane initialSize="28rem" minSize="20rem"
|
|
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 |
|
package/src/ui/docs/registry.tsx
CHANGED
|
@@ -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
|
|
422
|
-
render: pattern("Split
|
|
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
|
|
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
|
|
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,
|