@softize/opus 29.0.0 → 31.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 (59) hide show
  1. package/CHANGELOG.md +137 -0
  2. package/bin/lib/check.mjs +178 -94
  3. package/bin/lib/copy.mjs +2970 -1801
  4. package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +52 -0
  5. package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +27 -2
  6. package/docs/adr/0045-surface-replaces-content-and-card.md +3 -0
  7. package/docs/adr/0060-surface-composition-separates-chrome-and-intro.md +142 -0
  8. package/docs/adr/0062-listas-contidas-e-texto-flexivel.md +68 -0
  9. package/docs/adr/0063-metadados-das-superficies.md +44 -0
  10. package/docs/adr/0064-action-list-com-itens-e-fontes-externas.md +47 -0
  11. package/docs/adr/0065-presentation-composes-settings-sections.md +295 -0
  12. package/docs/adr/README.md +8 -0
  13. package/package.json +1 -1
  14. package/registry/skills/build-opus-ui/SKILL.md +21 -19
  15. package/registry/skills/build-opus-ui/references/evaluations.md +21 -10
  16. package/registry/skills/build-opus-ui/references/ui-patterns.md +33 -28
  17. package/src/core/presentation.ts +938 -32
  18. package/src/core/types.ts +2 -1
  19. package/src/ui/components/patterns/form.tsx +2 -0
  20. package/src/ui/components/patterns/grouped-list.ts +2 -2
  21. package/src/ui/components/patterns/list.tsx +588 -365
  22. package/src/ui/components/patterns/page-heading-context.tsx +12 -5
  23. package/src/ui/components/patterns/page.tsx +238 -181
  24. package/src/ui/components/patterns/presentation-fields.tsx +246 -0
  25. package/src/ui/components/patterns/presentation-sections.tsx +494 -0
  26. package/src/ui/components/patterns/presentation.tsx +1142 -229
  27. package/src/ui/components/patterns/structural-header.tsx +47 -34
  28. package/src/ui/components/patterns/surface-anatomy.ts +27 -217
  29. package/src/ui/components/patterns/surface.tsx +55 -37
  30. package/src/ui/components/patterns/trigger.tsx +10 -2
  31. package/src/ui/components/primitives/close-button.tsx +11 -3
  32. package/src/ui/components/primitives/command.tsx +2 -8
  33. package/src/ui/components/primitives/detail.tsx +1 -1
  34. package/src/ui/components/primitives/dialog.tsx +59 -89
  35. package/src/ui/components/primitives/drawer.tsx +52 -87
  36. package/src/ui/components/primitives/field.tsx +1 -1
  37. package/src/ui/components/primitives/pagination.tsx +3 -2
  38. package/src/ui/components/primitives/table.tsx +3 -3
  39. package/src/ui/components/primitives/tabs.tsx +13 -2
  40. package/src/ui/docs/content/action-form.md +2 -1
  41. package/src/ui/docs/content/action-list.md +146 -73
  42. package/src/ui/docs/content/action-trigger.md +5 -0
  43. package/src/ui/docs/content/button.md +5 -6
  44. package/src/ui/docs/content/command.md +62 -50
  45. package/src/ui/docs/content/compositions.md +129 -459
  46. package/src/ui/docs/content/dialog.md +72 -101
  47. package/src/ui/docs/content/drawer.md +69 -92
  48. package/src/ui/docs/content/field.md +2 -1
  49. package/src/ui/docs/content/page.md +207 -386
  50. package/src/ui/docs/content/presentation.md +371 -23
  51. package/src/ui/docs/content/separator.md +1 -1
  52. package/src/ui/docs/content/surface.md +94 -116
  53. package/src/ui/docs/content/table.md +7 -0
  54. package/src/ui/docs/content/tokens.md +18 -4
  55. package/src/ui/drivers/context.tsx +11 -4
  56. package/src/ui/drivers/react.tsx +27 -7
  57. package/src/ui/meta.ts +5 -5
  58. package/src/ui/react.tsx +38 -8
  59. package/src/ui/theme.css +44 -33
@@ -10,13 +10,20 @@ limites visuais da coleção; use [`Table`](/ui/table) diretamente quando a comp
10
10
  variante `framed`. Carregamento, erro e vazio usam o mesmo [`DataState`](/ui/data-state) das
11
11
  demais coleções.
12
12
 
13
- A barra mantém em uma única linha os controles declarados como `inline`: ela não quebra e não move
14
- filtros para o modal por falta de espaço. Seletores de valor único acompanham o valor escolhido até
15
- `20rem` e truncam o excedente; a busca varia entre `8rem` e `16rem` conforme a largura
16
- disponível. Quando a barra ficar longa demais, declare como `advanced` os filtros que não precisam
17
- ficar sempre à vista, como descreve a seção “Onde cada filtro aparece”. Recarregar e
18
- exibição formam um `ButtonGroup` espaçado; `toolbarActions` acrescenta, depois deles, operações
19
- ligadas ao recorte atual.
13
+ A tabela usa a largura disponível e permite quebra em células textuais. Colunas com `fit`
14
+ preservam valores compactos sem quebra. A largura das colunas de dados considera cabeçalhos e
15
+ valores, sem comprimir todas as colunas `fit` para ampliar a primeira. Os cabeçalhos quebram
16
+ quando falta espaço; seleção, ações de linha e células contendo apenas um switch
17
+ (direto ou envolvido por TooltipTrigger) mantêm a largura compacta.
18
+ Se identificadores sem espaços ou larguras mínimas explícitas excederem o espaço, a rolagem
19
+ fica nos resultados, sem alargar a toolbar ou o rodapé. Em `layout="fill"`, o cabeçalho continua fixo nessa região.
20
+
21
+ A barra distribui os controles `inline` em mais de uma linha quando necessário, preservando sua
22
+ ordem. Ela não move filtros automaticamente para o modal. Seletores de valor único acompanham o
23
+ valor escolhido até `20rem` e truncam o excedente; a busca varia entre `8rem` e `16rem`.
24
+ Declare como `advanced` os filtros que não precisam ficar sempre à vista, como descreve a seção
25
+ “Onde cada filtro aparece”. As opções da lista e os filtros avançados formam um `ButtonGroup` espaçado;
26
+ `toolbarActions` acrescenta, depois deles, ações da coleção, como criar, importar e exportar.
20
27
 
