@softize/opus 22.2.0 → 24.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 (115) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/bin/cli.mjs +3 -9
  3. package/bin/lib/check.mjs +242 -49
  4. package/bin/lib/copy.mjs +17 -35
  5. package/bin/lib/gen-runner.mjs +2 -0
  6. package/bin/lib/gen-stubs.mjs +3 -1
  7. package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +1 -1
  8. package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +6 -0
  9. package/docs/adr/0032-tab-baseline-respects-the-container-inset.md +16 -12
  10. package/docs/adr/0034-tabs-can-own-a-frame-without-owning-a-surface.md +8 -6
  11. package/docs/adr/0036-tabs-declares-trigger-distribution.md +11 -8
  12. package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
  13. package/docs/adr/0038-content-level-defines-primary-presentation.md +62 -0
  14. package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +62 -0
  15. package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +59 -0
  16. package/docs/adr/0042-toggle-group-shares-the-grouped-control-grammar.md +79 -0
  17. package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +73 -0
  18. package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +133 -0
  19. package/docs/adr/0045-surface-replaces-content-and-card.md +230 -0
  20. package/docs/adr/0046-structural-body-owns-content-rhythm.md +103 -0
  21. package/docs/adr/0047-pane-is-only-a-split-layout-unit.md +71 -0
  22. package/docs/adr/0048-modal-intro-separates-content-from-chrome.md +34 -0
  23. package/docs/code-style.md +1 -1
  24. package/package.json +5 -1
  25. package/registry/skills/build-opus-ui/SKILL.md +11 -11
  26. package/registry/skills/build-opus-ui/references/evaluations.md +10 -9
  27. package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -16
  28. package/registry/skills/create-opus-action/references/contract-and-binding.md +3 -3
  29. package/registry/templates/app/src/App.tsx +24 -12
  30. package/src/core/contracts.ts +1 -1
  31. package/src/core/runtime.ts +4 -37
  32. package/src/core/types.ts +5 -23
  33. package/src/design/index.ts +14 -3
  34. package/src/testing/index.ts +6 -26
  35. package/src/ui/appearance.ts +149 -0
  36. package/src/ui/components/patterns/action-form-card.tsx +46 -33
  37. package/src/ui/components/patterns/action-list-dialog.tsx +2 -4
  38. package/src/ui/components/patterns/confirm.tsx +3 -1
  39. package/src/ui/components/patterns/fill-region.ts +8 -7
  40. package/src/ui/components/patterns/form-dialog.tsx +1 -3
  41. package/src/ui/components/patterns/form.tsx +8 -7
  42. package/src/ui/components/patterns/list.tsx +64 -14
  43. package/src/ui/components/patterns/page.tsx +15 -11
  44. package/src/ui/components/patterns/pane.tsx +9 -240
  45. package/src/ui/components/patterns/presentation.tsx +14 -14
  46. package/src/ui/components/patterns/sidebar.tsx +7 -17
  47. package/src/ui/components/patterns/split.tsx +111 -59
  48. package/src/ui/components/patterns/{surface-header.tsx → structural-header.tsx} +50 -19
  49. package/src/ui/components/patterns/structural-media.tsx +26 -0
  50. package/src/ui/components/patterns/surface-anatomy.ts +71 -61
  51. package/src/ui/components/patterns/surface-assistant.tsx +2 -4
  52. package/src/ui/components/patterns/surface.tsx +620 -0
  53. package/src/ui/components/patterns/trigger.tsx +11 -9
  54. package/src/ui/components/primitives/ask.tsx +155 -83
  55. package/src/ui/components/primitives/button.tsx +11 -9
  56. package/src/ui/components/primitives/checkbox.tsx +8 -8
  57. package/src/ui/components/primitives/command.tsx +1 -1
  58. package/src/ui/components/primitives/composer.tsx +47 -45
  59. package/src/ui/components/primitives/detail.tsx +9 -2
  60. package/src/ui/components/primitives/dialog.tsx +60 -26
  61. package/src/ui/components/primitives/drawer.tsx +58 -12
  62. package/src/ui/components/primitives/icon-picker.tsx +93 -70
  63. package/src/ui/components/primitives/input-group.tsx +35 -28
  64. package/src/ui/components/primitives/input-otp.tsx +16 -16
  65. package/src/ui/components/primitives/input.tsx +20 -10
  66. package/src/ui/components/primitives/item.tsx +9 -12
  67. package/src/ui/components/primitives/menu.tsx +44 -65
  68. package/src/ui/components/primitives/metric-card.tsx +75 -59
  69. package/src/ui/components/primitives/radio-group.tsx +9 -9
  70. package/src/ui/components/primitives/select.tsx +344 -209
  71. package/src/ui/components/primitives/switch.tsx +10 -10
  72. package/src/ui/components/primitives/table.tsx +29 -24
  73. package/src/ui/components/primitives/tabs.tsx +58 -88
  74. package/src/ui/components/primitives/textarea.tsx +6 -6
  75. package/src/ui/components/primitives/toggle-group-context.ts +24 -0
  76. package/src/ui/components/primitives/toggle-group.tsx +116 -74
  77. package/src/ui/components/primitives/toggle.tsx +82 -16
  78. package/src/ui/docs/DocBrowser.tsx +77 -69
  79. package/src/ui/docs/content/action-form-card.md +9 -9
  80. package/src/ui/docs/content/action-list.md +85 -70
  81. package/src/ui/docs/content/action-view.md +33 -18
  82. package/src/ui/docs/content/actions.md +6 -7
  83. package/src/ui/docs/content/button.md +1 -1
  84. package/src/ui/docs/content/communication.md +1 -1
  85. package/src/ui/docs/content/customization.md +64 -11
  86. package/src/ui/docs/content/detail.md +8 -6
  87. package/src/ui/docs/content/dialog.md +100 -58
  88. package/src/ui/docs/content/drawer.md +63 -19
  89. package/src/ui/docs/content/field.md +14 -15
  90. package/src/ui/docs/content/icon-tile.md +14 -13
  91. package/src/ui/docs/content/page.md +34 -26
  92. package/src/ui/docs/content/pane.md +41 -290
  93. package/src/ui/docs/content/presentation.md +3 -3
  94. package/src/ui/docs/content/semantic-context.md +0 -1
  95. package/src/ui/docs/content/separator.md +6 -6
  96. package/src/ui/docs/content/sidebar.md +68 -266
  97. package/src/ui/docs/content/skeleton.md +20 -18
  98. package/src/ui/docs/content/split.md +14 -14
  99. package/src/ui/docs/content/surface.md +513 -0
  100. package/src/ui/docs/content/tabs.md +82 -83
  101. package/src/ui/docs/content/testing.md +2 -3
  102. package/src/ui/docs/content/toggle.md +120 -63
  103. package/src/ui/docs/content/tokens.md +124 -40
  104. package/src/ui/docs/registry.tsx +4 -6
  105. package/src/ui/drivers/react.tsx +4 -3
  106. package/src/ui/index.ts +30 -27
  107. package/src/ui/meta.ts +15 -27
  108. package/src/ui/react.tsx +42 -46
  109. package/src/ui/theme.css +311 -64
  110. package/src/vite/design.ts +23 -21
  111. package/src/ui/components/patterns/content-header.tsx +0 -532
  112. package/src/ui/components/patterns/surface-media.tsx +0 -69
  113. package/src/ui/components/primitives/card.tsx +0 -191
  114. package/src/ui/docs/content/card.md +0 -292
  115. package/src/ui/docs/content/content.md +0 -276
