@softize/opus 19.0.0 → 20.1.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 +239 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/check.mjs +198 -10
- package/bin/lib/copy.mjs +29 -0
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0020-icon-tiles-are-explicitly-composed.md +8 -0
- package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -0
- package/docs/data-products.md +15 -9
- package/docs/protocol.md +3 -4
- package/docs/releasing.md +2 -2
- package/package.json +2 -2
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
- package/registry/templates/app/package.json +1 -1
- package/src/core/runtime.ts +96 -8
- package/src/core/types.ts +5 -2
- package/src/ui/components/patterns/action-form-card.tsx +11 -1
- package/src/ui/components/patterns/content-header.tsx +109 -18
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +91 -92
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/presentation.tsx +52 -37
- package/src/ui/components/patterns/split.tsx +44 -4
- package/src/ui/components/patterns/surface-anatomy.ts +35 -8
- package/src/ui/components/patterns/surface-header.tsx +58 -25
- package/src/ui/components/patterns/trigger.tsx +12 -2
- package/src/ui/components/primitives/alert.tsx +9 -1
- package/src/ui/components/primitives/avatar.tsx +7 -3
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/carousel.tsx +34 -5
- package/src/ui/components/primitives/checkbox.tsx +11 -3
- package/src/ui/components/primitives/command.tsx +2 -1
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/dialog.tsx +42 -11
- package/src/ui/components/primitives/drawer.tsx +10 -2
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -2
- package/src/ui/components/primitives/icon-tile.tsx +7 -0
- package/src/ui/components/primitives/input-group.tsx +9 -2
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/kbd.tsx +1 -1
- package/src/ui/components/primitives/label.tsx +3 -0
- package/src/ui/components/primitives/menu.tsx +5 -0
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/components/primitives/progress.tsx +9 -1
- package/src/ui/components/primitives/slider.tsx +62 -11
- package/src/ui/components/primitives/truncate.tsx +10 -1
- package/src/ui/docs/content/accordion.md +74 -27
- package/src/ui/docs/content/action-form-card.md +24 -15
- package/src/ui/docs/content/action-form-dialog.md +17 -7
- package/src/ui/docs/content/action-form.md +92 -77
- package/src/ui/docs/content/action-list-dialog.md +75 -38
- package/src/ui/docs/content/action-list.md +141 -58
- package/src/ui/docs/content/action-trigger.md +27 -10
- package/src/ui/docs/content/action-view.md +14 -14
- package/src/ui/docs/content/actions.md +161 -66
- package/src/ui/docs/content/ai.md +43 -44
- package/src/ui/docs/content/alert.md +83 -29
- package/src/ui/docs/content/ask.md +51 -26
- package/src/ui/docs/content/aspect-ratio.md +22 -24
- package/src/ui/docs/content/audit.md +23 -19
- package/src/ui/docs/content/auth.md +38 -19
- package/src/ui/docs/content/avatar.md +54 -5
- package/src/ui/docs/content/badge.md +29 -9
- package/src/ui/docs/content/breadcrumb.md +21 -8
- package/src/ui/docs/content/button.md +73 -43
- package/src/ui/docs/content/calendar.md +134 -26
- package/src/ui/docs/content/card.md +41 -15
- package/src/ui/docs/content/carousel.md +37 -6
- package/src/ui/docs/content/chat.md +118 -37
- package/src/ui/docs/content/checkbox.md +73 -7
- package/src/ui/docs/content/cli.md +94 -55
- package/src/ui/docs/content/collapsible.md +43 -19
- package/src/ui/docs/content/command.md +30 -8
- package/src/ui/docs/content/composer.md +23 -8
- package/src/ui/docs/content/content.md +119 -27
- package/src/ui/docs/content/copyable.md +39 -8
- package/src/ui/docs/content/customization.md +55 -46
- package/src/ui/docs/content/cycle.md +33 -18
- package/src/ui/docs/content/data-products.md +115 -0
- package/src/ui/docs/content/data-state.md +20 -6
- package/src/ui/docs/content/data.md +20 -19
- package/src/ui/docs/content/detail.md +12 -5
- package/src/ui/docs/content/dialog.md +107 -24
- package/src/ui/docs/content/dictionary-value.md +4 -4
- package/src/ui/docs/content/dock.md +54 -19
- package/src/ui/docs/content/dot.md +35 -5
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/empty-value.md +20 -6
- package/src/ui/docs/content/empty.md +24 -3
- package/src/ui/docs/content/events.md +76 -16
- package/src/ui/docs/content/executions.md +9 -9
- package/src/ui/docs/content/field.md +21 -6
- package/src/ui/docs/content/getting-started.md +44 -34
- package/src/ui/docs/content/icon-picker.md +19 -13
- package/src/ui/docs/content/icon-tile.md +38 -15
- package/src/ui/docs/content/input-otp.md +58 -7
- package/src/ui/docs/content/input.md +22 -3
- package/src/ui/docs/content/item.md +53 -6
- package/src/ui/docs/content/kbd.md +5 -1
- package/src/ui/docs/content/label.md +13 -4
- package/src/ui/docs/content/log.md +5 -5
- package/src/ui/docs/content/markdown.md +5 -4
- package/src/ui/docs/content/mcp.md +49 -23
- package/src/ui/docs/content/menu.md +95 -10
- package/src/ui/docs/content/metric-card.md +12 -4
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +118 -12
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +55 -5
- package/src/ui/docs/content/popover.md +42 -5
- package/src/ui/docs/content/presentation.md +91 -47
- package/src/ui/docs/content/progress.md +27 -14
- package/src/ui/docs/content/queue.md +39 -21
- package/src/ui/docs/content/radio-group.md +26 -4
- package/src/ui/docs/content/router.md +29 -10
- package/src/ui/docs/content/runtime.md +90 -54
- package/src/ui/docs/content/scheduler.md +53 -21
- package/src/ui/docs/content/scroll-area.md +42 -35
- package/src/ui/docs/content/select.md +78 -33
- package/src/ui/docs/content/semantic-context.md +21 -2
- package/src/ui/docs/content/separator.md +15 -9
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +6 -1
- package/src/ui/docs/content/slider.md +37 -9
- package/src/ui/docs/content/spinner.md +4 -3
- package/src/ui/docs/content/split.md +19 -0
- package/src/ui/docs/content/storage.md +8 -9
- package/src/ui/docs/content/switch.md +10 -7
- package/src/ui/docs/content/table.md +46 -8
- package/src/ui/docs/content/tabs.md +14 -4
- package/src/ui/docs/content/testing.md +76 -31
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +48 -7
- package/src/ui/docs/content/toggle.md +3 -3
- package/src/ui/docs/content/tokens.md +15 -26
- package/src/ui/docs/content/tooltip.md +18 -2
- package/src/ui/docs/content/truncate.md +36 -6
- package/src/ui/docs/content/ui.md +74 -22
- package/src/ui/docs/content/upgrading.md +45 -36
- package/src/ui/docs/plugin.ts +36 -2
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +32 -32
- package/src/ui/react.tsx +1 -0
- package/src/vite/design.ts +16 -4
|
@@ -10,12 +10,13 @@ superfície, e a escolha entre regiões seccionadas e contínuas pertence a quem
|
|
|
10
10
|
Quando a própria região precisar de uma fronteira visível, use `framed`, descrito adiante. Pelo
|
|
11
11
|
mesmo motivo o `Content` não tem rodapé: um rodapé pertence à superfície que a hospeda.
|
|
12
12
|
|
|
13
|
-
No caso comum, prefira o shorthand:
|
|
13
|
+
No caso comum, prefira o shorthand. `count` mostra o total de itens ao lado do título:
|
|
14
14
|
|
|
15
15
|
```tsx preview col
|
|
16
16
|
render(
|
|
17
17
|
<Content
|
|
18
18
|
title="Dispositivos conectados"
|
|
19
|
+
count={3}
|
|
19
20
|
description="Sessões com acesso à sua conta."
|
|
20
21
|
actions={<Button variant="outline">Encerrar outras sessões</Button>}
|
|
21
22
|
>
|
|
@@ -30,21 +31,15 @@ render(
|
|
|
30
31
|
|
|
31
32
|
Use `framed` quando título e corpo precisarem formar uma única região delimitada. A moldura pertence
|
|
32
33
|
ao `Content`; componentes internos, como `DetailGroup`, continuam responsáveis somente pelo layout
|
|
33
|
-
e pela semântica dos próprios dados.
|
|
34
|
+
e pela semântica dos próprios dados. Por padrão, a borda permanece contínua e o header fica dentro
|
|
35
|
+
do recuo da região.
|
|
34
36
|
|
|
35
37
|
```tsx preview col
|
|
36
38
|
render(
|
|
37
39
|
<Content
|
|
38
40
|
framed
|
|
39
|
-
title=
|
|
40
|
-
|
|
41
|
-
<UserRound
|
|
42
|
-
className="size-4 text-muted-foreground"
|
|
43
|
-
aria-hidden="true"
|
|
44
|
-
/>
|
|
45
|
-
Cliente
|
|
46
|
-
</span>
|
|
47
|
-
}
|
|
41
|
+
title="Cliente"
|
|
42
|
+
media={<UserRound aria-hidden="true" />}
|
|
48
43
|
>
|
|
49
44
|
<DetailGroup columns={2}>
|
|
50
45
|
<DetailField label="Nome" value="Carla Souza" />
|
|
@@ -54,16 +49,66 @@ render(
|
|
|
54
49
|
);
|
|
55
50
|
```
|
|
56
51
|
|
|
52
|
+
## Header integrado à moldura
|
|
53
|
+
|
|
54
|
+
Quando uma região compacta precisar integrar visualmente o header à borda, use
|
|
55
|
+
`frameVariant="legend"`. O acabamento não muda a semântica: uma região de leitura continua sendo
|
|
56
|
+
uma `section`, o título continua sendo um heading e descrição, contador e ações permanecem
|
|
57
|
+
disponíveis.
|
|
58
|
+
|
|
59
|
+
```tsx preview col
|
|
60
|
+
render(
|
|
61
|
+
<Content
|
|
62
|
+
framed
|
|
63
|
+
frameVariant="legend"
|
|
64
|
+
title="Veículo"
|
|
65
|
+
media={<CarFront aria-hidden="true" />}
|
|
66
|
+
>
|
|
67
|
+
<DetailGroup columns={2}>
|
|
68
|
+
<DetailField label="Placa" value="ABC1D23" />
|
|
69
|
+
<DetailField label="Modelo" value="Corolla XEi" />
|
|
70
|
+
</DetailGroup>
|
|
71
|
+
</Content>,
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Para interromper a borda, o header pinta o título e as ações com o fundo da superfície que hospeda o
|
|
76
|
+
`Content`, lido da variável CSS `--surface-background`. `Card`, `Dialog` e `Drawer` já a declaram
|
|
77
|
+
com o fundo que pintam; fora deles, vale `--background`. Uma superfície com fundo próprio, como um
|
|
78
|
+
`Card` com `bg-*`, um `Popover` ou um contêiner da aplicação, precisa declarar a variável com o
|
|
79
|
+
mesmo fundo. Sem isso, a faixa atrás do título aparece com a cor da página.
|
|
80
|
+
|
|
81
|
+
```tsx preview col
|
|
82
|
+
render(
|
|
83
|
+
<Card className="bg-muted [--surface-background:var(--muted)]">
|
|
84
|
+
<CardBody>
|
|
85
|
+
<Content framed frameVariant="legend" title="Veículo">
|
|
86
|
+
<DetailGroup columns={2}>
|
|
87
|
+
<DetailField label="Placa" value="ABC1D23" />
|
|
88
|
+
<DetailField label="Modelo" value="Corolla XEi" />
|
|
89
|
+
</DetailGroup>
|
|
90
|
+
</Content>
|
|
91
|
+
</CardBody>
|
|
92
|
+
</Card>,
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
57
96
|
## Grupo de campos
|
|
58
97
|
|
|
59
98
|
Quando a região nomear um conjunto de campos relacionados, use `as="fieldset"`. O `Content`
|
|
60
99
|
renderiza o título como `legend`, preserva as props nativas do `fieldset` e mantém o mesmo contrato
|
|
61
100
|
de título e corpo. Essa escolha altera somente a semântica e o comportamento nativo; com `framed`,
|
|
62
|
-
a moldura permanece contínua.
|
|
101
|
+
a moldura permanece contínua. A mídia usa a mesma apresentação da `section`; contador, descrição e
|
|
102
|
+
ações não entram na legenda.
|
|
63
103
|
|
|
64
104
|
```tsx preview col
|
|
65
105
|
render(
|
|
66
|
-
<Content
|
|
106
|
+
<Content
|
|
107
|
+
as="fieldset"
|
|
108
|
+
framed
|
|
109
|
+
title="Proprietário"
|
|
110
|
+
media={<UserRound aria-hidden="true" />}
|
|
111
|
+
>
|
|
67
112
|
<FieldGroup>
|
|
68
113
|
<Field>
|
|
69
114
|
<FieldLabel htmlFor="owner-name">Nome</FieldLabel>
|
|
@@ -78,8 +123,9 @@ render(
|
|
|
78
123
|
);
|
|
79
124
|
```
|
|
80
125
|
|
|
81
|
-
|
|
82
|
-
|
|
126
|
+
O mesmo acabamento pode integrar a legenda nativa à borda. Declare `frameVariant="legend"` em
|
|
127
|
+
agrupamentos compactos, como as seções de filtros avançados; `as="fieldset"` continua sendo a
|
|
128
|
+
decisão semântica, e não uma forma de obter esse desenho.
|
|
83
129
|
|
|
84
130
|
```tsx preview col
|
|
85
131
|
render(
|
|
@@ -103,13 +149,16 @@ render(
|
|
|
103
149
|
|
|
104
150
|
Use os slots quando o header precisar de composição própria. A árvore aceita exatamente um
|
|
105
151
|
`ContentHeader` e um `ContentBody` como filhos diretos. O header exige um `ContentTitle` e aceita
|
|
106
|
-
uma descrição, um contador (`ContentMeta`) e uma região de ações. O
|
|
107
|
-
`Content`; `PageHeader` reconhece somente navegação, título e ações.
|
|
152
|
+
uma mídia (`ContentMedia`), uma descrição, um contador (`ContentMeta`) e uma região de ações. O
|
|
153
|
+
contador é próprio de `Content`; `PageHeader` reconhece somente navegação, título e ações.
|
|
108
154
|
|
|
109
155
|
```tsx preview col
|
|
110
156
|
render(
|
|
111
157
|
<Content level={2}>
|
|
112
158
|
<ContentHeader>
|
|
159
|
+
<ContentMedia>
|
|
160
|
+
<Monitor aria-hidden="true" />
|
|
161
|
+
</ContentMedia>
|
|
113
162
|
<ContentTitle>Dispositivos conectados</ContentTitle>
|
|
114
163
|
<ContentMeta>3</ContentMeta>
|
|
115
164
|
<ContentDescription>Sessões com acesso à sua conta.</ContentDescription>
|
|
@@ -132,28 +181,71 @@ do pai correto, filhos estruturais indiretos e a mistura de shorthand com compos
|
|
|
132
181
|
## Coleção nomeada
|
|
133
182
|
|
|
134
183
|
Quando a região principal da página é uma coleção, `Content` nomeia e governa essa coleção, enquanto
|
|
135
|
-
`ActionList` executa a consulta. Coloque criar, importar e outras ações sobre o conjunto completo em
|
|
184
|
+
[`ActionList`](/ui/action-list) executa a consulta. Coloque criar, importar e outras ações sobre o conjunto completo em
|
|
136
185
|
`ContentActions`. Busca, filtros, atualização e operações dependentes do recorte atual permanecem na
|
|
137
186
|
toolbar da lista. Essa divisão aproxima cada comando do objeto que ele afeta sem criar uma família
|
|
138
|
-
paralela de componentes `List*`.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
187
|
+
paralela de componentes `List*`.
|
|
188
|
+
|
|
189
|
+
Quando a coleção é o assunto da página, use `variant="page"` com `level={1}`: o `Content` assume a
|
|
190
|
+
escala do cabeçalho de [`Page`](/ui/page) e passa a ser o heading principal. As ações ficam no
|
|
191
|
+
extremo oposto ao título, como em qualquer variante.
|
|
192
|
+
|
|
193
|
+
```tsx preview col
|
|
194
|
+
render(
|
|
195
|
+
<Content level={1} variant="page" title="Clientes" actions={<Button>Criar cliente</Button>}>
|
|
196
|
+
<div className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
|
|
197
|
+
A lista de clientes ocupa o corpo.
|
|
198
|
+
</div>
|
|
199
|
+
</Content>,
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A criação usa um `Button` textual na variante padrão (`solid`) e no tamanho `default`, sem ícone,
|
|
204
|
+
nomeado `Criar {recurso}` (por exemplo, `Criar cliente`); o diálogo aberto pelo gatilho repete esse
|
|
205
|
+
título, e a edição usa `Editar {recurso}`.
|
|
206
|
+
|
|
207
|
+
Em uma [`Page`](/ui/page) com `layout="fill"`, a coleção pode ocupar a altura da página e rolar só os
|
|
208
|
+
resultados. `Content` e `ContentBody` repassam a altura até a [`ActionList`](/ui/action-list) sem
|
|
209
|
+
classes adicionais; um `Content` irmão, sem lista, mantém a altura do próprio conteúdo. A seção
|
|
210
|
+
“Ocupar a altura disponível” da `ActionList` mostra a composição completa.
|
|
142
211
|
|
|
143
212
|
## Propriedades de Content
|
|
144
213
|
|
|
145
214
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
146
215
|
| -------------- | ---------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
147
216
|
| `title` | `ReactNode` | | Forma curta: título da região (vira o heading ligado à `section`). |
|
|
217
|
+
| `media` | `ReactNode` | | Forma curta: ícone ou imagem que identifica a região antes do título. |
|
|
148
218
|
| `count` | `number` | | Forma curta: total de itens ao lado do título da região. |
|
|
149
219
|
| `description` | `ReactNode` | | Forma curta: frase de apoio sob o título. |
|
|
150
|
-
| `actions` | `ReactNode` | | Forma curta: ações sobre a região inteira
|
|
220
|
+
| `actions` | `ReactNode` | | Forma curta: ações sobre a região inteira, no extremo oposto ao título. |
|
|
151
221
|
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Nível semântico do heading, independente do destaque visual. |
|
|
152
222
|
| `variant` | `'page' \| 'section'` | `'section'` | Hierarquia visual: `page` reproduz o cabeçalho de `Page`; `section` é a região dentro de uma superfície. |
|
|
153
223
|
| `as` | `'section' \| 'fieldset'` | `'section'` | Usa uma região com heading ou um grupo de campos cujo título é a legenda nativa. |
|
|
154
224
|
| `framed` | `boolean` | `false` | Envolve o header e o body com a moldura estrutural da região. |
|
|
155
|
-
| `frameVariant` | `'default' \| 'legend'` | `'default'` |
|
|
225
|
+
| `frameVariant` | `'default' \| 'legend'` | `'default'` | Mantém a borda contínua ou integra visualmente o header à moldura. Não altera o elemento nem sua semântica. |
|
|
226
|
+
| `children` | `ReactNode` | | Corpo da região na forma curta, ou `ContentHeader` e `ContentBody` na composição explícita. |
|
|
227
|
+
|
|
228
|
+
As demais propriedades são as nativas de `section` ou, com `as="fieldset"`, de `fieldset`.
|
|
229
|
+
|
|
230
|
+
Na composição explícita de uma `section`, `ContentHeader` recebe `ContentMedia`, `ContentTitle`,
|
|
231
|
+
`ContentMeta`, `ContentDescription` e `ContentActions`. Em `fieldset`, recebe `ContentMedia` e
|
|
232
|
+
`ContentTitle`, preservando a apresentação enquanto a legenda continua nomeando somente o grupo de
|
|
233
|
+
campos. `ContentBody` recebe o conteúdo; nenhum desses slots aceita `title` ou `level` próprios — a
|
|
234
|
+
hierarquia é declarada em `Content`. `ContentHeader` não existe fora de `Content`.
|
|
235
|
+
|
|
236
|
+
## Propriedades de ContentMedia
|
|
237
|
+
|
|
238
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
239
|
+
| ----------- | ------------------------------------------------------------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
240
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'xs'` | Tamanho da moldura e do glifo na escala compartilhada dos controles. |
|
|
241
|
+
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | Declara o significado da cor. |
|
|
242
|
+
| `variant` | `'subtle' \| 'outline'` | `'subtle'` | Usa preenchimento tonal ou acrescenta borda. |
|
|
243
|
+
| `className` | `string` | | Ajusta a moldura sem substituir a escala. |
|
|
244
|
+
| `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. |
|
|
245
|
+
|
|
246
|
+
`ContentMedia` já é a moldura. Passe só o glifo e escolha `size`, `context` e `variant` no slot. Um
|
|
247
|
+
`IconTile` passado em `media` ou dentro de `ContentMedia` não vira moldura dentro de moldura: o slot
|
|
248
|
+
assume o lugar dele e herda o que ele declara. `media` com `null` ou `false` não desenha nada.
|
|
156
249
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
próprios — a hierarquia é declarada em `Content`. `ContentHeader` não existe fora de `Content`.
|
|
250
|
+
Em `fieldset`, a mídia é sempre decorativa: o `Content` a esconde da árvore de acessibilidade para
|
|
251
|
+
que o grupo seja anunciado apenas pelo título.
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
## Copiar por ícone
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use `Copyable` para que a pessoa copie um valor exato com um clique, como um identificador, uma
|
|
4
|
+
chave ou um endereço. Sem filhos, ele é um botão só com ícone: copia `value` e troca o ícone por um
|
|
5
|
+
check durante 1,5 segundo. Essa forma cabe em barras de ferramentas e células estreitas, ao lado do
|
|
6
|
+
valor exibido.
|
|
4
7
|
|
|
5
8
|
```tsx preview
|
|
6
9
|
<Copyable value="opus_sk_1a2b3c4d5e6f" className="text-muted-foreground hover:text-foreground" />
|
|
@@ -8,7 +11,8 @@ Sem filhos, é um botão-ícone: copia `value` e o ícone vira um check por ~1.5
|
|
|
8
11
|
|
|
9
12
|
## Com rótulo
|
|
10
13
|
|
|
11
|
-
Com filhos, o valor visível fica à esquerda e o ícone à direita — clicar no conjunto copia.
|
|
14
|
+
Com filhos, o valor visível fica à esquerda e o ícone à direita — clicar no conjunto copia. Borda,
|
|
15
|
+
fonte monoespaçada e demais detalhes visuais entram por `className`.
|
|
12
16
|
|
|
13
17
|
```tsx preview
|
|
14
18
|
<Copyable
|
|
@@ -19,21 +23,48 @@ Com filhos, o valor visível fica à esquerda e o ícone à direita — clicar n
|
|
|
19
23
|
</Copyable>
|
|
20
24
|
```
|
|
21
25
|
|
|
26
|
+
## Nome acessível
|
|
27
|
+
|
|
28
|
+
Com rótulo visível, o nome do botão é o próprio texto dos filhos. Quem usa comando de voz aciona o
|
|
29
|
+
botão dizendo o que vê, e o nome não muda durante a confirmação. Sem filhos, o botão se chama
|
|
30
|
+
“Copiar” e, durante a confirmação, “Copiado”.
|
|
31
|
+
|
|
32
|
+
A confirmação também é anunciada por leitores de tela: uma região de status, invisível na tela e
|
|
33
|
+
fora do botão, diz “Copiado” sem interromper a leitura em andamento.
|
|
34
|
+
|
|
35
|
+
Quando houver mais de um botão só com ícone na mesma tela, passe `aria-label` para distinguir o que
|
|
36
|
+
cada um copia, como “Copiar o ID do pedido”. Com rótulo visível, o `aria-label` raramente é
|
|
37
|
+
necessário; se usá-lo, comece pelo texto que a pessoa vê. Um `aria-label` próprio substitui os nomes
|
|
38
|
+
padrão, inclusive o “Copiado”, e o anúncio da confirmação continua.
|
|
39
|
+
|
|
22
40
|
## Duração do feedback
|
|
23
41
|
|
|
24
|
-
`feedbackMs` ajusta por quanto tempo a confirmação aparece; o padrão é 1500 milissegundos.
|
|
25
|
-
área de transferência não estiver disponível, a ação não produz efeito.
|
|
42
|
+
`feedbackMs` ajusta por quanto tempo a confirmação aparece; o padrão é 1500 milissegundos.
|
|
26
43
|
|
|
27
44
|
```tsx preview
|
|
28
|
-
<Copyable
|
|
29
|
-
|
|
45
|
+
<Copyable
|
|
46
|
+
value="https://opus.softize.dev/w/empresa-x"
|
|
47
|
+
feedbackMs={3000}
|
|
48
|
+
className="text-muted-foreground hover:text-foreground"
|
|
49
|
+
>
|
|
50
|
+
Copiar link
|
|
30
51
|
</Copyable>
|
|
31
52
|
```
|
|
32
53
|
|
|
54
|
+
A confirmação só aparece depois que a área de transferência aceita o texto. Se ela não estiver
|
|
55
|
+
disponível, como em uma página sem HTTPS, ou recusar a escrita, o ícone não muda.
|
|
56
|
+
|
|
33
57
|
## Propriedades de Copyable
|
|
34
58
|
|
|
35
59
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
36
60
|
|---|---|---|---|
|
|
37
|
-
| `value` | `string` | | O texto que vai para a área de transferência no clique. |
|
|
61
|
+
| `value` | `string` | obrigatório | O texto que vai para a área de transferência no clique. |
|
|
38
62
|
| `feedbackMs` | `number` | `1500` | Duração do estado “copiado”, em milissegundos. |
|
|
39
|
-
| `children` | `ReactNode` | | Rótulo ao lado do ícone
|
|
63
|
+
| `children` | `ReactNode` | | Rótulo visível ao lado do ícone. Quando presente, é o nome acessível do botão. |
|
|
64
|
+
| `aria-label` | `string` | sem filhos, `'Copiar'`, ou `'Copiado'` durante a confirmação | Nome acessível do botão. Substitui o nome vindo dos filhos e os nomes padrão. |
|
|
65
|
+
|
|
66
|
+
Os demais atributos vão para o elemento `button`. Um `onClick` próprio roda antes da cópia. O
|
|
67
|
+
botão é o único elemento que o Copyable coloca no contêiner: a região de status fica no fim do
|
|
68
|
+
`body`, visível apenas para leitores de tela, e é uma só para todos os Copyable da página. Ela
|
|
69
|
+
aparece com o primeiro Copyable montado e sai com o último, então uma tabela com um botão de copiar
|
|
70
|
+
por linha não multiplica regiões.
|
|
@@ -4,15 +4,16 @@ title: Customização
|
|
|
4
4
|
|
|
5
5
|
# Customização
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Quando a aplicação precisa de outra marca, outra largura ou outro comportamento, o Opus oferece
|
|
8
|
+
cinco alavancas, da mais global à mais pontual. Todas partem de compor e configurar os componentes
|
|
9
|
+
existentes; nenhuma exige copiar um componente para dentro do projeto e mantê-lo à parte. O que não
|
|
10
|
+
couber nelas evolui no próprio Opus, para valer em todos os projetos.
|
|
10
11
|
|
|
11
12
|
## 1 · Identidade por tokens
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
Para aplicar a marca, sobrescreva as variáveis CSS em `:root` depois de importar `theme.css`. Todos
|
|
15
|
+
os componentes acompanham a mudança, e o tema escuro também: a classe `.dark` redeclara as mesmas
|
|
16
|
+
variáveis.
|
|
16
17
|
|
|
17
18
|
```css
|
|
18
19
|
/* index.css do app — depois do import do tema. */
|
|
@@ -26,27 +27,20 @@ divergência declarada), para valer para casa toda.
|
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
O Opus deriva de `rounded-xs` a `rounded-2xl` dessa base. Assim, a marca muda a presença dos
|
|
29
|
-
cantos sem substituir as utilities escolhidas pelos componentes nem criar tokens por papel.
|
|
30
|
+
cantos sem substituir as utilities escolhidas pelos componentes nem criar tokens por papel. A lista
|
|
31
|
+
completa de variáveis está em [Tokens](/ui/tokens).
|
|
30
32
|
|
|
31
33
|
O tema não define `font-size` em `html`: a fonte raiz pertence ao navegador e à aplicação.
|
|
32
34
|
Quando o produto precisa de outra densidade, declare essa escolha no CSS do app. Por exemplo,
|
|
33
|
-
`html { font-size: 93.75%; }`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Layout desktop
|
|
37
|
-
|
|
38
|
-
O Opus oferece suporte a partir de `64rem` e não muda a composição conforme a largura da viewport.
|
|
39
|
-
Classes de tamanho, como `size="sm"`, escolhem a densidade do componente; não representam
|
|
40
|
-
breakpoints. Overflow e limites contra a janela continuam protegendo o conteúdo.
|
|
41
|
-
|
|
42
|
-
Não adicione uma adaptação isolada por viewport ou container em `className`. Uma futura
|
|
43
|
-
responsividade precisa começar pelo shell e formar um contrato comum para navegação, splits,
|
|
44
|
-
tabelas e componentes.
|
|
35
|
+
`html { font-size: 93.75%; }` reduz a raiz a 15/16 da fonte que o navegador oferece, e tudo que o
|
|
36
|
+
Opus mede em `rem` acompanha a proporção. O valor continua sendo uma escolha do app, não uma regra
|
|
37
|
+
da biblioteca.
|
|
45
38
|
|
|
46
39
|
## 2 · className em tudo
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
Todo componente aceita `className` e o combina com as próprias classes por tailwind-merge: quando
|
|
42
|
+
duas classes disputam a mesma propriedade, a do consumidor vence. Use essa alavanca para o layout
|
|
43
|
+
local, como largura, margem e grid, e não para repintar o visual dos componentes.
|
|
50
44
|
|
|
51
45
|
```tsx
|
|
52
46
|
<Button className="w-full">Continuar</Button>
|
|
@@ -56,73 +50,88 @@ tabelas e componentes.
|
|
|
56
50
|
|
|
57
51
|
## 3 · Recomposição estrutural
|
|
58
52
|
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
Os componentes são divididos em partes nomeadas, como `CardHeader` e `CardBody`, e a tela usa só as
|
|
54
|
+
que precisa. `asChild` faz o filho assumir o estilo e o comportamento do componente, e as funções
|
|
55
|
+
`*Variants` aplicam o visual de um componente a um elemento qualquer.
|
|
61
56
|
|
|
62
57
|
```tsx preview
|
|
63
58
|
<Button asChild variant="outline">
|
|
64
|
-
<a href="#" onClick={(e) => e.preventDefault()}
|
|
59
|
+
<a href="#" onClick={(e) => e.preventDefault()}>Link com aparência de botão</a>
|
|
65
60
|
</Button>
|
|
66
61
|
```
|
|
67
62
|
|
|
68
63
|
```tsx
|
|
69
|
-
//
|
|
64
|
+
// Partes: só o que a tela pede.
|
|
70
65
|
<Card>
|
|
71
66
|
<CardHeader><CardTitle>Workspace</CardTitle></CardHeader>
|
|
72
67
|
<CardBody>…</CardBody>
|
|
73
68
|
</Card>
|
|
74
69
|
|
|
75
|
-
// asChild: o
|
|
70
|
+
// asChild: o link assume o estilo e o comportamento do botão.
|
|
76
71
|
<Button asChild><a href="/docs">Abrir documentação</a></Button>
|
|
77
72
|
|
|
78
|
-
// buttonVariants:
|
|
73
|
+
// buttonVariants: o visual do botão em outro elemento.
|
|
79
74
|
<a className={buttonVariants({ variant: 'outline' })}>Link estilizado</a>
|
|
80
75
|
```
|
|
81
76
|
|
|
82
77
|
## 4 · Comportamento por props
|
|
83
78
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
Tudo o que abre ou seleciona aceita modo controlado (`open` e `onOpenChange`, `value` e
|
|
80
|
+
`onValueChange`). Posicionamento (`align`, `side`, `sideOffset`) e tempos (`delayDuration`) também
|
|
81
|
+
são props. A orquestração fica com a aplicação; o visual continua o dos componentes.
|
|
87
82
|
|
|
88
83
|
```tsx
|
|
89
84
|
const [open, setOpen] = useState(false)
|
|
90
85
|
|
|
91
86
|
<Dialog open={open} onOpenChange={setOpen}>…</Dialog>
|
|
92
|
-
<
|
|
87
|
+
<Dialog mode="alert">…</Dialog> {/* exige uma resposta explícita */}
|
|
93
88
|
<PopoverContent align="start" sideOffset={8} />
|
|
94
89
|
<TooltipProvider delayDuration={200}>…
|
|
95
90
|
```
|
|
96
91
|
|
|
92
|
+
Para exigir uma resposta, use `Dialog mode="alert"`: ele não fecha pelo clique externo e não mostra
|
|
93
|
+
o botão de fechar. Apenas esconder o botão com `showCloseButton={false}` não impede o fechamento por
|
|
94
|
+
Esc nem pelo clique externo. Veja [Dialog](/ui/dialog).
|
|
95
|
+
|
|
97
96
|
## 5 · Nos patterns, o contrato é a customização
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
Nos patterns — [`ActionForm`](/ui/action-form), [`ActionList`](/ui/action-list),
|
|
99
|
+
[`ActionView`](/ui/action-view) e [`ActionTrigger`](/ui/action-trigger) —, o contrato da action
|
|
100
|
+
determina campos, colunas e estados. A tela ajusta o que é dela: `fieldOptions` no formulário,
|
|
101
|
+
`columns` na lista, a função de render no `ActionView`. Para ir além, envolva o pattern em um
|
|
102
|
+
componente da feature em vez de copiá-lo.
|
|
102
103
|
|
|
103
104
|
```tsx
|
|
104
|
-
// O
|
|
105
|
+
// O formulário vem do contrato; a tela só fornece as opções que conhece.
|
|
105
106
|
<ActionForm
|
|
106
107
|
action={workspaceCreateContract}
|
|
107
|
-
fieldOptions={{
|
|
108
|
+
fieldOptions={{ ownerId: workspaceMembers }}
|
|
108
109
|
onSuccess={(d) => navigate(`/workspaces/${d.id}`)}
|
|
109
110
|
/>
|
|
110
111
|
|
|
111
|
-
//
|
|
112
|
+
// A lista recebe as colunas; a célula é render livre.
|
|
112
113
|
<ActionList action={listAction} input={{ q }} columns={[
|
|
113
114
|
{ key: 'name', header: 'Workspace', cell: (w) => w.name },
|
|
114
115
|
]} />
|
|
115
116
|
```
|
|
116
117
|
|
|
117
|
-
##
|
|
118
|
+
## Largura mínima e densidade
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
O Opus oferece suporte a partir de `64rem` e não muda a composição conforme a largura da viewport.
|
|
121
|
+
Props de tamanho, como `size="sm"`, escolhem a densidade do componente; não representam
|
|
122
|
+
breakpoints. Overflow e limites contra a janela continuam protegendo o conteúdo.
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
Não adicione em `className` uma adaptação isolada por viewport ou container. Uma responsividade
|
|
125
|
+
futura precisa começar pelo shell e formar um contrato comum para navegação, splits, tabelas e
|
|
126
|
+
componentes.
|
|
127
|
+
|
|
128
|
+
## O que fica fora das alavancas
|
|
129
|
+
|
|
130
|
+
Alguns detalhes fazem parte da identidade visual do Opus e são iguais em todo projeto: a faixa do
|
|
131
|
+
rodapé do dialog, a elevação no tema escuro, o escurecimento do botão ao ser pressionado
|
|
132
|
+
(`active:brightness-90`) e a seta do tooltip. Eles não são configuráveis pela aplicação.
|
|
125
133
|
|
|
126
|
-
Se uma necessidade real não cabe nas alavancas (tokens
|
|
127
|
-
contrato),
|
|
128
|
-
declarada
|
|
134
|
+
Se uma necessidade real não cabe nas alavancas (tokens, `className`, partes e `asChild`, props e
|
|
135
|
+
contrato), não crie uma variação local. Proponha a evolução do componente no Opus, com a
|
|
136
|
+
divergência declarada conforme a skill `build-opus-ui`; assim a mudança vale para todos os projetos
|
|
137
|
+
e passa a aparecer nesta documentação.
|
|
@@ -1,36 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Evolução contínua
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
# Evolução contínua
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
Use este fluxo quando o seu projeto precisar de algo que o Opus ainda não oferece ou encontrar um
|
|
8
|
+
defeito nele. O Opus incorpora um comportamento quando ele se repete em mais de um projeto; então o
|
|
9
|
+
projeto troca a implementação local pelo import. Preferência de estilo e conveniência isolada não
|
|
10
|
+
são motivo para incorporar uma peça.
|
|
4
11
|
|
|
5
|
-
##
|
|
12
|
+
## Registrar um apontamento
|
|
6
13
|
|
|
7
|
-
Cada apontamento vira uma Issue
|
|
14
|
+
Cada apontamento vira uma Issue em
|
|
15
|
+
[github.com/softize-dev/opus](https://github.com/softize-dev/opus/issues). Antes de abrir uma nova,
|
|
16
|
+
procure uma Issue sobre o mesmo comportamento e comente nela. Há dois tipos:
|
|
8
17
|
|
|
9
|
-
- **`enhancement
|
|
10
|
-
|
|
18
|
+
- **`enhancement`:** código genérico que serviria a qualquer projeto. Antes de apontar, confira se o
|
|
19
|
+
Opus já oferece o recurso no inventário de componentes, nos exports ou nesta documentação; se
|
|
20
|
+
oferecer, use-o.
|
|
21
|
+
- **`bug`:** defeito ou limitação do próprio Opus, encontrado durante o uso.
|
|
11
22
|
|
|
12
|
-
Descreva cada apontamento com estes campos
|
|
23
|
+
Descreva cada apontamento com estes campos, como nos exemplos:
|
|
13
24
|
|
|
14
25
|
```text
|
|
15
26
|
{"type": "enhancement", "behavior": "o comportamento em uma frase.", "file": "src/onde-vive.ts", "kind": "ui|sdk|driver|infra"}
|
|
16
|
-
{"type": "bug", "behavior": "o que quebra ou falta.", "file": "onde se manifesta.", "note": "
|
|
27
|
+
{"type": "bug", "behavior": "o que quebra ou falta.", "file": "onde se manifesta.", "note": "como reproduzir e o contorno aplicado."}
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
|
|
30
|
+
O pacote não coleta apontamentos do seu projeto automaticamente.
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
## Seguir sem esperar a correção
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
2. **Entregue** a feature com o workaround.
|
|
25
|
-
3. **Aponte** na Issue e siga em frente.
|
|
34
|
+
Uma lacuna ou um defeito do Opus não precisa bloquear a entrega:
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
1. **Contorne no projeto:** componha um wrapper local. O Opus é distribuído como código-fonte, então
|
|
37
|
+
qualquer superfície dele pode ser envolvida. Não edite `node_modules`, porque a alteração se
|
|
38
|
+
perde na próxima instalação.
|
|
39
|
+
2. **Entregue** a funcionalidade com o contorno.
|
|
40
|
+
3. **Registre** o apontamento na Issue.
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
A correção no Opus acontece em paralelo. Enquanto ela não sai, o contorno continua no projeto.
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
## Como o apontamento termina
|
|
32
45
|
|
|
33
|
-
- **
|
|
34
|
-
|
|
46
|
+
- Um **enhancement** aceito é implementado no Opus e publicado em uma nova versão. Ao
|
|
47
|
+
[atualizar o Opus](/docs/upgrading), o projeto troca o contorno pelo import.
|
|
48
|
+
- Um **bug** vira uma correção com entrada no `CHANGELOG`; na atualização, o contorno sai.
|
|
35
49
|
|
|
36
|
-
A
|
|
50
|
+
A decisão sobre o que entra no Opus fica com a Softize, que mantém o pacote. O registro das peças
|
|
51
|
+
incorporadas está no `PROMOTED.md` do pacote.
|