21
28
  ```tsx preview col
22
29
  render(
@@ -34,24 +41,30 @@ render(
34
41
 
35
42
  ## Coleção nomeada
36
43
 
37
- Em uma página, envolva a listagem com [`Surface`](/ui/surface). O título e a criação ocupam
38
- extremos opostos do cabeçalho da coleção; busca, filtros e atualização permanecem na toolbar do
39
- `ActionList`.
44
+ Em uma página, use [`Page`](/ui/page) para o título e ações gerais. Ações da coleção
45
+ ficam na barra do `ActionList`, inclusive a criação, mesmo sem depender dos filtros atuais.
46
+ Em abas, a barra acompanha a lista do painel ativo.
40
47
 
41
48
  ```tsx
42
- <Surface
43
- as="section"
44
- title="Workspaces"
45
- level={1}
46
- actions={<Button>Criar workspace</Button>}
47
- >
48
- <ActionList action={workspaceList} input={{}} />
49
- </Surface>
49
+ <Page title="Workspaces" layout="fill">
50
+ <ActionList
51
+ action={workspaceList}
52
+ input={{}}
53
+ toolbarActions={
54
+ <Button aria-label="Criar workspace" onClick={openCreate}>
55
+ Criar
56
+ </Button>
57
+ }
58
+ />
59
+ </Page>
50
60
  ```
51
61
 
52
- Use `toolbarActions` somente quando a operação depender do recorte visível, como exportar os
53
- resultados filtrados. A propriedade recebe os nós já prontos: em uma ação somente com ícone, forneça
54
- você mesmo o `aria-label` e o `Tooltip` com o nome da operação.
62
+ O botão exibe “Criar”; seu nome acessível e o formulário identificam o recurso.
63
+ Menu com vários tipos de criação preserva os nomes específicos. Operações sobre a seleção
64
+ usam `batchActions`, e ações de um registro usam `rowActions`.
65
+
66
+ `toolbarActions` recebe os nós já prontos e aparece também em visualizações sem filtros
67
+ ou tabela. Em uma ação somente com ícone, forneça `aria-label` e tooltip.
55
68
 
56
69
  ## Barra de filtros compartilhada
57
70
 
@@ -221,7 +234,7 @@ Declare `grouping.fields` quando uma dimensão enumerável puder organizar a tab
221
234
  recorte de negócio. Cada campo precisa ser um filtro `select` ou `lookup`; texto, número e data não
222
235
  são dimensões de agrupamento. O filtro pode aceitar uma ou várias opções: cada consulta de grupo
223
236
  envia um único valor na forma declarada. A lista continua plana por padrão, e a pessoa escolhe
224
- **Agrupar por** em **Exibição**.
237
+ **Agrupar por** em **Opções da lista**.
225
238
 
226
239
  ```ts