@@ -1,7 +1,7 @@
1
1
  ## Alternar painéis relacionados
2
2
 
3
- Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de cada `TabsTrigger`
4
- corresponde ao `TabsContent` que ele abre. `defaultValue` define a aba inicial no modo não
3
+ Use `Tabs` para alternar painéis relacionados no mesmo contexto. O `value` de cada `TabsListItem`
4
+ corresponde ao `TabsPanel` que ele abre. `defaultValue` define a aba inicial no modo não
5
5
  controlado.
6
6
 
7
7
  `icon` posiciona um ícone decorativo antes do rótulo e normaliza SVGs para `1rem`. Quando a aba
@@ -10,31 +10,31 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
10
10
  ```tsx preview col
11
11
  <Tabs defaultValue="sessions">
12
12
  <TabsList>
13
- <TabsTrigger value="overview" icon={<LayoutDashboard />}>
13
+ <TabsListItem value="overview" icon={<LayoutDashboard />}>
14
14
  Visão geral
15
- </TabsTrigger>
16
- <TabsTrigger value="sessions" icon={<History />}>
15
+ </TabsListItem>
16
+ <TabsListItem value="sessions" icon={<History />}>
17
17
  Sessões
18
- </TabsTrigger>
19
- <TabsTrigger value="skills" icon={<Settings />}>
18
+ </TabsListItem>
19
+ <TabsListItem value="skills" icon={<Settings />}>
20
20
  Skills
21
- </TabsTrigger>
21
+ </TabsListItem>
22
22
  </TabsList>
23
- <TabsContent value="overview">
23
+ <TabsPanel value="overview">
24
24
  <p className="text-sm text-muted-foreground">
25
25
  Resumo do workspace Empresa X.
26
26
  </p>
27
- </TabsContent>
28
- <TabsContent value="sessions">
27
+ </TabsPanel>
28
+ <TabsPanel value="sessions">
29
29
  <p className="text-sm text-muted-foreground">
30
30
  3 sessões nesta semana, 1 em andamento.
31
31
  </p>
32
- </TabsContent>
33
- <TabsContent value="skills">
32
+ </TabsPanel>
33
+ <TabsPanel value="skills">
34
34
  <p className="text-sm text-muted-foreground">
35
35
  6 skills vinculadas aos agentes do workspace.
36
36
  </p>
37
- </TabsContent>
37
+ </TabsPanel>
38
38
  </Tabs>
39
39
  ```