227
240
  filters: {
@@ -254,9 +267,17 @@ aplicados à mesma dimensão restringem os grupos exibidos.
254
267
 
255
268
  ## Paginação e carregamento
256
269
 
257
- `ActionList` envia `limit`, definido por `pageSize`, e `page` no input. O handler aplica o recorte e
270
+ Para coleções pequenas que devem aparecer por inteiro, declare `paginate: false` no
271
+ contrato da list action. ActionList omite `page` e `limit` da consulta, do estado lido
272
+ da URL e dos links gerados; não exibe rodapé nem seletor de quantidade. O handler deve
273
+ devolver todos os itens que atendem à busca e aos filtros. O componente não busca páginas
274
+ adicionais em segundo plano nem aumenta o limite artificialmente. A opção também é
275
+ respeitada pelas listas renderizadas por Presentation, sem configuração visual duplicada.
276
+
277
+ Nos demais modos, `ActionList` envia `limit`, definido por `pageSize`, e `page` no input. O handler aplica o recorte e
258
278
  devolve `total` na resposta paginada. Quando existe total, o rodapé mostra a página atual, os links
259
- disponíveis e a quantidade de itens. Alterar busca, filtro, ordenação ou período retorna à primeira
279
+ disponíveis e a quantidade de itens. Com total zero, o rodapé e seu divisor não aparecem:
280
+ a mensagem de vazio permanece como indicação do resultado. Alterar busca, filtro, ordenação ou período retorna à primeira
260
281
  página. Durante a primeira consulta, `DataState` apresenta o carregamento; em atualizações
261
282
  posteriores, a lista permanece visível com `aria-busy`.
262
283
 
@@ -272,20 +293,8 @@ rodapé podem continuar visíveis enquanto só os resultados rolam. Para isso, d
272
293
  render(
273
294
  <DocBrowserActionProvider>
274
295
  <div className="h-80 w-full overflow-x-auto overflow-y-hidden rounded-lg border border-border">
275
- <Page layout="fill">
276
- <PageBody>
277
- <Surface as="section" level={1}>
278
- <SurfaceHeader>
279
- <SurfaceTitle>Workspaces</SurfaceTitle>
280
- <SurfaceActions>
281
- <Button>Criar workspace</Button>
282
- </SurfaceActions>
283
- </SurfaceHeader>
284
- <SurfaceBody>
285
- <ActionList action={docWorkspaceList} input={{}} />
286
- </SurfaceBody>
287
- </Surface>
288
- </PageBody>
296
+ <Page title="Workspaces" layout="fill">
297
+ <ActionList action={docWorkspaceList} input={{}} toolbarActions={<Button aria-label="Criar workspace">Criar</Button>} />
289
298
  </Page>
290
299
  </div>
291
300
  </DocBrowserActionProvider>,
@@ -312,8 +321,8 @@ componentes no caminho. Nos demais casos, a decisão é sua:
312
321
 
313
322
  - Para a lista manter a altura dos resultados, como em uma seção secundária cuja rolagem pertence ao
314
323
  `PageBody`, declare `layout="flow"`.
315
- - Um contêiner próprio entre `SurfaceBody` e a lista, como um `TabsPanel`, não repassa a altura, e
316
- a lista volta a `flow`. Declare `layout="fill"` na lista e dê ao contêiner as classes
324
+ - `TabsPanel` repassa a altura disponível à lista. Um contêiner próprio sem essa integração
325
+ interrompe a herança: declare `layout="fill"` na lista e dê ao contêiner as classes
317
326
  `flex min-h-0 flex-1 flex-col`.
318
327
  - Dentro de [`Surface`](/ui/surface), [`Dialog`](/ui/dialog), [`Drawer`](/ui/drawer) ou
319
328
  [`Pane`](/ui/pane), a lista não herda o `fill` da página: a altura do corpo pertence à
@@ -435,13 +444,17 @@ filters: {
435
444
 
436
445
  ## Exibição e estado na URL
437
446
 
438
- O botão de exibição abre as preferências da listagem em qualquer visualização. Na tabela, permite
447
+ O botão **Opções da lista** reúne as preferências disponíveis para a visualização. Na tabela, permite
439
448
  escolher a ordenação, inverter sua direção, agrupar e mostrar ou ocultar colunas, inclusive as
440
- declaradas com `hidden`; em todas as visualizações, permite alterar a quantidade de itens por
441
- página. Um ponto sinaliza quando alguma preferência diverge do padrão. O rodapé explicita a
449
+ declaradas com `hidden`; nas listas paginadas, permite alterar a quantidade de itens por
450
+ página. Quando não há preferências disponíveis, o botão não aparece. Um ponto sinaliza quando alguma preferência diverge do padrão. O rodapé explicita a
442
451
  ordenação e o agrupamento ativos junto da paginação e do total. Busca, ordenação, filtros,
443
452
  visualização, colunas e limite podem ser serializados na query string pelos helpers do componente:
444
453
 
454
+ Operações usam `invalidates` para atualizar os dados, e o consumidor pode manter suas
455
+ assinaturas de eventos. Em caso de falha, o estado de erro oferece **Tentar novamente**
456
+ diretamente. Não há comando de recarga manual no menu nem polling automático no ActionList.
457
+
445
458
  Cada campo de `sort.fields` precisa de uma coluna com rótulo textual para aparecer no seletor e no
446
459
  resumo do rodapé; a coluna pode estar `hidden`. Campos sem coluna continuam válidos para o handler e
447
460
  para uma ordenação padrão, mas o Opus não expõe seu identificador técnico na interface. **Sem
@@ -461,9 +474,55 @@ restaurá-lo.
461
474
 
462
475
  Convenção compacta (defaults omitidos): `?q=…&sort=key:dir&status=…&view=board&cols=a,b,c&limit=25`.
463
476
 
477
+ ## Itens conectados
478
+
479
+ Para coleções que apresentam um nome, uma descrição e ações por registro, declare os campos
480
+ em `item`. O ActionList monta a moldura e os separadores com ItemGroup; mantém carregamento,
481
+ erro, recuperação e vazio. `title` coloca o nome da coleção junto das `toolbarActions`.
482
+ Nome e descrição ocupam uma linha cada, com reticências quando ultrapassam a largura disponível.
483
+
484
+ ```tsx preview col
485
+ render(
486
+ <DocBrowserActionProvider>
487
+ <ActionList
488
+ action={docWorkspaceList}
489
+ input={{}}
490
+ title="Workspaces"
491
+ item={{ title: "name", description: "client" }}
492
+ />
493
+ </DocBrowserActionProvider>
494
+ );
495
+ ```
496
+
497
+ Os nomes em `item` são campos diretos do resultado, com valores textuais ou numéricos.
498
+ O objeto é serializável. `rowActions` recebe o registro e um contexto com `items`, `total`,
499
+ `isFetching`, `isError` e `refetch`; `toolbarActions` também pode ser uma função desse contexto.
500
+ O modo de itens não combina `children`, `views` ou seleção em lote. `onRowClick` continua
501
+ restrito à tabela; ações dos itens ficam nos controles explícitos de `rowActions`.
502
+
503
+ Com uma página, esse modo dispensa o rodapé. Com mais resultados, mantém a paginação.
504
+ Depois de remover o último item de uma página, volta à última página válida. A tabela conserva
505
+ seu rodapé e seus controles existentes.
506
+
507
+ ### Fonte governada por outro serviço
508
+
509
+ Quando a coleção pertence a uma integração que já possui cliente e autorização, `source`
510
+ conecta esse cliente sem criar uma action artificial. Declare uma chave com o escopo de acesso
511
+ e um `load(input, { signal })` que retorna `{ items, total, cursor }`. A fonte deve respeitar
512
+ `page` e `limit`; se o serviço retorna todos os registros, o adapter pagina o conjunto completo.
513
+
514
+ A fonte substitui `action`, exige QueryClientProvider e dispensa OpusProvider quando não há
515
+ actions nos controles. Seu cache é separado das actions, não reaproveita dados entre escopos e
516
+ é descartado após a desmontagem. Falhas usam a mensagem pública `errorMessage` e oferecem
517
+ recuperação; detalhes técnicos do erro não são exibidos. Dados sensíveis usados pelo cliente
518
+ externo devem permanecer no adapter, fora dos itens retornados.
519
+
520
+ `source` é runtime: seu loader não entra no JSON. Uma definição serializável futura deverá
521
+ referenciar uma fonte registrada, enquanto `item` já descreve a apresentação sem JSX.
522
+
464
523
  ## Composição dos itens
465
524
 
466
- Passe `children` para substituir a tabela por uma composição própria, como uma lista de
525
+ Quando `item` não representar a composição necessária, passe `children` para substituir a tabela por uma composição própria, como uma lista de
467
526
  [`Item`](/ui/item). A barra de filtros e os estados de carregamento, erro e vazio continuam sendo
468
527
  tratados por `ActionList`. O contrato não leva o tipo do item até o componente: anote o parâmetro de
469
528
  `children` (ou de `render`, em uma visualização) ou declare os genéricos, como em
@@ -557,32 +616,46 @@ para fora de `toolbarActions`.
557
616
 
558
617
  ## Propriedades de ActionList
559
618
 
560
- | Propriedade | Tipo | Padrão | Descrição |
561
- | ----------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
562
- | `action` | `ListActionLike` | | A ListAction do Opus (kind list, output = item). A paginação envia `limit`/`page` e usa o `total` devolvido. |
563
- | `input` | `TInput` | | O escopo base, como `{ workspaceId }`. A toolbar acrescenta busca, filtros e paginação sem sobrescrever essas chaves. |
564
- | `layout` | `'flow' \| 'fill'` | `'fill'` como filha direta de `PageBody`, `SurfaceBody` ou do corpo da `Presentation` em uma página `fill`; `'flow'` nos demais casos | Em `fill`, ocupa a altura de um ancestral limitado e rola somente a região de resultados; na tabela, o cabeçalho fica fixo. Sem ancestral limitado, equivale a `flow`. |
565
- | `cells` | `Record<string, (item) => ReactNode>` | | Células custom por cima das colunas do contrato (chave = column.key). |
566
- | `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime para filtros select/lookup. Em uma dimensão de agrupamento, a coleção precisa ser completa e ter de 1 a 12 itens. |
567
- | `filterDefaults` | `Record<string, string>` | | Valores de repouso aplicados à consulta; o `X` só aparece quando o filtro diverge deles e retorna a eles. |
568
- | `filterIcons` | `Record<string, ReactNode>` | | Ícones leading dos filtros, indexados pelo nome declarado no contrato. |
569
- | `advancedFilters` | `{ columns?: 1 \| 2 \| 3; sectionIcons?: Record<string, ReactNode>; sectionVariant?: 'plain' \| 'framed' }` | `{ columns: 1, sectionVariant: 'plain' }` | Define as colunas, os ícones e o tratamento dos grupos; o Opus deriva a largura correspondente do diálogo. |
570
- | `periodPosition` | `'before' \| 'after' \| { afterFilter: string }` | `'before'` | Posiciona o período antes, depois ou imediatamente após um filtro inline. |
571
- | `period` | `{ basisFilter: string }` | | Reúne o período e o filtro inline indicado em um único controle. |
572
- | `views` | `Record<string, ActionListView<TItem>>` | | Visualizações alternativas `{ label, icon?, render }`; o seletor alterna entre a tabela e estas. |
573
- | `beforeContent` | `ReactNode` | | Conteúdo entre a toolbar e o corpo, como um resumo que reage ao recorte atual. |
574
- | `columns` | `ActionListColumn<TItem>[]` | | Tabela explícita, que substitui as colunas declaradas no contrato. |
575
- | `children` | `(items, refetch) => ReactNode` | | Modo composição: layout livre; a toolbar segue. Tem precedência sobre columns. |
576
- | `batch` | `ActionListBatchAction[]` | | Ações em lote: `{ label, can?, run, confirm?: { title, message? }, destructive? }` — liga a multi-seleção. |
577
- | `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
578
- | `pageSize` | `number` | `50` | Itens por página padrão (vira `limit`/`page` no input; handler devolve `total`). O usuário troca no popover de exibição. |
579
- | `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — para quem embala sincronizar com a URL. |
580
- | `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado, por exemplo quando um formulário inline aberto conta como conteúdo. |
581
- | `loading` | `boolean` | | Carga extra agregada à da consulta, como uma query irmã de que os `children` dependem. |
582
- | `emptyMessage` | `string` | `'Nenhum resultado.'` | Frase do estado vazio (o `Empty` do `DataState`). |
583
- | `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro; a ação de tentar de novo refaz a consulta. |
584
- | `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
585
- | `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
586
- | `viewControls` | `ReactNode` | | Controles da visualização ativa, imediatamente após o seletor de visualização. |
587
- | `toolbarActions` | `ReactNode` | | Operações ligadas ao recorte atual, no fim da barra e separadas do grupo de controles. Os nós são renderizados como recebidos: nome acessível e tooltip de uma ação somente com ícone ficam com o consumidor. |
588
- | `rowActions` | `(item: TItem) => ReactNode` | | Ações por linha (coluna final, à direita). A partir de duas, entram em um grupo com intervalo compacto; uma só fica solta, sem grupo. Cliques ali não disparam o `onRowClick`. |
619
+ `action` e `source` são alternativas mutuamente exclusivas.
620
+
621
+ | Propriedade | Tipo | Padrão | Descrição |
622
+ | --- | --- | --- | --- |
623
+ | `source` | `ListDataSource<TItem>` | — | Fonte externa com `key` e `load`; substitui a action. |
624
+ | `title` | `string` | — | Título da coleção, ao lado das ações. |
625
+ | `item` | `ActionListItemSpec` | — | Campos `title` e `description?` da apresentação em itens. |
626
+
627
+
628
+ | Propriedade | Tipo | Padrão | Descrição |
629
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
630
+ | `action` | `ListActionLike` | | A ListAction do Opus (kind list, output = item). A paginação envia `limit`/`page` e usa o `total` devolvido. |
631
+ | `input` | `TInput` | | O escopo base, como `{ workspaceId }`. A toolbar acrescenta busca, filtros e paginação sem sobrescrever essas chaves. |
632
+ | `layout` | `'flow' \| 'fill'` | `'fill'` como filha direta de `PageBody`, `SurfaceBody` ou do corpo da `Presentation` em uma página `fill`; `'flow'` nos demais casos | Em `fill`, ocupa a altura de um ancestral limitado e rola somente a região de resultados; na tabela, o cabeçalho fica fixo. Sem ancestral limitado, equivale a `flow`. |
633
+ | `cells` | `Record<string, (item) => ReactNode>` | | Células custom por cima das colunas do contrato (chave = column.key). |
634
+ | `filterOptions` | `Record<string, SelectOption[]>` | | Opções de runtime para filtros select/lookup. Em uma dimensão de agrupamento, a coleção precisa ser completa e ter de 1 a 12 itens. |
635
+ | `filterDefaults` | `Record<string, string>` | | Valores de repouso aplicados à consulta; o `X` só aparece quando o filtro diverge deles e retorna a eles. |
636
+ | `filterIcons` | `Record<string, ReactNode>` | | Ícones leading dos filtros, indexados pelo nome declarado no contrato. |
637
+ | `advancedFilters` | `{ columns?: 1 \| 2 \| 3; sectionIcons?: Record<string, ReactNode>; sectionVariant?: 'plain' \| 'framed' }` | `{ columns: 1, sectionVariant: 'plain' }` | Define as colunas, os ícones e o tratamento dos grupos; o Opus deriva a largura correspondente do diálogo. |
638
+ | `periodPosition` | `'before' \| 'after' \| { afterFilter: string }` | `'before'` | Posiciona o período antes, depois ou imediatamente após um filtro inline. |
639
+ | `period` | `{ basisFilter: string }` | | Reúne o período e o filtro inline indicado em um único controle. |
640
+ | `views` | `Record<string, ActionListView<TItem>>` | | Visualizações alternativas `{ label, icon?, render }`; o seletor alterna entre a tabela e estas. |
641
+ | `beforeContent` | `ReactNode \| (input: TInput) => ReactNode` | | Conteúdo entre a toolbar e o corpo; a função recebe o mesmo input efetivo da consulta, para resumos que acompanham o recorte. |
642
+ | `columns` | `ActionListColumn<TItem>[]` | | Tabela explícita, que substitui as colunas declaradas no contrato. |
643
+ | `children` | `(items, refetch) => ReactNode` | | Modo composição: layout livre; a toolbar segue. Tem precedência sobre columns. |
644
+ | `batch` | `ActionListBatchAction[]` | | Ações em lote: `{ label, can?, run, confirm?: { title, message? }, destructive? }` — liga a multi-seleção. |
645
+ | `rowId` | `(item) => string` | `item.id` | Identidade da linha para seleção. |
646
+ | `pageSize` | `number` | `50` | Itens por página padrão (vira `limit`/`page` no input; handler devolve `total`). O usuário troca no popover de exibição. |
647
+ | `state / onStateChange` | `ActionListState / (next) => void` | interno | Estado da toolbar controlado — para quem embala sincronizar com a URL. |
648
+ | `empty` | `(items) => boolean` | `items.length === 0` | Sobrepõe o vazio derivado, por exemplo quando um formulário inline aberto conta como conteúdo. |
649
+ | `loading` | `boolean` | | Carga extra agregada à da consulta, como uma query irmã de que os `children` dependem. |
650
+ | `emptyMessage` | `string` | `'Nenhum resultado.'` | Frase do estado vazio (o `Empty` do `DataState`). |
651
+ | `errorMessage` | `string` | `'Não foi possível carregar.'` | Título do aviso de erro; a ação de tentar de novo refaz a consulta. |
652
+ | `retryLabel` | `string` | `'Tentar de novo'` | Nome acessível e tooltip da ação de recuperação. |
653
+ | `onRowClick` | `(item: TItem) => void` | | Clique na linha (ex.: navegar para o detalhe) — só na tabela. |
654
+ | `viewControls` | `ReactNode` | | Controles da visualização ativa, imediatamente após o seletor de visualização. |
655
+ | `toolbarActions` | `ReactNode \| ((context) => ReactNode)` | | Ações da coleção, incluindo criação, junto ao título quando declarado ou no fim da barra. Os nós são renderizados como recebidos: nome acessível e tooltip de uma ação somente com ícone ficam com o consumidor. |
656
+ | `rowActions` | `(item: TItem, context) => ReactNode` | | Ações por registro, na tabela ou à direita dos itens. Na tabela, a partir de duas, entram em um grupo com intervalo compacto; uma só fica solta, sem grupo. Cliques ali não disparam o `onRowClick`. |
657
+
658
+ Para carregar um detalhe a partir do mesmo recorte da coleção, use
659
+ `listStateToInput(state, action, { input, filterDefaults, pageSize })`. O helper materializa
660
+ busca, filtros, período, ordenação e paginação com a mesma regra usada pelo ActionList.
661
+ `input` transporta o escopo fixo; `filterDefaults` preenche filtros que não vieram no estado.
@@ -103,6 +103,11 @@ um atalho, um arrastar, um item de menu.
103
103
  | `disabled` | `boolean` | `false` | Desabilita o botão independentemente da execução em andamento. |
104
104
  | `onSuccess` | `(data: TData) => void` | | Pós-sucesso (cache já invalidado pelo action.invalidates). |
105
105
  | `onLoadingChange` | `(loading: boolean) => void` | | Informa início e fim da execução a quem coordena bloqueio entre várias actions. |
106
+ | `aria-label` | `string` | | Nome acessível completo quando o texto visível é abreviado. |
106
107
  | `icon` | `React.ReactNode` | | Torna o botão icon-only: rótulo no tooltip e no `aria-label`, clique que não vaza para o item. |
107
108
  | `itemLabel` | `string` | | Nome do alvo na pergunta (sai entre aspas, em destaque, antes da mensagem do contrato). |
108
109
  | `className` | `string` | | Classes do botão. O tamanho vem de `size` (escala única); com `icon`, o default é `icon-xs`. |
110
+
111
+ Quando `label` abrevia o texto visível, forneça `aria-label` com o nome completo da operação.
112
+ O nome acessível deve conter o texto visível; por exemplo, “Criar” e “Criar cliente”.
113
+ Presentation faz essa associação ao usar um rótulo contextual no comando.
@@ -32,8 +32,8 @@ há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em
32
32
  | Nome | Medida | Uso |
33
33
  | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
34
34
  | `xs` | 1.5rem | Ação dentro de um campo (`InputGroupButton`). |
35
- | `sm` | 2rem | Headers, criação de coleções e operações densas de toolbar ou seção. |
36
- | `default` | 2.25rem | Footers de Dialog e Drawer e controles comuns; é também a linha de `Input`. |
35
+ | `sm` | 2rem | Criação de coleções e operações densas de toolbar ou seção. |
36
+ | `default` | 2.25rem | Headers, footers de Dialog e Drawer e controles comuns; é também a linha de `Input`. |
37
37
  | `lg` | 2.5rem | Chamada principal com mais área de toque. |
38
38
  | `icon-xs` | 1.5rem | Ação só de ícone dentro de um campo ou de uma linha densa; é o quadrado de `ActionTrigger` com `icon`. |
39
39
  | `icon-sm` | 2rem | Ação só de ícone em composições compactas que precisam de mais presença; também é o quadrado das setas do pager de `ActionList`. |
@@ -41,15 +41,14 @@ há texto visível. O glifo dentro do controle acompanha o tamanho (0.875rem em
41
41
  | `icon-lg` | 2.5rem | Ação só de ícone ao lado de um `lg`. |
42
42
 
43
43
  Escolha o tamanho pela região, não pela importância visual: `variant` e `context` resolvem a
44
- hierarquia da ação. Headers de Page, Dialog, Drawer e Surface usam `sm`/`icon-sm`. A criação
44
+ hierarquia da ação. Headers de Page, Dialog, Drawer e Surface usam `default`/`icon`. A criação
45
45
  no cabeçalho de uma coleção usa `sm` textual, sem ícone. Footers de
46
46
  [`Dialog`](/ui/dialog) e [`Drawer`](/ui/drawer) conservam `default`. Operações densas de toolbar ou de seção usam `sm`;
47
47
  ações dentro de linha ou célula usam `xs` ou `icon-xs`. Assim a mesma decisão mantém a mesma altura
48
48
  mesmo quando uma superfície troca uma ação secundária por uma primária.
49
49
 
50
- O retorno de `PageBack` usa `icon-sm`. O fechamento de Dialog e Drawer é um `CloseButton` circular
51
- (`shape="pill"`), `neutral` e `subtle`, no quadrado `icon-sm` por padrão, ou `icon-xs` com
52
- `closeSize="xs"`.
50
+ O retorno de `PageBack` usa `icon`. O fechamento de Dialog e Drawer é um `CloseButton` circular
51
+ (`shape="pill"`), `neutral` e `subtle`, no quadrado `icon-xs` (`closeSize="xs"`) por padrão.
53
52
 
54
53
  ```tsx preview
55
54
  <Button size="xs">Mínimo</Button>
@@ -11,13 +11,21 @@ escolher um valor em um formulário, use [`Select`](/ui/select).
11
11
  <CommandList>
12
12
  <CommandEmpty>Nada encontrado.</CommandEmpty>
13
13
  <CommandGroup heading="Sessões">
14
- <CommandItem><FolderGit2 /> Onboarding do faturamento</CommandItem>
15
- <CommandItem><FolderGit2 /> Integração com o ERP</CommandItem>
14
+ <CommandItem>
15
+ <FolderGit2 /> Onboarding do faturamento
16
+ </CommandItem>
17
+ <CommandItem>
18
+ <FolderGit2 /> Integração com o ERP
19
+ </CommandItem>
16
20
  </CommandGroup>
17
21
  <CommandSeparator />
18
22
  <CommandGroup heading="Ações">
19
- <CommandItem><Plus /> Criar sessão <CommandShortcut>⌘N</CommandShortcut></CommandItem>
20
- <CommandItem><Play /> Iniciar ambiente</CommandItem>
23
+ <CommandItem>
24
+ <Plus /> Criar sessão <CommandShortcut>⌘N</CommandShortcut>
25
+ </CommandItem>
26
+ <CommandItem>
27
+ <Play /> Iniciar ambiente
28
+ </CommandItem>
21
29
  </CommandGroup>
22
30
  </CommandList>
23
31
  </Command>
@@ -26,23 +34,23 @@ escolher um valor em um formulário, use [`Select`](/ui/select).
26
34
  ## Paleta de comandos em modal
27
35
 
28
36
  `CommandDialog` apresenta a mesma lista dentro de um [`Dialog`](/ui/dialog), com um cabeçalho
29
- compacto que mostra o título e o botão de fechar. A descrição fica disponível somente para
37
+ padrão que mostra o título e o botão de fechar. A descrição fica disponível somente para
30
38
  tecnologias assistivas. O componente não registra atalho de teclado: para abrir a paleta com
31
39
  ⌘K, o aplicativo escuta o evento e altera `open`, como no exemplo.
32
40
 
33
41
  ```tsx preview
34
- const [open, setOpen] = useState(false)
42
+ const [open, setOpen] = useState(false);
35
43
 
36
44
  useEffect(() => {
37
45
  const onKeyDown = (event: KeyboardEvent): void => {
38
- if (event.key.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) {
39
- event.preventDefault()
40
- setOpen((current) => !current)
46
+ if (event.key.toLowerCase() === "k" && (event.metaKey || event.ctrlKey)) {
47
+ event.preventDefault();
48
+ setOpen((current) => !current);
41
49
  }
42
- }
43
- document.addEventListener('keydown', onKeyDown)
44
- return () => document.removeEventListener('keydown', onKeyDown)
45
- }, [])
50
+ };
51
+ document.addEventListener("keydown", onKeyDown);
52
+ return () => document.removeEventListener("keydown", onKeyDown);
53
+ }, []);
46
54
 
47
55
  render(
48
56
  <>
@@ -54,63 +62,67 @@ render(
54
62
  <CommandList>
55
63
  <CommandEmpty>Nada encontrado.</CommandEmpty>
56
64
  <CommandGroup heading="IA">
57
- <CommandItem onSelect={() => setOpen(false)}><Bot /> Chamar agente</CommandItem>
58
- <CommandItem onSelect={() => setOpen(false)}><Sparkles /> Importar skill</CommandItem>
65
+ <CommandItem onSelect={() => setOpen(false)}>
66
+ <Bot /> Chamar agente
67
+ </CommandItem>
68
+ <CommandItem onSelect={() => setOpen(false)}>
69
+ <Sparkles /> Importar skill
70
+ </CommandItem>
59
71
  </CommandGroup>
60
72
  </CommandList>
61
73
  </CommandDialog>
62
74
  </>,
63
- )
75
+ );
64
76
  ```
65
77
 
66
78
  ## Propriedades de Command
67
79
 
68
- | Propriedade | Tipo | Padrão | Descrição |
69
- |---|---|---|---|
70
- | `shouldFilter` | `boolean` | `true` | Com `false`, desliga a filtragem e a ordenação automáticas; o aplicativo passa a renderizar somente os itens que correspondem à busca. |
71
- | `filter` | `(value: string, search: string, keywords?: string[]) => number` | | Pontua cada item para a busca atual, de `0` (oculto) a `1` (melhor resultado). |
72
- | `value` | `string` | | Item destacado no modo controlado. |
73
- | `onValueChange` | `(value: string) => void` | | Chamado quando o item destacado muda. |
74
- | `loop` | `boolean` | `false` | Faz as setas voltarem ao início ou ao fim da lista. |
75
- | `label` | `string` | | Nome acessível do menu, sem exibição visual. |
76
- | `className` | `string` | | Classes adicionais aplicadas à raiz. |
80
+ | Propriedade | Tipo | Padrão | Descrição |
81
+ | --------------- | ---------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | `shouldFilter` | `boolean` | `true` | Com `false`, desliga a filtragem e a ordenação automáticas; o aplicativo passa a renderizar somente os itens que correspondem à busca. |
83
+ | `filter` | `(value: string, search: string, keywords?: string[]) => number` | | Pontua cada item para a busca atual, de `0` (oculto) a `1` (melhor resultado). |
84
+ | `value` | `string` | | Item destacado no modo controlado. |
85
+ | `onValueChange` | `(value: string) => void` | | Chamado quando o item destacado muda. |
86
+ | `loop` | `boolean` | `false` | Faz as setas voltarem ao início ou ao fim da lista. |
87
+ | `label` | `string` | | Nome acessível do menu, sem exibição visual. |
88
+ | `className` | `string` | | Classes adicionais aplicadas à raiz. |
77
89
 
78
90
  ## Propriedades de CommandInput
79
91
 
80
- | Propriedade | Tipo | Padrão | Descrição |
81
- |---|---|---|---|
82
- | `placeholder` | `string` | | Texto exibido enquanto a busca está vazia. |
83
- | `value` | `string` | | Texto da busca no modo controlado. |
84
- | `onValueChange` | `(search: string) => void` | | Chamado quando o texto da busca muda. |
85
- | `disabled` | `boolean` | `false` | Desabilita o campo de busca. |
86
- | `className` | `string` | | Classes adicionais aplicadas ao campo; o ícone de busca fica no wrapper. |
92
+ | Propriedade | Tipo | Padrão | Descrição |
93
+ | --------------- | -------------------------- | ------- | ------------------------------------------------------------------------ |
94
+ | `placeholder` | `string` | | Texto exibido enquanto a busca está vazia. |
95
+ | `value` | `string` | | Texto da busca no modo controlado. |
96
+ | `onValueChange` | `(search: string) => void` | | Chamado quando o texto da busca muda. |
97
+ | `disabled` | `boolean` | `false` | Desabilita o campo de busca. |
98
+ | `className` | `string` | | Classes adicionais aplicadas ao campo; o ícone de busca fica no wrapper. |
87
99
 
88
100
  ## Propriedades de CommandDialog
89
101
 
90
- | Propriedade | Tipo | Padrão | Descrição |
91
- |---|---|---|---|
92
- | `open` | `boolean` | | Estado do modal no modo controlado. |
93
- | `onOpenChange` | `(open: boolean) => void` | | Atualiza o estado do modal; pode ser conectado ao atalho do aplicativo. |
94
- | `title` | `string` | `'Paleta de comandos'` | Título exibido no header compacto; também nomeia o diálogo para tecnologias assistivas. |
95
- | `description` | `string` | `'Busque um comando para executar…'` | Descrição disponível somente para tecnologias assistivas. |
96
- | `showCloseButton` | `boolean` | `true` | Exibe o botão de fechamento. |
97
- | `className` | `string` | | Classes do conteúdo do diálogo, como a largura. |
102
+ | Propriedade | Tipo | Padrão | Descrição |
103
+ | ----------------- | ------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------- |
104
+ | `open` | `boolean` | | Estado do modal no modo controlado. |
105
+ | `onOpenChange` | `(open: boolean) => void` | | Atualiza o estado do modal; pode ser conectado ao atalho do aplicativo. |
106
+ | `title` | `string` | `'Paleta de comandos'` | Título exibido no header padrão; também nomeia o diálogo para tecnologias assistivas. |
107
+ | `description` | `string` | `'Busque um comando para executar…'` | Descrição disponível somente para tecnologias assistivas. |
108
+ | `showCloseButton` | `boolean` | `true` | Exibe o botão de fechamento. |
109
+ | `className` | `string` | | Classes do conteúdo do diálogo, como a largura. |
98
110
 
99
111
  ## Propriedades de CommandItem
100
112
 
101
- | Propriedade | Tipo | Padrão | Descrição |
102
- |---|---|---|---|
103
- | `onSelect` | `(value: string) => void` | | Chamado ao selecionar o item por clique ou teclado. |
104
- | `value` | `string` | | Valor estável usado na busca e no destaque. Sem ele, o valor é inferido do texto do item. |
105
- | `keywords` | `string[]` | | Termos adicionais considerados na busca. |
106
- | `disabled` | `boolean` | `false` | Impede a seleção do item. |
113
+ | Propriedade | Tipo | Padrão | Descrição |
114
+ | ----------- | ------------------------- | ------- | ----------------------------------------------------------------------------------------- |
115
+ | `onSelect` | `(value: string) => void` | | Chamado ao selecionar o item por clique ou teclado. |
116
+ | `value` | `string` | | Valor estável usado na busca e no destaque. Sem ele, o valor é inferido do texto do item. |
117
+ | `keywords` | `string[]` | | Termos adicionais considerados na busca. |
118
+ | `disabled` | `boolean` | `false` | Impede a seleção do item. |
107
119
 
108
120
  ## Propriedades de CommandGroup
109
121
 
110
- | Propriedade | Tipo | Padrão | Descrição |
111
- |---|---|---|---|
112
- | `heading` | `React.ReactNode` | | Título exibido acima dos itens do grupo. |
113
- | `value` | `string` | | Identificador do grupo; obrigatório e único quando não há `heading`. |
122
+ | Propriedade | Tipo | Padrão | Descrição |
123
+ | ----------- | ----------------- | ------ | -------------------------------------------------------------------- |
124
+ | `heading` | `React.ReactNode` | | Título exibido acima dos itens do grupo. |
125
+ | `value` | `string` | | Identificador do grupo; obrigatório e único quando não há `heading`. |
114
126
 
115
127
  `CommandList` limita a altura a `18.75rem` e rola o excedente; `CommandEmpty` aparece somente quando
116
128
  nenhum item corresponde à busca; `CommandSeparator` divide grupos; e `CommandShortcut` mostra, à