40
40
 
@@ -42,25 +42,25 @@ mostrar somente o ícone, declare `aria-label` para preservar seu nome acessíve
42
42
 
43
43
  Use `framed` quando a navegação e seus painéis precisarem formar um único bloco. A propriedade
44
44
  reúne borda, raio e recorte no próprio `Tabs`, mas não aplica fundo: o bloco herda a
45
- superfície em que foi composto. Por isso, não envolva `Tabs` em um `Card` somente para obter essa
45
+ superfície em que foi composto. Por isso, não envolva `Tabs` em um `Surface` somente para obter essa
46
46
  moldura.
47
47
 
48
48
  ```tsx preview col
49
49
  <Tabs defaultValue="overview" framed className="gap-0">
50
- <TabsList variant="line" className="px-2 pt-2">
51
- <TabsTrigger value="overview">Visão geral</TabsTrigger>
52
- <TabsTrigger value="history">Histórico</TabsTrigger>
50
+ <TabsList>
51
+ <TabsListItem value="overview">Visão geral</TabsListItem>
52
+ <TabsListItem value="history">Histórico</TabsListItem>
53
53
  </TabsList>
54
- <TabsContent value="overview" className="p-4">
54
+ <TabsPanel value="overview" className="p-4">
55
55
  <p className="text-sm text-muted-foreground">
56
56
  Resumo do workspace Empresa X.
57
57
  </p>
58
- </TabsContent>
59
- <TabsContent value="history" className="p-4">
58
+ </TabsPanel>
59
+ <TabsPanel value="history" className="p-4">
60
60
  <p className="text-sm text-muted-foreground">
61
61
  Alterações recentes do workspace.
62
62
  </p>
63
- </TabsContent>
63
+ </TabsPanel>
64
64
  </Tabs>
65
65
  ```
66
66
 
@@ -73,127 +73,126 @@ altera os painéis associados.
73
73
 
74
74
  ```tsx preview col
75
75
  <Tabs defaultValue="overview" distribution="equal">
76
- <TabsList variant="line">
77
- <TabsTrigger value="overview">Visão geral</TabsTrigger>
78
- <TabsTrigger value="contacts">Contatos</TabsTrigger>
79
- <TabsTrigger value="history">Histórico</TabsTrigger>
76
+ <TabsList>
77
+ <TabsListItem value="overview">Visão geral</TabsListItem>
78
+ <TabsListItem value="contacts">Contatos</TabsListItem>
79
+ <TabsListItem value="history">Histórico</TabsListItem>
80
80
  </TabsList>
81
- <TabsContent value="overview">Resumo do atendimento.</TabsContent>
82
- <TabsContent value="contacts">Tentativas de contato.</TabsContent>
83
- <TabsContent value="history">Alterações registradas.</TabsContent>
81
+ <TabsPanel value="overview">Resumo do atendimento.</TabsPanel>
82
+ <TabsPanel value="contacts">Tentativas de contato.</TabsPanel>
83
+ <TabsPanel value="history">Alterações registradas.</TabsPanel>
84
84
  </Tabs>
85
85
  ```
86
86
 
87
- ## Variante line
87
+ ## Linha de navegação
88
88
 
89
- Use `variant="line"` em `TabsList` quando a navegação precisar de uma linha-base, como em um
90
- cabeçalho. Na orientação horizontal, a lista ocupa toda a largura disponível e a aba ativa é
91
- marcada sobre essa linha, em vez de usar uma superfície preenchida. A linha respeita o recuo do
92
- contêiner; dentro de `DrawerBody` e `DialogBody`, ela acompanha o alinhamento do restante do corpo
93
- sem alcançar as laterais da superfície.
89
+ `TabsList` usa uma linha-base porque representa navegação entre painéis, não uma escolha de valor.
90
+ Na orientação horizontal, a lista ocupa toda a largura disponível e a aba ativa é marcada sobre
91
+ essa linha. A linha respeita o recuo do contêiner; dentro de `DrawerBody` e `DialogBody`, ela
92
+ acompanha o alinhamento do restante do corpo sem alcançar as laterais da superfície.
94
93
  Cada item contém uma área interativa com a mesma altura de um Button do tamanho correspondente. A
95
94
  área permanece transparente e recebe fundo apenas no hover; a aba ativa é indicada pela linha
96
- primária inferior. Um respiro de `0.375rem` separa o controle dessa linha; em `Tabs framed`, ele
97
- aumenta para `0.5rem` e acompanha o padding superior da moldura.
95
+ primária inferior. Um respiro de `0.375rem` separa o controle dessa linha. Em `Tabs framed`, a
96
+ lista recebe `0.25rem` nas laterais e no topo, e a mesma medida separa o controle da linha.
98
97
 
99
98
  ```tsx preview col
100
99
  <Tabs defaultValue="agents">
101
- <TabsList variant="line">
102
- <TabsTrigger value="agents">Agentes</TabsTrigger>
103
- <TabsTrigger value="repos">Repositórios</TabsTrigger>
104
- <TabsTrigger value="settings">Configurações</TabsTrigger>
100
+ <TabsList>
101
+ <TabsListItem value="agents">Agentes</TabsListItem>
102
+ <TabsListItem value="repos">Repositórios</TabsListItem>
103
+ <TabsListItem value="settings">Configurações</TabsListItem>
105
104
  </TabsList>
106
- <TabsContent value="agents">
105
+ <TabsPanel value="agents">
107
106
  <p className="text-sm text-muted-foreground">
108
107
  developer, reviewer e designer ativos.
109
108
  </p>
110
- </TabsContent>
111
- <TabsContent value="repos">
109
+ </TabsPanel>
110
+ <TabsPanel value="repos">
112
111
  <p className="text-sm text-muted-foreground">
113
112
  empresa-x-api e empresa-x-web vinculados.
114
113
  </p>
115
- </TabsContent>
116
- <TabsContent value="settings">
114
+ </TabsPanel>
115
+ <TabsPanel value="settings">
117
116
  <p className="text-sm text-muted-foreground">Preferências do workspace.</p>
118
- </TabsContent>
117
+ </TabsPanel>
119
118
  </Tabs>
120
119
  ```
121
120
 
122
121
  ## Vertical
123
122
 
124
- Com `orientation="vertical"`, a lista forma uma coluna. O exemplo usa a variante padrão; com
125
- `variant="line"`, a marca da aba ativa passa para a lateral do gatilho.
123
+ Com `orientation="vertical"`, a lista forma uma coluna e a marca da aba ativa passa para a lateral
124
+ do gatilho.
126
125
 
127
126
  ```tsx preview col
128
127
  <Tabs defaultValue="prompt" orientation="vertical">
129
128
  <TabsList>
130
- <TabsTrigger value="prompt">Prompt</TabsTrigger>
131
- <TabsTrigger value="skills">Skills</TabsTrigger>
132
- <TabsTrigger value="history">Histórico</TabsTrigger>
129
+ <TabsListItem value="prompt">Prompt</TabsListItem>
130
+ <TabsListItem value="skills">Skills</TabsListItem>
131
+ <TabsListItem value="history">Histórico</TabsListItem>
133
132
  </TabsList>
134
- <TabsContent value="prompt">
133
+ <TabsPanel value="prompt">
135
134
  <p className="text-sm text-muted-foreground">
136
135
  O prompt do agente developer.
137
136
  </p>
138
- </TabsContent>
139
- <TabsContent value="skills">
137
+ </TabsPanel>
138
+ <TabsPanel value="skills">
140
139
  <p className="text-sm text-muted-foreground">
141
140
  Skills vinculadas a este agente.
142
141
  </p>
143
- </TabsContent>
144
- <TabsContent value="history">
142
+ </TabsPanel>
143
+ <TabsPanel value="history">
145
144
  <p className="text-sm text-muted-foreground">
146
145
  Sessões anteriores deste agente.
147
146
  </p>
148
- </TabsContent>
147
+ </TabsPanel>
149
148
  </Tabs>
150
149
  ```
151
150
 
152
151
  ## Tamanho
153
152
 
154
- `size="sm"` no `Tabs` reduz a altura da lista de `2.25rem` para `2rem`. É o par do `sm` de `Button` e
155
- `Select` para uma fileira densa, como uma toolbar ou um cabeçalho, em que o segmento não deve ficar
156
- mais alto que os elementos vizinhos.
153
+ `size="sm"` no `Tabs` reduz a área interativa de `2.25rem` para `2rem`. É o par do `sm` de `Button` e
154
+ `Select` para uma fileira densa, como uma toolbar ou um cabeçalho.
157
155
 
158
156
  ```tsx preview col
159
157
  <Tabs defaultValue="preview" size="sm">
160
158
  <TabsList>
161
- <TabsTrigger value="preview">Preview</TabsTrigger>
162
- <TabsTrigger value="code">Código</TabsTrigger>
163
- <TabsTrigger value="ai">IA</TabsTrigger>
159
+ <TabsListItem value="preview">Preview</TabsListItem>
160
+ <TabsListItem value="code">Código</TabsListItem>
161
+ <TabsListItem value="ai">IA</TabsListItem>
164
162
  </TabsList>
165
163
  </Tabs>
166
164
  ```
167
165
 
168
166
  ## Propriedades de Tabs
169
167
 
170
- | Propriedade | Tipo | Padrão | Descrição |
171
- | --------------- | ---------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
172
- | `defaultValue` | `string` | | Aba inicial no modo não controlado. |
173
- | `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
174
- | `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
175
- | `size` | `'default' \| 'sm'` | `'default'` | Altura da lista na escala única dos controles (2.25 e 2rem), aplicada em `TabsList`. Não altera a altura de `line` horizontal, que acompanha o conteúdo. |
176
- | `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
177
- | `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
178
- | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
168
+ | Propriedade | Tipo | Padrão | Descrição |
169
+ | --------------- | ---------------------------- | -------------- | --------------------------------------------------------------------------------------------- |
170
+ | `defaultValue` | `string` | | Aba inicial no modo não controlado. |
171
+ | `value` | `string` | | Aba ativa no modo controlado. Use com `onValueChange`. |
172
+ | `onValueChange` | `(value: string) => void` | | Chamado quando a pessoa seleciona outra aba. |
173
+ | `size` | `'default' \| 'sm'` | `'default'` | Altura da área interativa na escala única dos controles (2.25 e 2rem), aplicada aos gatilhos. |
174
+ | `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou divide igualmente a largura disponível entre os gatilhos. |
175
+ | `framed` | `boolean` | `false` | Aplica borda, raio e recorte ao conjunto sem definir fundo ou foreground. |
176
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Direção da lista de abas. |
179
177
 
180
178
  ## Propriedades de TabsList
181
179
 
182
- | Propriedade | Tipo | Padrão | Descrição |
183
- | ----------- | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- |
184
- | `variant` | `'default' \| 'line'` | `'default'` | `default` usa uma superfície preenchida; `line` cria uma linha-base horizontal de largura total e marca nela a aba ativa. |
180
+ | Propriedade | Tipo | Padrão | Descrição |
181
+ | ------------ | -------- | ------ | ----------------------------------------------------------------- |
182
+ | `aria-label` | `string` | | Nomeia a navegação quando não houver um rótulo visível associado. |
183
+ | `className` | `string` | | Ajusta a composição externa sem substituir a linha de navegação. |
185
184
 
186
- ## Propriedades de TabsTrigger
185
+ ## Propriedades de TabsListItem
187
186
 
188
187
  | Propriedade | Tipo | Padrão | Descrição |
189
188
  | ----------- | ----------- | ------- | ----------------------------------------------------------------------------------------------- |
190
- | `value` | `string` | | Identificador que associa o gatilho ao `TabsContent` correspondente. |
189
+ | `value` | `string` | | Identificador que associa o gatilho ao `TabsPanel` correspondente. |
191
190
  | `icon` | `ReactNode` | | Ícone decorativo antes do rótulo, em `1rem`. Em uma aba sem texto, declare também `aria-label`. |
192
191
  | `disabled` | `boolean` | `false` | Impede a seleção da aba. |
193
192
 
194
- ## Propriedades de TabsContent
193
+ ## Propriedades de TabsPanel
195
194
 
196
- | Propriedade | Tipo | Padrão | Descrição |
197
- | ------------ | -------- | ------ | ---------------------------------------------------------------------- |
198
- | `value` | `string` | | Identificador do painel, igual ao `value` do `TabsTrigger` que o abre. |
199
- | `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
195
+ | Propriedade | Tipo | Padrão | Descrição |
196
+ | ------------ | -------- | ------ | ----------------------------------------------------------------------- |
197
+ | `value` | `string` | | Identificador do painel, igual ao `value` do `TabsListItem` que o abre. |
198
+ | `forceMount` | `true` | | Mantém o painel montado mesmo quando outra aba está ativa. |
@@ -90,9 +90,8 @@ expect(emitted).toEqual([{ event: 'task.done', data: { id: '1' } }])
90
90
 
91
91
  Os dados que o modo design mostra não são comportamento da action e não precisam de teste de
92
92
  unidade: o plugin `opusDesign()` os gera a partir do contrato (ver
93
- [Actions](/docs/actions)). Enquanto o `mockHandler` existir, obsoleto desde a 22.1.0,
94
- `runAction` com `OPUS_MODE=design` ainda o executa, como o runtime. Teste a action ligada ao
95
- handler em vez dele.
93
+ [Actions](/docs/actions)). `runAction` executa somente a action ligada ao handler; use cenários do
94
+ plugin para respostas específicas do modo design.
96
95
 
97
96
  ## memStorage
98
97
 
@@ -14,24 +14,20 @@ inicial no modo não controlado; o conteúdo pode ser texto ou ícone.
14
14
  Use `pressed` e `onPressedChange` quando o estado pertencer ao consumidor.
15
15
 
16
16
  ```tsx preview
17
- const [readOnly, setReadOnly] = useState(true)
17
+ const [readOnly, setReadOnly] = useState(true);
18
18
 
19
19
  render(
20
- <Toggle
21
- variant="outline"
22
- pressed={readOnly}
23
- onPressedChange={setReadOnly}
24
- >
20
+ <Toggle variant="outline" pressed={readOnly} onPressedChange={setReadOnly}>
25
21
  <Eye />
26
22
  Somente leitura
27
23
  </Toggle>,
28
- )
24
+ );
29
25
  ```
30
26
 
31
27
  ## Variantes e tamanhos
32
28
 
33
- Na variante `default`, o fundo aparece somente quando o controle está ativo; `outline` mantém a
34
- borda. `size` ajusta a altura.
29
+ Na variante `default`, o fundo aparece somente quando o controle está ativo. `outline` mantém a
30
+ borda e usa um fundo sutil distinto dela no estado ativo. `size` ajusta a altura.
35
31
 
36
32
  ```tsx preview
37
33
  <Toggle aria-label="Quebra de linha">
@@ -64,15 +60,15 @@ borda. `size` ajusta a altura.
64
60
 
65
61
  ## Propriedades de Toggle
66
62
 
67
- | Propriedade | Tipo | Padrão | Descrição |
68
- |---|---|---|---|
69
- | `pressed` | `boolean` | | Estado no modo controlado. Use com `onPressedChange`. |
70
- | `onPressedChange` | `(pressed: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
71
- | `defaultPressed` | `boolean` | `false` | Estado inicial no modo não controlado. |
72
- | `variant` | `'default' \| 'outline'` | `'default'` | `default` não tem borda e só ganha fundo quando ativo; `outline` mantém a borda. |
73
- | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Altura na escala única dos controles (2 · 2.25 · 2.5rem) — sm para toolbar densa, lg para alvo mais confortável. |
74
- | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do controle; `pill` arredonda as extremidades. |
75
- | `disabled` | `boolean` | `false` | Esmaece e bloqueia o clique, preservando o estado visual. |
63
+ | Propriedade | Tipo | Padrão | Descrição |
64
+ | ----------------- | ---------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------- |
65
+ | `pressed` | `boolean` | | Estado no modo controlado. Use com `onPressedChange`. |
66
+ | `onPressedChange` | `(pressed: boolean) => void` | | Chamado a cada alternância, com o novo estado. |
67
+ | `defaultPressed` | `boolean` | `false` | Estado inicial no modo não controlado. |
68
+ | `variant` | `'default' \| 'outline'` | `'default'` | `default` não tem borda e só ganha fundo quando ativo; `outline` mantém a borda. |
69
+ | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Altura na escala única dos controles (2 · 2.25 · 2.5rem) — sm para toolbar densa, lg para alvo mais confortável. |
70
+ | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do controle; `pill` arredonda as extremidades. |
71
+ | `disabled` | `boolean` | `false` | Esmaece e bloqueia o clique, preservando o estado visual. |
76
72
 
77
73
  ## ToggleGroup
78
74
 
@@ -82,96 +78,157 @@ relacionados. `type="single"` mantém um item ativo; `type="multiple"` aceita v
82
78
  ### Escolha única
83
79
 
84
80
  ```tsx preview
85
- const [view, setView] = useState('sessions')
81
+ const [view, setView] = useState("sessions");
86
82
 
87
83
  render(
88
- <ToggleGroup type="single" value={view} onValueChange={(v) => v && setView(v)}>
89
- <ToggleGroupItem value="overview">Visão geral</ToggleGroupItem>
90
- <ToggleGroupItem value="sessions">Sessões</ToggleGroupItem>
91
- <ToggleGroupItem value="skills">Habilidades</ToggleGroupItem>
84
+ <ToggleGroup
85
+ type="single"
86
+ value={view}
87
+ onValueChange={(v) => v && setView(v)}
88
+ >
89
+ <Toggle value="overview">Visão geral</Toggle>
90
+ <Toggle value="sessions">Sessões</Toggle>
91
+ <Toggle value="skills">Habilidades</Toggle>
92
92
  </ToggleGroup>,
93
- )
93
+ );
94
94
  ```
95
95
 
96
96
  ### Escolha múltipla
97
97
 
98
98
  ```tsx preview
99
- const [marks, setMarks] = useState(['bold'])
99
+ const [marks, setMarks] = useState(["bold"]);
100
100
 
101
101
  render(
102
102
  <ToggleGroup type="multiple" value={marks} onValueChange={setMarks}>
103
- <ToggleGroupItem value="bold" aria-label="Negrito"><Bold /></ToggleGroupItem>
104
- <ToggleGroupItem value="italic" aria-label="Itálico"><Italic /></ToggleGroupItem>
105
- <ToggleGroupItem value="underline" aria-label="Sublinhado"><Underline /></ToggleGroupItem>
103
+ <Toggle value="bold" aria-label="Negrito">
104
+ <Bold />
105
+ </Toggle>
106
+ <Toggle value="italic" aria-label="Itálico">
107
+ <Italic />
108
+ </Toggle>
109
+ <Toggle value="underline" aria-label="Sublinhado">
110
+ <Underline />
111
+ </Toggle>
106
112
  </ToggleGroup>,
107
- )
113
+ );
108
114
  ```
109
115
 
110
116
  ### Itens só com ícone e tooltip
111
117
 
112
118
  Quando o item mostra somente um ícone, mantenha o `aria-label` e acrescente uma dica visual. Envolva o
113
- `ToggleGroupItem` em `TooltipTrigger asChild`: o item preserva o estado selecionado e seus atributos,
119
+ `Toggle` em `TooltipTrigger asChild`: o controle preserva o estado selecionado e seus atributos,
114
120
  e a dica aparece sem criar outro botão. O `TooltipProvider` da raiz do aplicativo continua necessário.
115
121
 
116
122
  ```tsx preview
117
- const [align, setAlign] = useState('left')
123
+ const [align, setAlign] = useState("left");
118
124
 
119
125
  render(
120
- <ToggleGroup type="single" value={align} onValueChange={(v) => v && setAlign(v)}>
126
+ <ToggleGroup
127
+ type="single"
128
+ value={align}
129
+ onValueChange={(v) => v && setAlign(v)}
130
+ >
121
131
  <Tooltip>
122
132
  <TooltipTrigger asChild>
123
- <ToggleGroupItem value="left" aria-label="Alinhar à esquerda"><AlignLeft /></ToggleGroupItem>
133
+ <Toggle value="left" aria-label="Alinhar à esquerda">
134
+ <AlignLeft />
135
+ </Toggle>
124
136
  </TooltipTrigger>
125
137
  <TooltipContent>Alinhar à esquerda</TooltipContent>
126
138
  </Tooltip>
127
139
  <Tooltip>
128
140
  <TooltipTrigger asChild>
129
- <ToggleGroupItem value="center" aria-label="Centralizar"><AlignCenter /></ToggleGroupItem>
141
+ <Toggle value="center" aria-label="Centralizar">
142
+ <AlignCenter />
143
+ </Toggle>
130
144
  </TooltipTrigger>
131
145
  <TooltipContent>Centralizar</TooltipContent>
132
146
  </Tooltip>
133
147
  <Tooltip>
134
148
  <TooltipTrigger asChild>
135
- <ToggleGroupItem value="right" aria-label="Alinhar à direita"><AlignRight /></ToggleGroupItem>
149
+ <Toggle value="right" aria-label="Alinhar à direita">
150
+ <AlignRight />
151
+ </Toggle>
136
152
  </TooltipTrigger>
137
153
  <TooltipContent>Alinhar à direita</TooltipContent>
138
154
  </Tooltip>
139
155
  </ToggleGroup>,
140
- )
156
+ );
141
157
  ```
142
158
 
143
- ### Variante e espaçamento
159
+ ### Relação entre os controles
144
160
 
145
- `variant`, `size` e `shape` definidos no grupo chegam aos itens por contexto. `spacing` separa os
146
- itens; com zero, eles formam um bloco contínuo.
161
+ `mode` segue a mesma gramática de `ButtonGroup`. `connected`, o padrão, une os controles e remove
162
+ bordas internas duplicadas; `spaced` preserva cada controle com um pequeno intervalo; `divided`
163
+ acrescenta divisores entre eles. `variant`, `size` e `shape` definidos no grupo chegam aos controles
164
+ por contexto.
147
165
 
148
166
  ```tsx preview
149
- <ToggleGroup type="single" variant="outline" spacing={2} defaultValue="developer">
150
- <ToggleGroupItem value="developer">developer</ToggleGroupItem>
151
- <ToggleGroupItem value="reviewer">reviewer</ToggleGroupItem>
152
- <ToggleGroupItem value="designer">designer</ToggleGroupItem>
167
+ <ToggleGroup type="single" variant="outline" defaultValue="call">
168
+ <Toggle value="call">
169
+ <Phone /> Telefone
170
+ </Toggle>
171
+ <Toggle value="email">
172
+ <Mail /> E-mail
173
+ </Toggle>
174
+ <Toggle value="whatsapp">
175
+ <MessageCircle /> WhatsApp
176
+ </Toggle>
153
177
  </ToggleGroup>
154
178
  ```
155
179
 
180
+ Use `mode="spaced"` quando cada controle precisar preservar sua própria borda e seu próprio raio.
181
+ Use `mode="divided"` quando a separação também precisar ficar explícita por divisores.
182
+
183
+ ### Aparência segmentada
184
+
185
+ Use `mode="segmented"` para escolhas curtas que precisam permanecer visíveis dentro de uma
186
+ superfície compartilhada. O item ativo usa o mesmo fundo de uma superfície elevada; os demais
187
+ permanecem no fundo do grupo. Segmentos inativos vizinhos recebem um divisor sutil; o divisor
188
+ desaparece dos dois lados da seleção. Essa aparência escolhe um valor no contexto atual: para
189
+ navegar entre painéis, use Tabs.
190
+
191
+ ```tsx preview
192
+ <ToggleGroup type="single" mode="segmented" defaultValue="sessions">
193
+ <Toggle value="overview">
194
+ <LayoutGrid /> Visão geral
195
+ </Toggle>
196
+ <Toggle value="sessions">
197
+ <History /> Sessões
198
+ </Toggle>
199
+ <Toggle value="skills">
200
+ <Settings /> Habilidades
201
+ </Toggle>
202
+ </ToggleGroup>
203
+ ```
204
+
205
+ `mode` trata a relação visual entre os controles. `variant` continua tratando cada `Toggle`; por
206
+ isso, a composição segmentada recomendada usa a variante padrão. O recuo fica dentro da altura da
207
+ escala de controles: o conjunto mantém `2rem`, `2.25rem` ou `2.5rem` conforme `size`.
208
+
209
+ ### Distribuição e moldura
210
+
211
+ `distribution="content"`, o padrão, preserva a largura dos rótulos. Use `equal` para dividir toda a
212
+ largura disponível igualmente entre os controles. `framed` acrescenta borda e recuo estrutural sem
213
+ mudar o modo de agrupamento.
214
+
156
215
  ### Propriedades de ToggleGroup
157
216
 
158
- | Propriedade | Tipo | Padrão | Descrição |
159
- |---|---|---|---|
160
- | `type` | `'single' \| 'multiple'` | | Define seleção única (`string`) ou múltipla (`string[]`). |
161
- | `value` | `string \| string[]` | | Seleção controlada; o tipo acompanha `type`. |
162
- | `onValueChange` | `(value: string \| string[]) => void` | | Informa a nova seleção. No modo single, uma string vazia representa nenhum item ativo. |
163
- | `defaultValue` | `string \| string[]` | | Seleção inicial no modo não controlado. |
164
- | `variant` | `'default' \| 'outline'` | `'default'` | Tratamento visual repassado aos itens. |
165
- | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho repassado aos itens. |
166
- | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do grupo e de suas extremidades. |
167
- | `spacing` | `number` | `0` | Distância entre os itens em unidades de spacing. |
168
-
169
- ### Propriedades de ToggleGroupItem
170
-
171
- | Propriedade | Tipo | Padrão | Descrição |
172
- |---|---|---|---|
173
- | `value` | `string` | | Identificador que entra no valor do grupo quando o item é ativado. |
174
- | `disabled` | `boolean` | `false` | Bloqueia somente este item e preserva seu estado visual. |
175
- | `variant` | `'default' \| 'outline'` | `'default'` | Tratamento do item quando o grupo não declara `variant`; o valor do grupo prevalece. |
176
- | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho do item quando o grupo não declara `size`; o valor do grupo prevalece. |
177
- | `shape` | `'default' \| 'pill'` | shape do grupo | Geometria deste item; quando informada, prevalece sobre a do grupo. |
217
+ | Propriedade | Tipo | Padrão | Descrição |
218
+ | --------------- | ----------------------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
219
+ | `type` | `'single' \| 'multiple'` | | Define seleção única (`string`) ou múltipla (`string[]`). |
220
+ | `value` | `string \| string[]` | | Seleção controlada; o tipo acompanha `type`. |
221
+ | `onValueChange` | `(value: string \| string[]) => void` | | Informa a nova seleção. No modo single, uma string vazia representa nenhum item ativo. |
222
+ | `defaultValue` | `string \| string[]` | | Seleção inicial no modo não controlado. |
223
+ | `variant` | `'default' \| 'outline'` | `'default'` | Tratamento visual repassado aos itens. `outline` aplica borda a cada item. |
224
+ | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Tamanho repassado aos itens. |
225
+ | `shape` | `'default' \| 'pill'` | `'default'` | Geometria do grupo e de suas extremidades. |
226
+ | `mode` | `'connected' \| 'spaced' \| 'divided' \| 'segmented'` | `'connected'` | Define como os controles se relacionam; `segmented` cria uma superfície compartilhada para a seleção. |
227
+ | `distribution` | `'content' \| 'equal'` | `'content'` | Preserva a largura dos rótulos ou reparte igualmente a largura disponível. |
228
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Organiza os controles e os divisores no eixo indicado. |
229
+ | `framed` | `boolean` | `false` | Acrescenta borda, raio e recuo ao grupo sem alterar o modo. |
230
+
231
+ Dentro de `ToggleGroup`, cada `Toggle` exige `value`. O grupo controla o estado selecionado; props
232
+ standalone como `pressed` e `onPressedChange` não participam dessa composição. `disabled`, `variant`,
233
+ No grupo, `variant` e `size` pertencem ao conjunto e chegam a todos os controles. `shape` no item
234
+ pode declarar uma pílula local dentro do grupo.