@praxisui/table 9.0.0-beta.0 → 9.0.0-beta.2
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/README.md +139 -1883
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,1944 +1,200 @@
|
|
|
1
|
-
|
|
2
|
-
title: "Table"
|
|
3
|
-
slug: "table-overview"
|
|
4
|
-
description: "Visao geral do @praxisui/table com TableConfig unificada, filtros, renderers, performance e integracao enterprise."
|
|
5
|
-
doc_type: "reference"
|
|
6
|
-
document_kind: "component-overview"
|
|
7
|
-
component: "table"
|
|
8
|
-
category: "components"
|
|
9
|
-
audience:
|
|
10
|
-
- "frontend"
|
|
11
|
-
- "host"
|
|
12
|
-
- "architect"
|
|
13
|
-
level: "intermediate"
|
|
14
|
-
status: "active"
|
|
15
|
-
owner: "praxis-ui"
|
|
16
|
-
tags:
|
|
17
|
-
- "table"
|
|
18
|
-
- "tableconfig"
|
|
19
|
-
- "filters"
|
|
20
|
-
- "renderers"
|
|
21
|
-
- "enterprise"
|
|
22
|
-
order: 25
|
|
23
|
-
icon: "table_chart"
|
|
24
|
-
toc: true
|
|
25
|
-
sidebar: true
|
|
26
|
-
search_boost: 1.0
|
|
27
|
-
reading_time: 14
|
|
28
|
-
estimated_setup_time: 25
|
|
29
|
-
version: "1.0"
|
|
30
|
-
related_docs:
|
|
31
|
-
- "dynamic-filter-architecture-overview"
|
|
32
|
-
- "dynamic-filter-host-integration-guide"
|
|
33
|
-
- "dynamic-filter-payload-contract"
|
|
34
|
-
- "dynamic-filter-range-filters-guide"
|
|
35
|
-
- "dynamic-filter-editor-settings-guide"
|
|
36
|
-
- "dynamic-inline-filter-catalog"
|
|
37
|
-
- "dynamic-filter-troubleshooting-guide"
|
|
38
|
-
- "dynamic-filter-backend-contract-cheatsheet"
|
|
39
|
-
- "adr-dynamic-filter-cross-lib-coupling-2026-03"
|
|
40
|
-
- "host-integration-guide"
|
|
41
|
-
- "consumer-integration-quickstart"
|
|
42
|
-
keywords:
|
|
43
|
-
- "TableConfig"
|
|
44
|
-
- "filters"
|
|
45
|
-
- "renderers"
|
|
46
|
-
- "performance"
|
|
47
|
-
- "dynamic filter"
|
|
48
|
-
last_updated: "2026-03-07"
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
# @praxisui/table
|
|
52
|
-
|
|
53
|
-
> Estado canônico do workspace Angular: JSON Logic é o único contrato ativo de regras condicionais.
|
|
54
|
-
|
|
55
|
-
## Documentation
|
|
56
|
-
|
|
57
|
-
- Documentação oficial: https://praxisui.dev
|
|
58
|
-
- Aplicação de referência: https://github.com/codexrodrigues/praxis-ui-quickstart
|
|
59
|
-
- Indicado para: data grids enterprise com filtros, edicao, virtualizacao, renderers e governanca de configuracao
|
|
60
|
-
|
|
61
|
-
## When to use
|
|
62
|
-
|
|
63
|
-
- Exibir grandes volumes de dados com filtros e operacoes ricas
|
|
64
|
-
- Governar configuracao de colunas, toolbar e comportamento em runtime
|
|
65
|
-
- Integrar tabelas a fluxos CRUD, IA e editores visuais do ecossistema Praxis UI
|
|
66
|
-
|
|
67
|
-
## Rich Content Convergence
|
|
68
|
-
|
|
69
|
-
- `mapTableRendererToRichContentP0(...)` e a ponte canonica entre os renderers de celula da tabela e o vocabulario compartilhado `1.0` de rich content em `@praxisui/core`.
|
|
70
|
-
- O subconjunto promovido nesta fase e intencionalmente pequeno: `icon`, `image`, `badge`, `chip -> badge`, `avatar`, `progress` e `compose`.
|
|
71
|
-
- Renderers interativos como `button`, `toggle`, `menu`, `link`, `html` e `rating`, alem do shell de detail/expansion, continuam table-owned ate promocao semantica explicita.
|
|
72
|
-
|
|
73
|
-
## Detail Rich Surfaces
|
|
74
|
-
|
|
75
|
-
- `behavior.expansion.detail.schemaContract.allowedNodes` agora inclui `detailList` como node canônico novo para colecoes ricas no detail row.
|
|
76
|
-
- A mesma fronteira agora inclui `cardGrid` como container canônico para grades densas de cards no detail.
|
|
77
|
-
- `mediaBlock` entrou como primitive compartilhada para headers/identidade rica no topo do detail, sem criar renderer paralelo table-owned.
|
|
78
|
-
- `timeline` entrou como surface cronologica compartilhada para atividade recente, preservando o shell host-owned da tabela.
|
|
79
|
-
- `actionBar` entrou como surface host-mediated para grupos de ações contextuais no detail, sem duplicar `row actions`.
|
|
80
|
-
- `tableRef`, `formRef`, `chartRef`, `templateRef` e `diagramEmbed` agora entram como embeds governados no detail row.
|
|
81
|
-
- `list` continua significando colecao simples. Ele nao foi promovido para subtree declarativa por item.
|
|
82
|
-
- `detailList` e host-mediated:
|
|
83
|
-
- o host resolve a colecao e despacha `itemActions`
|
|
84
|
-
- cada item renderiza sua subtree declarativa via `PraxisRichContent`
|
|
85
|
-
- `cardGrid` tambem e host-mediated:
|
|
86
|
-
- o host resolve apenas o shell responsivo do grid
|
|
87
|
-
- cada card continua renderizado como `RichCardNode` via `PraxisRichContent`
|
|
88
|
-
- `actionBar` tambem e host-mediated:
|
|
89
|
-
- o host continua dono de dispatch, disabled, `visibleWhen` e `payloadExpr`
|
|
90
|
-
- o miolo visual de cada botão continua delegado para `PraxisRichContent`
|
|
91
|
-
- embeds governados tambem sao host-mediated:
|
|
92
|
-
- o host continua dono de discovery, resolucao e abertura de surface
|
|
93
|
-
- o shell visual, metadados e CTA do embed ficam padronizados na tabela
|
|
94
|
-
- `diagramEmbed` adiciona preview governado de fonte, sem abrir uma DSL paralela no detail
|
|
95
|
-
- `mediaBlock` e host-neutral:
|
|
96
|
-
- o contrato base vive em `@praxisui/core`
|
|
97
|
-
- o rendering vive em `@praxisui/rich-content`
|
|
98
|
-
- a tabela apenas o aceita no detail schema e o delega para o runtime compartilhado
|
|
99
|
-
- O primeiro corte cobre:
|
|
100
|
-
- `field` ou `items`
|
|
101
|
-
- `emptyText`
|
|
102
|
-
- `itemSchema.nodes`
|
|
103
|
-
- `itemContext` (`detailItem` / `detailIndex`)
|
|
104
|
-
- `itemActions` com `payloadExpr`, `visibleWhen` e `disabledWhen`
|
|
105
|
-
- O segundo corte cobre:
|
|
106
|
-
- `columns` (`1 | 2 | 3 | 4 | 'auto'`)
|
|
107
|
-
- `minCardWidth`
|
|
108
|
-
- `cards[]` com `title`, `subtitle` e `content` limitado a `RichPresenterNode | RichComposeNode`
|
|
109
|
-
- O terceiro corte agora cobre:
|
|
110
|
-
- `mediaBlock.avatar`
|
|
111
|
-
- `mediaBlock.title`
|
|
112
|
-
- `mediaBlock.subtitle`
|
|
113
|
-
- `mediaBlock.meta`
|
|
114
|
-
- `mediaBlock.trailing`
|
|
115
|
-
- O quarto corte agora cobre:
|
|
116
|
-
- `timeline.field` ou `timeline.items`
|
|
117
|
-
- `timeline.emptyText`
|
|
118
|
-
- `timeline.itemSchema.titleField`
|
|
119
|
-
- `timeline.itemSchema.subtitleField`
|
|
120
|
-
- `timeline.itemSchema.metaField`
|
|
121
|
-
- `timeline.itemSchema.iconField`
|
|
122
|
-
- `timeline.itemSchema.badgeField`
|
|
123
|
-
- O quinto corte agora cobre:
|
|
124
|
-
- `tableRef`, `formRef`, `chartRef` e `templateRef` com `description`, `caption`, `inputs`, `presetRef`, `emptyText` e `action`
|
|
125
|
-
- `diagramEmbed` com `provider`, `source` ou `sourceField`, `presetRef` e `action`
|
|
126
|
-
|
|
127
|
-
## `x-ui.analytics` como fallback tabular canônico
|
|
128
|
-
|
|
129
|
-
O `@praxisui/table` materializa a familia `analytic-table` quando o `@praxisui/core`
|
|
130
|
-
resolve que uma projection de `x-ui.analytics` continua valida, mas nao e um chart
|
|
131
|
-
inequivoco.
|
|
132
|
-
|
|
133
|
-
Regras:
|
|
134
|
-
|
|
135
|
-
- a decisao da familia continua no `@praxisui/core`
|
|
136
|
-
- o `@praxisui/table` nao decide `chart` vs `analytic-table`
|
|
137
|
-
- a tabela apenas materializa a decision/projection recebida
|
|
138
|
-
- a materializacao preserva `dimension`, `metrics`, `sort`, `limit` e agregacao sem
|
|
139
|
-
inventar defaults semanticos no host
|
|
140
|
-
- `primaryMetrics` e `secondaryMetrics` entram na mesma trilha canonica de execucao
|
|
141
|
-
- projections `timeseries` preservam o valor temporal bruto para manter ordenacao
|
|
142
|
-
cronologica consistente no fallback tabular
|
|
143
|
-
|
|
144
|
-
Superficies publicas:
|
|
145
|
-
|
|
146
|
-
- `AnalyticsTableConfigAdapterService`: converte projection em `TableConfig` local
|
|
147
|
-
- `AnalyticsTableStatsApiService`: executa `praxis.stats` e devolve linhas tabulares
|
|
148
|
-
- `AnalyticsTableContractService`: fachada publica para hosts que querem resolver
|
|
149
|
-
contrato + dados de tabela analitica sem reimplementar leitura de `x-ui.analytics`
|
|
150
|
-
|
|
151
|
-
Filtro runtime:
|
|
152
|
-
|
|
153
|
-
- a projection continua sendo a fonte canonica da semantica analitica
|
|
154
|
-
- o host pode passar `queryContext.filters` em `loadTableViews(..., { queryContexts })`
|
|
155
|
-
- somente `filters` entram no request remoto nesta fase
|
|
156
|
-
- `sort`, `limit` e agregacao continuam vindo da projection publicada
|
|
157
|
-
|
|
158
|
-
> Componente de tabela empresarial avançado com arquitetura unificada
|
|
159
|
-
|
|
160
|
-
## Customization Mode Contract
|
|
161
|
-
|
|
162
|
-
- `enableCustomization` is the canonical public input for runtime customization mode.
|
|
163
|
-
- The default is `false`; hosts opt in when they want settings, authoring affordances and schema-drift UX.
|
|
164
|
-
- Customization mode does not change the table data mode. It only gates configuration/editorial surfaces.
|
|
165
|
-
|
|
166
|
-
## Praxis Semantic Assistant
|
|
167
|
-
|
|
168
|
-
- The table assistant is part of the global Praxis semantic copilot experience, not a standalone table patch bot.
|
|
169
|
-
- In customization mode, opening the assistant registers a `PraxisAssistantContextSnapshot` through `PraxisAssistantSessionRegistryService`.
|
|
170
|
-
- The table session identity follows `table:{routeKey}:{componentInstanceId || tableId}` so multiple tables on the same page can preserve separate assistant sessions.
|
|
171
|
-
- Minimizing the table shell keeps the governed session in `PraxisAssistantSessionRegistryService` with `presence: 'origin-anchor'`; the visual affordance returns to the table assistant trigger, which changes to the minimized state and reopens the preserved session. The App Shell does not interpret or execute table semantics.
|
|
172
|
-
- The table snapshot stores only safe context: table identity, target, context chips, manifest reference, resource path, schema field names, data/runtime digests, capability refs and governance hints.
|
|
173
|
-
- When rows are selected, the assistant context includes a governed `table-row-selection` digest with selected count, id field, selected ids and a small sanitized row sample. The runtime never uses selected row text to decide primary intent; the LLM/backend receives the digest only as grounding after the assistant turn has the table component scope.
|
|
174
|
-
- The table also registers a runtime observation provider through `PraxisRuntimeComponentObservationRegistryService`. Runtime observations publish redacted component evidence only: identity, resource refs, `runtimeSurfaceInstanceRef`, lifecycle, selection and data-profile digests, schema field refs, declared record surfaces and declared table actions.
|
|
175
|
-
- Runtime observations must not include raw rows, full cell values, hidden fields, secrets or intent decisions. Surface and action refs are derived from declared contracts such as `aiContext.recordSurfaces`, table capabilities and adapter context; undeclared navigation or inferred intents stay out of the observation envelope.
|
|
176
|
-
- Prompts that indicate shared business decisions, such as rules, policies, compliance, LGPD, approval, eligibility or access control, must be routed to governed `domain-rules` handoff instead of local table config patches.
|
|
177
|
-
|
|
178
|
-
### Consultative answers vs governed edits
|
|
179
|
-
|
|
180
|
-
`@praxisui/table` declares two component-level response modes through its authoring context:
|
|
181
|
-
|
|
182
|
-
- `consult/answer`: use when the user asks a factual or how-to question about the current table. Examples: "qual endpoint esta servindo esta tabela?", "quais campos existem nesta tabela?", "quais recursos de estilização a tabela suporta?" and "como criar uma coluna que soma duas colunas?".
|
|
183
|
-
- `edit/componentEditPlan`: use when the user asks to apply or materialize a change. Examples: "mostre ativo como badge verde", "crie uma coluna bonus com 10% do salario", "deixe o avatar vermelho quando salario for maior que 30000" and "anime as linhas com salario acima de 30000".
|
|
184
|
-
|
|
185
|
-
Consultative answers are selected by the backend/LLM from the component `responseModes` contract and return `type: "info"`. The table runtime must not classify user intent through local keywords; it only sends grounded context such as `resourcePath`, schema fields and the table authoring manifest, then renders the orchestrator response without an apply action. For how-to or documentation-style turns, the orchestrator should explain the governed operation shape and give a safe example instead of collecting missing edit parameters.
|
|
186
|
-
|
|
187
|
-
Governed edits must be selected by the same backend/LLM response-mode decision and produce a validated `componentEditPlan`; the table compiles that plan into a patch and only then exposes the review/apply step.
|
|
188
|
-
|
|
189
|
-
## Migration Note
|
|
190
|
-
|
|
191
|
-
- This release treats `enableCustomization=false` as the canonical default.
|
|
192
|
-
- Hosts that relied on the previous implicit authoring behavior must now declare `[enableCustomization]="true"` explicitly.
|
|
193
|
-
- This is an intentional breaking change for enterprise governance: authoring is opt-in, not ambient.
|
|
194
|
-
|
|
195
|
-
## 🌟 Visão Geral
|
|
196
|
-
|
|
197
|
-
A biblioteca `@praxisui/table` fornece um componente de tabela robusto e altamente configurável para aplicações Angular empresariais. Com a nova arquitetura unificada, oferece uma experiência de desenvolvimento simplificada mantendo todos os recursos avançados.
|
|
198
|
-
|
|
199
|
-
## 🎨 Tema M3 (tokens mÃnimos)
|
|
200
|
-
|
|
201
|
-
Para garantir que a tabela, filtros e editores reflitam o tema do app host:
|
|
202
|
-
|
|
203
|
-
- SuperfÃcies: `--md-sys-color-surface`, `--md-sys-color-surface-variant`, `--md-sys-color-surface-container-*`
|
|
204
|
-
- Texto/contorno: `--md-sys-color-on-surface`, `--md-sys-color-on-surface-variant`, `--md-sys-color-on-primary`, `--md-sys-color-outline`, `--md-sys-color-outline-variant`
|
|
205
|
-
- Semânticos: `--md-sys-color-primary`, `--md-sys-color-secondary`, `--md-sys-color-tertiary`, `--md-sys-color-error`
|
|
206
|
-
- Containers: `--md-sys-color-primary-container`, `--md-sys-color-secondary-container`, `--md-sys-color-tertiary-container`, `--md-sys-color-error-container`
|
|
207
|
-
- Overlay: `--md-sys-color-scrim`
|
|
208
|
-
- Elevação: `--md-sys-elevation-level1`–`--md-sys-elevation-level3`
|
|
209
|
-
|
|
210
|
-
Observação: tokens `--md-sys-*` e `--mdc-theme-*` continuam aceitos quando presentes.
|
|
211
|
-
Nota: a classe de tema é decisão do host (`.dark-theme` ou `.theme-dark`/`.theme-light`); mantenha tokens e componentes no mesmo escopo.
|
|
212
|
-
|
|
213
|
-
Surface stacking is owned by `@praxisui/table`: when toolbar, grid and paginator are rendered as one table surface, the runtime flattens the internal join corners and keeps only the outer corners rounded. Host apps should theme the table through Material/Praxis tokens instead of targeting internal descendants such as `.mat-mdc-table`, `.mat-mdc-paginator` or `.praxis-toolbar` to repair border radius joins.
|
|
214
|
-
|
|
215
|
-
## ✨ CaracterÃsticas Principais
|
|
216
|
-
|
|
217
|
-
### ðŸ—ï¸ Arquitetura Unificada
|
|
218
|
-
|
|
219
|
-
- **Interface única**: `TableConfig` consolidada
|
|
220
|
-
- **Type Safety**: Tipagem forte em toda a API
|
|
221
|
-
- **Performance otimizada**: Eliminação de overhead de adaptação
|
|
222
|
-
- **API simplificada**: Menos confusão, mais produtividade
|
|
223
|
-
|
|
224
|
-
### 📊 Recursos Avançados
|
|
225
|
-
|
|
226
|
-
- **Paginação inteligente**: Client-side e server-side
|
|
227
|
-
- **Ordenação múltipla**: Suporte a multi-sort
|
|
228
|
-
- **Filtros dinâmicos**: Global e por coluna
|
|
229
|
-
- **Seleção de linhas**: Single, multiple e bulk actions
|
|
230
|
-
- **Redimensionamento**: Colunas redimensionáveis
|
|
231
|
-
- **Virtualização**: Para grandes volumes de dados
|
|
232
|
-
- **Exportação**: CSV, Excel, PDF
|
|
233
|
-
- **Acessibilidade**: WCAG 2.1 AA compliant
|
|
234
|
-
- **Verificação de Schema**: ETag/If-None-Match com notificações (somente em customização)
|
|
235
|
-
|
|
236
|
-
## Expansion Hypermedia
|
|
237
|
-
|
|
238
|
-
O primeiro corte de `behavior.expansion.detail.source.mode = "hypermedia"` é
|
|
239
|
-
`capabilities-first`:
|
|
240
|
-
|
|
241
|
-
- a row expandida começa em `_links.capabilities`
|
|
242
|
-
- `surfaces`, `actions` e operações canônicas vêm do snapshot agregado retornado
|
|
243
|
-
pelo backend
|
|
244
|
-
- o detail renderiza um resumo contextual somente leitura, com foco em UX de
|
|
245
|
-
negócio e affordances disponÃveis naquele registro
|
|
246
|
-
- este corte não executa `surface.open` inline nem dispara workflow inline
|
|
247
|
-
- quando o item não expõe `rel="capabilities"`, o runtime aplica fail-closed
|
|
248
|
-
|
|
249
|
-
Observação de escala:
|
|
250
|
-
- o prefetch por linha continua ativo quando `actions.row.enabled=true`
|
|
251
|
-
- quando apenas o expansion `hypermedia` está ligado, o discovery contextual é
|
|
252
|
-
resolvido sob demanda ao expandir a linha
|
|
253
|
-
|
|
254
|
-
### 📄 Contrato Inline do Filtro
|
|
255
|
-
|
|
256
|
-
- Referencia canonica: `projects/praxis-dynamic-fields/docs/dynamic-fields-inline-components-guide.md` (slug publicado: `dynamic-fields-inline-components-guide`)
|
|
257
|
-
- No contexto da tabela, use este contrato para definir `controlType` inline e politica de compatibilidade (canonica vs legado).
|
|
258
|
-
|
|
259
|
-
### 🧠Decisoes de Arquitetura (ADR)
|
|
260
|
-
|
|
261
|
-
- Bundle/adapters: `projects/praxis-table/docs/adr/2026-03-filter-drawer-adapter-light-entrypoint.md`
|
|
262
|
-
|
|
263
|
-
### 🎨 Editores Visuais
|
|
264
|
-
|
|
265
|
-
- **Behavior Editor**: Configuração de comportamentos
|
|
266
|
-
- **Columns Editor**: Gestão avançada de colunas
|
|
267
|
-
- **Toolbar Editor**: Personalização de ações
|
|
268
|
-
- **Messages Editor**: Textos e localização
|
|
269
|
-
|
|
270
|
-
Nota (Regras)
|
|
271
|
-
- A antiga aba visual genérica foi removida deste pacote. O editor especializado de regras da Tabela já está disponÃvel no painel de configuração.
|
|
272
|
-
|
|
273
|
-
### 🧩 Editor de Regras (column-first)
|
|
274
|
-
|
|
275
|
-
O novo editor é "column-first" e usa uma tipagem compartilhada com o Editor de Colunas. Principais pontos:
|
|
276
|
-
|
|
277
|
-
- Escopo: aplique regras na linha inteira (rowConditionalStyles) ou em uma coluna especÃfica (columns[].conditionalStyles).
|
|
278
|
-
- Operadores por tipo: o conjunto de operadores é filtrado por tipo (string/number/date/boolean/enum).
|
|
279
|
-
- Editores de valor dinâmicos: inputs numéricos, listas (CSV/múltipla seleção), datepickers.
|
|
280
|
-
- Grupos lógicos: construa condições com AND/OR/NOT e reordene via arrastar-e-soltar (CDK DragDrop).
|
|
281
|
-
- Preview: execute “Testar†para ver quantas linhas seriam afetadas pelas regras ativas.
|
|
282
|
-
- Import/Export: exporta JSON sem o campo `enabled`; importa com validação estrutural de JSON Logic e sanitização de estilos (allowlist).
|
|
283
|
-
|
|
284
|
-
#### Estados de linha vs estilos de célula
|
|
285
|
-
|
|
286
|
-
Use `rowConditionalStyles` quando a condição descreve o estado semântico da linha inteira, como prioridade, SLA vencido, item sem responsável, bloqueio ou estagnação. Para temas e faixas visuais, prefira `cssClass` em `rowConditionalStyles` e resolva a aparência no CSS do host. Isso preserva o ownership de tema do host e evita que gradientes ou backgrounds reiniciem em cada célula.
|
|
287
|
-
|
|
288
|
-
Use `columns[].conditionalStyles` quando o destaque pertence a uma coluna específica, como texto negativo em vermelho, status em badge, valor monetário fora da faixa ou ícone contextual de uma célula.
|
|
289
|
-
|
|
290
|
-
Padrão recomendado:
|
|
291
|
-
|
|
292
|
-
```ts
|
|
293
|
-
rowConditionalStyles: [
|
|
294
|
-
{
|
|
295
|
-
condition: { contains: [{ var: 'prioridadeNome' }, 'Alta'] },
|
|
296
|
-
cssClass: 'app-row--priority-high',
|
|
297
|
-
description: 'Prioridade alta recebe âncora visual de linha.',
|
|
298
|
-
},
|
|
299
|
-
]
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
O editor especializado tambem persiste `effects: RuleEffectDefinition[]` em
|
|
303
|
-
`rowConditionalStyles` e `columns[].conditionalStyles`. O runtime consome esses
|
|
304
|
-
efeitos diretamente e ainda preserva `cssClass`/`style` como fallback de
|
|
305
|
-
compatibilidade, mantendo o ciclo abrir -> aplicar/salvar -> reabrir sem exigir
|
|
306
|
-
que consumidores recompilem manualmente os efeitos visuais.
|
|
307
|
-
|
|
308
|
-
Para renderizacao condicional, `columns[].conditionalRenderers` tambem aceita
|
|
309
|
-
`effects: RuleEffectDefinition[]` como fonte canonica para renderer, tooltip e
|
|
310
|
-
animacao. `rowConditionalRenderers` fica tipado no contrato publico para tooltip
|
|
311
|
-
e animacao de linha, preservando os campos planos `tooltip`/`animation` como
|
|
312
|
-
fallback.
|
|
313
|
-
|
|
314
|
-
Animações condicionais devem ser authoradas como decisão semântica, não como CSS
|
|
315
|
-
manual em `style.animation`. Para animar a linha inteira, use
|
|
316
|
-
`rowConditionalRenderers[].animation`; para animar somente uma célula/coluna, use
|
|
317
|
-
`columns[].conditionalRenderers[].animation`. Prefira presets semânticos
|
|
318
|
-
(`info-soft`, `success-confirm`, `warning-attention`, `critical-alert`,
|
|
319
|
-
`audit-review`, `sync-pending` ou aliases como `pulse-soft`, `sla-breach`,
|
|
320
|
-
`risk-critical`) antes de `type` bruto. Exemplo:
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
rowConditionalRenderers: [
|
|
324
|
-
{
|
|
325
|
-
id: 'salario-alto-pulse',
|
|
326
|
-
condition: { '>': [{ var: 'salario' }, 30000] },
|
|
327
|
-
animation: { preset: 'pulse-soft', trigger: 'onAppear', intensity: 'subtle', repeat: 'once' },
|
|
328
|
-
enabled: true,
|
|
329
|
-
},
|
|
330
|
-
]
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
`appearance.animations.enabled = false` desativa animações; os flags
|
|
334
|
-
`appearance.animations.specific.row` e `appearance.animations.specific.cell`
|
|
335
|
-
desativam seletivamente animações condicionais de linha e célula.
|
|
336
|
-
|
|
337
|
-
Colunas calculadas usam `columns[].computed.expression` como AST JSON Logic
|
|
338
|
-
canonico. Regras, estilos e renderers condicionais podem referenciar esses
|
|
339
|
-
valores via `computed.<field>`. Quando uma coluna calculada depende de outra
|
|
340
|
-
coluna calculada, o runtime resolve a ordem de avaliacao por dependencias
|
|
341
|
-
declaradas ou referencias `var: "computed.<field>"`, evitando que o resultado
|
|
342
|
-
dependa da ordem fisica de `columns[]`.
|
|
343
|
-
|
|
344
|
-
```scss
|
|
345
|
-
.app-table .mat-mdc-row.app-row--priority-high {
|
|
346
|
-
background: linear-gradient(
|
|
347
|
-
90deg,
|
|
348
|
-
color-mix(in srgb, var(--md-sys-color-error-container) 42%, transparent),
|
|
349
|
-
transparent
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.app-table .mat-mdc-row.app-row--priority-high .mat-mdc-cell {
|
|
354
|
-
background: transparent;
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
Evite aplicar gradientes de estado de linha diretamente em `.mat-mdc-cell`. Como cada célula tem largura, sticky behavior e renderer próprios, o gradiente pode parecer quebrado por coluna, especialmente em tema dark.
|
|
359
|
-
|
|
360
|
-
Exemplos de JSON Logic:
|
|
361
|
-
|
|
362
|
-
```json
|
|
363
|
-
{ "contains": [{ "var": "status" }, "Ativo"] }
|
|
364
|
-
{ "!": [{ "in": [{ "var": "status" }, ["A", "I"]] }] }
|
|
365
|
-
{ "and": [{ ">=": [{ "var": "price" }, 10] }, { "<=": [{ "var": "price" }, 20] }] }
|
|
366
|
-
{ "and": [{ ">=": [{ "var": "createdAt" }, "2024-10-01"] }, { "<=": [{ "var": "createdAt" }, "2024-10-31"] }] }
|
|
367
|
-
{ "or": [{ "==": [{ "var": "name" }, null] }, { "==": [{ "var": "name" }, ""] }] }
|
|
368
|
-
{ "==": [{ "var": "active" }, true] }
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
Notas sobre enum/CSV:
|
|
372
|
-
- Para campos enumerados, as opções são inferidas de `valueMapping` da coluna ou de `fields?.options`.
|
|
373
|
-
- Para `in/not in` em números/strings, use CSV (ex.: `10, 20, 30` ou `Alice, Bob`).
|
|
374
|
-
|
|
375
|
-
#### Operadores e helpers de JSON Logic
|
|
376
|
-
|
|
377
|
-
O runtime da tabela já inclui operadores e helpers canônicos para JSON/tempo/listas/texto sem configuração extra:
|
|
378
|
-
|
|
379
|
-
- JSON: `jsonGet`, `hasJsonKey`, `jsonPathMatches`, `jsonEq`, `jsonIn`, `jsonType`
|
|
380
|
-
- Texto/valor: `isBlank`, `eqIgnoreCase`, `coalesce`
|
|
381
|
-
- Faixas: `between`, `dateBetween`
|
|
382
|
-
- Coleções: `containsAny`, `containsAll`, `len`, `lenEq`, `lenGt`, `lenLt`, `lenAtLeast`
|
|
383
|
-
- Tempo: `today`, `now`, `dateAdd`, `dateFormat`, `isToday`, `inLast`, `isPast`, `isPastOrNow`, `isFuture`, `isFutureOrNow`, `weekdayIn`
|
|
384
|
-
|
|
385
|
-
Exemplos de JSON Logic:
|
|
386
|
-
- `{ "===": [{ "jsonGet": [{ "var": "payload" }, "$.user.name"] }, "Alice"] }`
|
|
387
|
-
- `{ "jsonIn": [{ "var": "customer" }, "$.id", [10, 20]] }`
|
|
388
|
-
- `{ "eqIgnoreCase": [{ "var": "status" }, "ATIVO"] }`
|
|
389
|
-
- `{ "between": [{ "var": "amount" }, 10, 20] }`
|
|
390
|
-
- `{ "dateBetween": [{ "var": "createdAt" }, "2025-01-01", "2025-12-31"] }`
|
|
391
|
-
- `{ "containsAll": [{ "var": "tags" }, ["vip", "gold"]] }`
|
|
392
|
-
|
|
393
|
-
Guia completo: `projects/praxis-table/docs/json-logic-operators-and-helpers.md`
|
|
394
|
-
|
|
395
|
-
#### 🎨 Efeitos (Conteúdo C)
|
|
396
|
-
|
|
397
|
-
O bloco de Efeitos permite aplicar estilo e visuais quando a condição é verdadeira:
|
|
398
|
-
|
|
399
|
-
- Toolbar de texto com Ãcones (negrito, itálico, sublinhado) e presets rápidos (tachado, maiúsculas/minúsculas/capitalizar, tamanho +/- e reset).
|
|
400
|
-
- Presets de feedback com Ãcones (sucesso, aviso, erro, info) — mantêm o rótulo via tooltip e `aria-label`.
|
|
401
|
-
- “Posição do valor†como grupo de alternância (antes / entre-1 / entre-2 / depois / oculto).
|
|
402
|
-
- “Ordem dos visuais†com seletor compacto (3 mais comuns) e menu para opções extras.
|
|
403
|
-
- Layout do compose: espaçamento, alinhamento, quebra de linha e reticências.
|
|
404
|
-
|
|
405
|
-
Dicas
|
|
406
|
-
- Use “Resetar texto†para reverter efeitos de texto sem perder cor de fundo/borda.
|
|
407
|
-
- Para acessibilidade, os grupos possuem `role="toolbar"`, tooltips e navegação por teclado consistente.
|
|
408
|
-
|
|
409
|
-
Como reverter efeitos
|
|
410
|
-
- “Reset Text†remove apenas propriedades de tipografia e transformação de texto.
|
|
411
|
-
- “Limpar efeitos†apaga classe e estilo livres do bloco rápido.
|
|
412
|
-
|
|
413
|
-
Observação: capturas de tela “antes/depois†podem ser adicionadas no PR conforme necessário.
|
|
414
|
-
|
|
415
|
-
#### Relacionais (lookup)
|
|
416
|
-
|
|
417
|
-
Campos relacionais (FK/objetos) podem ser editados no Rules Editor com busca assÃncrona (typeahead) e seleção single/multiple.
|
|
418
|
-
|
|
419
|
-
Boas práticas
|
|
420
|
-
- Informe `resourcePath` (ou `endpoint`) no `FieldDefinition` para habilitar o lookup via `GenericCrudService` do host.
|
|
421
|
-
- Defina `valueField` (chave estável, tipicamente `id`) e `displayField` (label amigável, como `name`).
|
|
422
|
-
|
|
423
|
-
## 🔎 Filtro: Atalhos para PerÃodo (DateRange)
|
|
424
|
-
|
|
425
|
-
Habilite atalhos de perÃodo no filtro dinâmico sem alterar o contrato do `praxis-filter` — apenas via metadata do campo:
|
|
426
|
-
|
|
427
|
-
```ts
|
|
428
|
-
// Em FilterConfig (ou na metadata de campos do filtro)
|
|
429
|
-
{
|
|
430
|
-
name: 'period',
|
|
431
|
-
label: 'PerÃodo',
|
|
432
|
-
controlType: 'dateRange',
|
|
433
|
-
showShortcuts: true,
|
|
434
|
-
shortcuts: ['thisMonth','lastMonth','thisYear'],
|
|
435
|
-
// Opcional:
|
|
436
|
-
// i18nShortcuts: { thisMonth: 'Este mês', lastMonth: 'Mês passado' },
|
|
437
|
-
// applyOnShortcutClick: true
|
|
438
|
-
}
|
|
439
|
-
```
|
|
440
|
-
- Para múltipla seleção, use `multiple: true` no `FieldDefinition` quando aplicável.
|
|
441
|
-
- No runtime da tabela não é necessário registrar funções JSON/data manualmente; os helpers já são embutidos.
|
|
442
|
-
- APIs com busca: suporte a um parâmetro livre de texto (por ex. `search`) facilita a experiência do autocomplete.
|
|
443
|
-
|
|
444
|
-
Exemplo de FieldDefinition (lookup)
|
|
445
|
-
```ts
|
|
446
|
-
import type { FieldDefinition } from '@praxisui/core';
|
|
447
|
-
|
|
448
|
-
const fields: FieldDefinition[] = [
|
|
449
|
-
{
|
|
450
|
-
name: 'customer',
|
|
451
|
-
type: 'object',
|
|
452
|
-
// Habilita o lookup via GenericCrudService
|
|
453
|
-
resourcePath: 'customers',
|
|
454
|
-
// Mapeia o id/label que virão do backend
|
|
455
|
-
valueField: 'id',
|
|
456
|
-
displayField: 'name',
|
|
457
|
-
// Se quiser multiseleção em todo o app
|
|
458
|
-
// multiple: true,
|
|
459
|
-
},
|
|
460
|
-
];
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
Operadores relacionais em JSON Logic
|
|
464
|
-
- `id ==` -> `{ "jsonEq": [{ "var": "customer" }, "$.id", 10] }`
|
|
465
|
-
- `id in` -> `{ "jsonIn": [{ "var": "customer" }, "$.id", [10, 20]] }`
|
|
466
|
-
- `has property` -> `{ "hasJsonKey": [{ "var": "customer" }, "$.meta.etag"] }`
|
|
467
|
-
- Condições por caminho (join simples): use `jsonEq/jsonIn` sobre o path necessário
|
|
468
|
-
|
|
469
|
-
UI (no editor)
|
|
470
|
-
- `id ==` (single): autocomplete + chip “label [id]†com ação “Limparâ€.
|
|
471
|
-
- `id in` (multi): autocomplete + chips removÃveis por item.
|
|
472
|
-
- `has property`: input do caminho (ex.: `$.meta.etag`).
|
|
473
|
-
|
|
474
|
-
Formato esperado do payload (relacional)
|
|
475
|
-
|
|
476
|
-
Para que o editor/preview e o runtime avaliem corretamente as regras relacionais, o valor do campo deve ser um objeto (ou compatÃvel com JSON) contendo ao menos o identificador e, opcionalmente, propriedades exibidas:
|
|
477
|
-
|
|
478
|
-
```ts
|
|
479
|
-
// Exemplo de linha (row) com campo relacional "customer"
|
|
480
|
-
const row = {
|
|
481
|
-
id: 123,
|
|
482
|
-
customer: {
|
|
483
|
-
id: 10, // ↠usado em id == / id in via jsonEq/jsonIn
|
|
484
|
-
name: 'ACME', // ↠usado para exibir label no autocomplete/chip
|
|
485
|
-
meta: { etag: 'v1' },
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
// Regras relacionais em JSON Logic
|
|
490
|
-
// id ==
|
|
491
|
-
// { "jsonEq": [{ "var": "customer" }, "$.id", 10] }
|
|
492
|
-
// id in
|
|
493
|
-
// { "jsonIn": [{ "var": "customer" }, "$.id", [10, 20]] }
|
|
494
|
-
// has property
|
|
495
|
-
// { "hasJsonKey": [{ "var": "customer" }, "$.meta.etag"] }
|
|
496
|
-
// caminho/valor (join simples)
|
|
497
|
-
// { "jsonEq": [{ "var": "customer" }, "$.name", "ACME"] }
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
Notas
|
|
501
|
-
- O editor usa `displayField` (ex.: `name`) para compor o rótulo, e `valueField` (ex.: `id`) para gerar o AST Json Logic correspondente.
|
|
502
|
-
- Se o backend devolver o relacionamento já "normalizado" (ex.: `customerId` e `customerName` na mesma linha), você ainda pode expressar condições com operadores padrão (`==`, `in`) no campo numérico/literal — a abordagem relacional é útil quando o payload contém o objeto.
|
|
503
|
-
|
|
504
|
-
Onde é consumido (preview e runtime)
|
|
505
|
-
|
|
506
|
-
- Preview/validação no Editor de Regras:
|
|
507
|
-
- Authoring canônico manual e visual em JSON Logic: `projects/praxis-table/src/lib/rules-editor/table-rules-editor.component.ts`
|
|
508
|
-
- Runtime na Tabela (aplica `rowConditionalStyles`/`conditionalStyles`):
|
|
509
|
-
- JSON Logic canônico: `projects/praxis-table/src/lib/praxis-table.ts`
|
|
510
|
-
|
|
511
|
-
Observação
|
|
512
|
-
- JSON Logic é o contrato canônico para novas regras e para o editor de regras da tabela.
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
### âš™ï¸ Painel de Configurações
|
|
516
|
-
|
|
517
|
-
Para abrir o painel de configurações, habilite o modo de edição na tabela:
|
|
518
|
-
|
|
519
|
-
```html
|
|
520
|
-
<praxis-table [enableCustomization]="true"></praxis-table>
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
Um botão de engrenagem será exibido no canto superior direito. Ao clicar nele, o `SettingsPanel` é aberto permitindo ajustar:
|
|
524
|
-
|
|
525
|
-
- **Comportamento**: paginação, ordenação, filtros e recursos avançados.
|
|
526
|
-
- **Colunas**: visibilidade, ordem, largura e estilo.
|
|
527
|
-
- **Toolbar**: ações e botões da barra de ferramentas.
|
|
528
|
-
- **Mensagens**: textos e rótulos exibidos na interface.
|
|
529
|
-
|
|
530
|
-
As alterações podem ser aplicadas temporariamente com **Aplicar** ou salvas de forma persistente com **Salvar & Fechar**.
|
|
531
|
-
|
|
532
|
-
### Assistente de IA da Tabela
|
|
533
|
-
|
|
534
|
-
Quando `enableCustomization` esta ativo, a tabela expoe o assistente de IA pelo shell canonico de `@praxisui/ai`.
|
|
535
|
-
O chrome conversacional, `sessionId`, `clientTurnId`, respostas rapidas e ciclo de revisao/aplicacao sao orquestrados
|
|
536
|
-
por `PraxisAssistantTurnOrchestratorService`; a semantica especifica da tabela continua em `TableAiAdapter` e no
|
|
537
|
-
`TableAgenticAuthoringTurnFlow`.
|
|
538
|
-
|
|
539
|
-
## Agentic Authoring & Manifest
|
|
540
|
-
|
|
541
|
-
Para acoes globais de `toolbar`, `row` e `bulk`, o authoring agentic aceita somente `GlobalActionRef` governado:
|
|
542
|
-
`actionId`, `payload`, `payloadExpr` e `meta`. O schema bloqueia propriedades arbitrarias e mantem `payloadExpr`
|
|
543
|
-
como escape avancado explicito, nao como objeto livre. Configuracoes novas devem persistir a acao em
|
|
544
|
-
`effects[].kind = "global-action"` com `effects[].globalAction`; `globalAction` plano permanece apenas como fallback
|
|
545
|
-
de compatibilidade para documentos existentes.
|
|
546
|
-
|
|
547
|
-
O `@praxisui/table` declara authoring agentic através de um `ComponentAuthoringManifest` canônico. O manifesto é a fonte versionada para descoberta de alvos, operações e validações de configuração da tabela.
|
|
548
|
-
|
|
549
|
-
- **Component ID:** `praxis-table`
|
|
550
|
-
- **Config Schema:** `TableConfig`
|
|
551
|
-
- **Alvos Editáveis:** `column`, `computedColumn`, `renderer`, `conditionalRenderer`, `rowAction`, `toolbarAction`, `toolbar`, `filter`, `grouping`, `selection`, `export`, `appearance`, `expansion`, `rule`, `meta`, `bulkAction`, `contextAction`, `pagination`, `sorting`, `interaction`, `loading`, `emptyState`, `resizing`, `dragging`, `editing`, `messages`, `localization`, `performance`, `data`, `accessibility`.
|
|
552
|
-
- **Famílias de Operações:** Operações com target resolvers próprios por operação, alinhadas ao contrato canônico de configuração da tabela e aos paths publicados pelo pacote.
|
|
553
|
-
- **Validação:** Validadores determinísticos cobrem unicidade, integridade de caminhos, suporte a renderizadores, presets de formatação, segurança de estilos, segurança de resource binding somente em `expansion.detailSource.configure`, apresentação do detalhe expansível em `expansion.detailPresentation.configure`, e preservação de round-trip do editor.
|
|
554
|
-
|
|
555
|
-
O manifesto (v2.0.0) é exportado como `PRAXIS_TABLE_AUTHORING_MANIFEST` e projetado no `ai_registry` como contrato canônico de authoring. Essa projeção não deve ser tratada como aprovação semântica automática: cada operação precisa manter evidência testada de target resolver, paths canônicos, efeitos coerentes e validators pertinentes ao que a operação realmente edita. O gate `validate:authoring-contracts` deve rejeitar cobertura nominal, como validators globais sem uso ou validators ligados a operações não relacionadas.
|
|
556
|
-
|
|
557
|
-
## 🚀 Instalação
|
|
558
|
-
|
|
559
|
-
```bash
|
|
560
|
-
npm install @praxisui/ai@beta @praxisui/core@beta @praxisui/table@beta
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
Peers necessários (instale no app host):
|
|
564
|
-
- `@angular/core` `^20.0.0`, `@angular/common` `^20.0.0`
|
|
565
|
-
- `@praxisui/ai` `^9.0.0-beta.0`
|
|
566
|
-
- `@praxisui/core` `^0.0.1`
|
|
567
|
-
- `@praxisui/dynamic-fields` `^0.0.1` (quando usar editores/inputs dinâmicos)
|
|
568
|
-
- `@praxisui/dynamic-form` `^0.0.1` (quando integrar com formulários dinâmicos)
|
|
569
|
-
- `@praxisui/settings-panel` `^0.0.1` (para painel de configuração embutido)
|
|
570
|
-
|
|
571
|
-
## 📠Uso Básico
|
|
572
|
-
|
|
573
|
-
### Conectando ao Backend com `resourcePath`
|
|
574
|
-
|
|
575
|
-
A forma mais poderosa de usar a `<praxis-table>` é conectá-la diretamente a um endpoint de API compatÃvel com o ecossistema Praxis. Isso é feito através do input `resourcePath`.
|
|
576
|
-
|
|
577
|
-
Quando `resourcePath` é fornecido, a tabela se torna "inteligente":
|
|
578
|
-
|
|
579
|
-
1. **Busca automática de dados**: A tabela gerencia a paginação, ordenação e filtros, fazendo as requisições necessárias ao backend.
|
|
580
|
-
2. **Geração dinâmica de colunas**: A tabela busca os metadados (schema) do backend para gerar as colunas automaticamente, respeitando as configurações definidas no `praxis-metadata-core` (via anotação `@UISchema`).
|
|
581
|
-
|
|
582
|
-
```html
|
|
583
|
-
<!-- Exemplo no template do seu componente -->
|
|
584
|
-
<praxis-table resourcePath="human-resources/departamentos" [enableCustomization]="true"> </praxis-table>
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
Neste exemplo:
|
|
588
|
-
|
|
589
|
-
- `resourcePath="human-resources/departamentos"` instrui a tabela a se comunicar com o endpoint `/api/human-resources/departamentos`.
|
|
590
|
-
- A tabela fará requisições como `POST /api/human-resources/departamentos/filter` para obter os dados e `GET /schemas/filtered?path=/api/human-resources/departamentos/filter&operation=post&schemaType=response` para obter o schema estrutural usado no bootstrap das colunas.
|
|
591
|
-
- `enableCustomization` controla explicitamente o gate de edição visual. O default canônico agora é `false`; declare `[enableCustomization]="true"` quando o host quiser expor customização e surfaces editoriais.
|
|
592
|
-
|
|
593
|
-
### Consulta Declarativa com `queryContext`
|
|
594
|
-
|
|
595
|
-
Para orquestração entre widgets em `praxis-dynamic-page`, o contrato semântico primário da tabela passa a ser `queryContext`.
|
|
596
|
-
|
|
597
|
-
```html
|
|
598
|
-
<praxis-table
|
|
599
|
-
resourcePath="human-resources/departamentos"
|
|
600
|
-
[queryContext]="{
|
|
601
|
-
filters: { status: 'ACTIVE' },
|
|
602
|
-
sort: ['nome,asc'],
|
|
603
|
-
page: { index: 0, size: 25 }
|
|
604
|
-
}"
|
|
605
|
-
></praxis-table>
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
Regras atuais:
|
|
609
|
-
|
|
610
|
-
- `queryContext.filters` já participa do pipeline efetivo da tabela
|
|
611
|
-
- `filterCriteria` continua aceito como bridge legada de compatibilidade
|
|
612
|
-
- em novo authoring, examples e bindings, prefira `queryContext`
|
|
613
|
-
|
|
614
|
-
### Empty State + Quick Connect (sem `resourcePath`)
|
|
615
|
-
|
|
616
|
-
Se a tabela for renderizada sem `resourcePath`, um cartão de "Empty State" é exibido convidando a conectar o componente a um recurso. Basta clicar em "Conectar a recurso" para abrir um painel rápido com um único campo:
|
|
617
|
-
|
|
618
|
-
```text
|
|
619
|
-
resourcePath: ex.: human-resources/departamentos
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
Ao aplicar/salvar, a `<praxis-table>` é automaticamente configurada para buscar o schema e os dados do backend. Esse fluxo evita telas em branco e simplifica o onboarding do componente em páginas novas.
|
|
623
|
-
|
|
624
|
-
### Fluxo de Comunicação do `resourcePath`
|
|
625
|
-
|
|
626
|
-
O diagrama abaixo ilustra como a propriedade `resourcePath` conecta o componente frontend ao backend no runtime atual da tabela. Antes de iniciar o fluxo remoto, a lib resolve o modo de dados com precedência **`resourcePath` explÃcito > `data` local > conexão persistida**. Quando o modo efetivo é `remote`, o bootstrap ocorre em três etapas principais: **Configurar**, **Carregar Schema estrutural** e **Buscar Dados**.
|
|
627
|
-
|
|
628
|
-
```mermaid
|
|
629
|
-
sequenceDiagram
|
|
630
|
-
participant FE_Component as Componente Angular<br>(departamentos.html)
|
|
631
|
-
participant Praxis_Table as @praxisui/table<br>(praxis-table.ts)
|
|
632
|
-
participant Crud_Service as @praxisui/core<br>(generic-crud.service.ts)
|
|
633
|
-
participant Docs_Controller as ApiDocsController<br>(/schemas/filtered)
|
|
634
|
-
participant BE_Controller as Backend Controller<br>(DepartamentoController.java)
|
|
635
|
-
participant Abstract_Controller as AbstractCrudController<br>(/filter)
|
|
636
|
-
|
|
637
|
-
FE_Component->>Praxis_Table: Usa o componente com <br> <praxis-table resourcePath="human-resources/departamentos">
|
|
638
|
-
|
|
639
|
-
activate Praxis_Table
|
|
640
|
-
Praxis_Table->>Praxis_Table: ngOnInit()/ngOnChanges() resolve dataMode
|
|
641
|
-
Note over Praxis_Table: remote quando há resourcePath efetivo
|
|
642
|
-
Praxis_Table->>Crud_Service: 1. Chama crudService.configure("human-resources/departamentos")
|
|
643
|
-
|
|
644
|
-
activate Crud_Service
|
|
645
|
-
Crud_Service->>Crud_Service: Armazena o resourcePath
|
|
646
|
-
deactivate Crud_Service
|
|
647
|
-
|
|
648
|
-
Praxis_Table->>Praxis_Table: 2. Chama this.loadSchema()
|
|
649
|
-
Praxis_Table->>Crud_Service: Chama crudService.getSchema()
|
|
650
|
-
|
|
651
|
-
activate Crud_Service
|
|
652
|
-
Crud_Service->>Crud_Service: schemaUrl() preserva o base path canônico
|
|
653
|
-
Crud_Service->>Crud_Service: Deriva path="/api/human-resources/departamentos/filter"<br>operation="post", schemaType="response"
|
|
654
|
-
Crud_Service->>Docs_Controller: GET /schemas/filtered?path=.../filter&operation=post&schemaType=response
|
|
655
|
-
deactivate Crud_Service
|
|
656
|
-
|
|
657
|
-
activate Docs_Controller
|
|
658
|
-
Docs_Controller->>Docs_Controller: Resolve grupo OpenAPI a partir do path
|
|
659
|
-
Docs_Controller->>Docs_Controller: Filtra payload estrutural + x-ui
|
|
660
|
-
Docs_Controller-->>Crud_Service: 200/304 com schema + ETag/X-Schema-Hash
|
|
661
|
-
deactivate Docs_Controller
|
|
662
|
-
|
|
663
|
-
activate Crud_Service
|
|
664
|
-
Crud_Service-->>Praxis_Table: Normaliza schema e retorna FieldDefinition[]
|
|
665
|
-
deactivate Crud_Service
|
|
666
|
-
|
|
667
|
-
Praxis_Table->>Praxis_Table: Constrói as colunas da tabela<br>a partir do schema recebido
|
|
668
|
-
|
|
669
|
-
Praxis_Table->>Praxis_Table: 3. Chama this.fetchData()
|
|
670
|
-
Praxis_Table->>Crud_Service: Chama crudService.filter(...) para buscar dados
|
|
671
|
-
|
|
672
|
-
activate Crud_Service
|
|
673
|
-
Crud_Service->>Crud_Service: getEndpointUrl('filter') constrói a URL: <br> "/api/human-resources/departamentos/filter"
|
|
674
|
-
Crud_Service->>BE_Controller: Requisição HTTP POST para .../filter
|
|
675
|
-
deactivate Crud_Service
|
|
676
|
-
|
|
677
|
-
activate BE_Controller
|
|
678
|
-
BE_Controller->>Abstract_Controller: Herda o método que lida com @PostMapping("/filter")
|
|
679
|
-
|
|
680
|
-
activate Abstract_Controller
|
|
681
|
-
Abstract_Controller->>Abstract_Controller: Processa a requisição e busca os dados
|
|
682
|
-
Abstract_Controller-->>BE_Controller: Retorna os dados
|
|
683
|
-
deactivate Abstract_Controller
|
|
684
|
-
|
|
685
|
-
BE_Controller-->>Crud_Service: Resposta HTTP com os dados (Page<DepartamentoDTO>)
|
|
686
|
-
deactivate BE_Controller
|
|
687
|
-
|
|
688
|
-
activate Crud_Service
|
|
689
|
-
Crud_Service-->>Praxis_Table: Retorna Page<T> com os dados paginados
|
|
690
|
-
deactivate Crud_Service
|
|
691
|
-
|
|
692
|
-
Praxis_Table->>Praxis_Table: Atualiza o dataSource da tabela com os dados recebidos
|
|
693
|
-
deactivate Praxis_Table
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
Notas importantes:
|
|
697
|
-
|
|
698
|
-
- O host informa `resourcePath` sem o prefixo `/api`; o `GenericCrudService` resolve as URLs reais do backend a partir da configuração de API do host.
|
|
699
|
-
- O bootstrap de schema não depende mais de uma chamada frontend para `GET /{resource}/schemas`; o runtime consome diretamente `/schemas/filtered` com `ETag`/`X-Schema-Hash`.
|
|
700
|
-
- O `praxis-filter` acoplado acompanha o mesmo `resourcePath` em modo remoto e deriva seu schema via `/schemas/filtered?path=.../filter&operation=post&schemaType=request`.
|
|
701
|
-
- Sem `resourcePath` efetivo, a tabela não entra nesse fluxo remoto; ela cai em `local` ou `empty` conforme a precedência canônica.
|
|
702
|
-
|
|
703
|
-
## â„¹ï¸ Dicas de UX (Tabela)
|
|
704
|
-
|
|
705
|
-
- Multi‑sort: habilite em Comportamento → Ordenação. No uso, mantenha Ctrl/Cmd pressionado ao clicar em múltiplas colunas.
|
|
706
|
-
- Duplo clique na linha: configure em Comportamento → Interação (habilitar e escolha a ação: editar/visualizar/personalizada).
|
|
707
|
-
- Virtualização: habilite em Comportamento → Virtualização para listas grandes (requer altura de linha previsÃvel).
|
|
708
|
-
- Densidade e linhas de grade: ajuste em Comportamento → Aparência rápida (compacta/confortável/espaçosa; bordas horizontais/verticais).
|
|
709
|
-
|
|
710
|
-
## Rolagem Horizontal (owner do scroll)
|
|
711
|
-
|
|
712
|
-
Por padrão, a `<praxis-table>` agora assume a responsabilidade pela rolagem horizontal quando o conteúdo excede a largura do container. Isso evita cortes de conteúdo e comportamentos inconsistentes entre projetos.
|
|
713
|
-
|
|
714
|
-
### Input: `horizontalScroll`
|
|
715
|
-
|
|
716
|
-
```
|
|
717
|
-
@Input() horizontalScroll: 'auto' | 'wrap' | 'none' = 'auto';
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
- `auto` (padrão): a tabela cria um viewport com `overflow-x: auto` e permite que a tabela interna cresça com `width: max-content`. Quando a soma das colunas > container, aparece a barra de rolagem horizontal neste viewport.
|
|
721
|
-
- `wrap`: permite quebra de linha nas células (libera `white-space: normal`), reduzindo a largura necessária; ideal quando você prefere minimizar a rolagem horizontal.
|
|
722
|
-
- `none`: desabilita o comportamento interno; o host (página) deve fornecer o container com `overflow-x: auto` e as regras de largura necessárias.
|
|
723
|
-
|
|
724
|
-
Exemplo:
|
|
725
|
-
|
|
726
|
-
```html
|
|
727
|
-
<!-- Comportamento padrão (auto) -->
|
|
728
|
-
<praxis-table resourcePath="employees"></praxis-table>
|
|
729
|
-
|
|
730
|
-
<!-- Reduz rolagem: permite quebra de linha nas células -->
|
|
731
|
-
<praxis-table resourcePath="employees" horizontalScroll="wrap"></praxis-table>
|
|
732
|
-
|
|
733
|
-
<!-- Host controla o scroll horizontal -->
|
|
734
|
-
<div class="table-shell" style="overflow-x:auto">
|
|
735
|
-
<praxis-table resourcePath="employees" horizontalScroll="none"></praxis-table>
|
|
736
|
-
<!-- host pode usar width: max-content na tabela interna conforme seu design system -->
|
|
737
|
-
</div>
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
### Editor de Configuração (Settings Panel)
|
|
741
|
-
|
|
742
|
-
Na aba “Visão Geral & Comportamentoâ€, há um seletor “Scroll Horizontal†com as opções `Auto`, `Wrap` e `Host` (none). As alterações podem ser aplicadas (Aplicar) ou salvas (Salvar & Fechar). O valor é persistido junto com outras preferências da tabela.
|
|
743
|
-
|
|
744
|
-
### Notas
|
|
745
|
-
|
|
746
|
-
- O caminho virtual (CDK Virtual Scroll) permanece sendo o owner da rolagem vertical; o viewport horizontal é um contêiner externo que não interfere no scroll vertical.
|
|
747
|
-
- Cabeçalho sticky continua funcional. Teste combinações com colunas sticky/virt para seu caso.
|
|
748
|
-
- Para grids alternativos (ex.: Kendo), as mesmas regras de `max-content`/`min-width: 100%` se aplicam ao elemento de tabela interno.
|
|
749
|
-
|
|
750
|
-
## Virtualização (CDK)
|
|
751
|
-
|
|
752
|
-
Quando `behavior.virtualization.enabled` estiver ativo, as linhas da tabela são renderizadas com `cdk-virtual-scroll-viewport` (cabeçalho permanece igual).
|
|
753
|
-
|
|
754
|
-
- Propriedades:
|
|
755
|
-
- `itemHeight`: altura da linha (px); padrão 44.
|
|
756
|
-
- `bufferSize`: itens adicionais de buffer.
|
|
757
|
-
- `minContainerHeight`: altura mÃnima do viewport (px ou CSS, ex.: `320` ou `50vh`).
|
|
758
|
-
- `strategy`: `fixed` | `dynamic` (atual uso visual não altera lógica de medição).
|
|
759
|
-
- Observação: sticky/pinned em colunas pode ter limitações em conjunto com virtual scroll (mantido sob feature flag; caminho não‑virtual preservado como fallback).
|
|
760
|
-
|
|
761
|
-
## Paginação (posição/estilo)
|
|
762
|
-
|
|
763
|
-
### Concept Usage
|
|
764
|
-
|
|
765
|
-
- Schema-driven UI
|
|
766
|
-
- Self-describing APIs
|
|
767
|
-
- Rules engines and specifications
|
|
768
|
-
- Configuration-driven development
|
|
769
|
-
|
|
770
|
-
- `behavior.pagination.position`: `top` | `bottom` | `both`.
|
|
771
|
-
- `behavior.pagination.style`: `default` | `compact` (aplica classe de estilo no paginator).
|
|
772
|
-
|
|
773
|
-
Nota sobre estratégia (client vs server)
|
|
774
|
-
- Se `behavior.pagination.strategy` não estiver definido, a tabela assume `server` automaticamente quando há `resourcePath` (dados remotos). Caso contrário, usa `client`.
|
|
775
|
-
- O mesmo vale para `behavior.sorting.strategy`.
|
|
776
|
-
|
|
777
|
-
## Duplo clique na linha
|
|
778
|
-
|
|
779
|
-
Ative em Comportamento → Interação.
|
|
780
|
-
|
|
781
|
-
- Output: `rowDoubleClick` com payload `{ action: string; row: any }`.
|
|
782
|
-
- Ação: `edit` | `view` | `custom` (quando `custom`, use `customAction` no Behavior para definir o identificador).
|
|
783
|
-
|
|
784
|
-
Exemplo de uso (template do host):
|
|
785
|
-
|
|
786
|
-
```html
|
|
787
|
-
<praxis-table
|
|
788
|
-
[config]="tableConfig"
|
|
789
|
-
[resourcePath]="'human-resources/departamentos'"
|
|
790
|
-
(rowDoubleClick)="onRowDoubleClick($event)">
|
|
791
|
-
</praxis-table>
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
```ts
|
|
795
|
-
onRowDoubleClick(evt: { action: string; row: any }) {
|
|
796
|
-
if (evt.action === 'edit') {
|
|
797
|
-
// abrir editor
|
|
798
|
-
} else if (evt.action === 'view') {
|
|
799
|
-
// abrir detalhe somente leitura
|
|
800
|
-
} else {
|
|
801
|
-
// ação customizada
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
## Ordenação inicial (defaultSort)
|
|
807
|
-
|
|
808
|
-
- Configure em `behavior.sorting.defaultSort`.
|
|
809
|
-
- Aceita objeto único (coluna/direção) ou array para multi‑sort.
|
|
810
|
-
- Status runtime atual: `behavior.sorting.multiSort` está **schema-only** (apenas 1 critério efetivo em runtime).
|
|
811
|
-
|
|
812
|
-
Exemplos:
|
|
813
|
-
|
|
814
|
-
```ts
|
|
815
|
-
sorting: {
|
|
816
|
-
enabled: true,
|
|
817
|
-
strategy: 'server',
|
|
818
|
-
multiSort: false,
|
|
819
|
-
defaultSort: { column: 'nome', direction: 'asc' },
|
|
820
|
-
}
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
```ts
|
|
824
|
-
sorting: {
|
|
825
|
-
enabled: true,
|
|
826
|
-
strategy: 'server',
|
|
827
|
-
multiSort: true,
|
|
828
|
-
defaultSort: [
|
|
829
|
-
{ column: 'status', direction: 'desc' },
|
|
830
|
-
{ column: 'nome', direction: 'asc' },
|
|
831
|
-
],
|
|
832
|
-
}
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
Observação: quando informado, o defaultSort é aplicado na carga inicial se não houver estado de ordenação ativo.
|
|
836
|
-
Observação enterprise: para evitar ambiguidade de contrato, mantenha `multiSort: false` no runtime atual. Se um array for informado em `defaultSort`, trate como payload de compatibilidade/roadmap e valide no host como single-sort efetivo.
|
|
837
|
-
|
|
838
|
-
### Roadmap / migração de multiSort
|
|
839
|
-
|
|
840
|
-
- Curto prazo: `multiSort` segue schema-only no runtime.
|
|
841
|
-
- Recomendação de migração: desenhar integrações com fallback para ordenação única (`column` + `direction`).
|
|
842
|
-
- Planejamento: quando multi-sort runtime for implementado, publicar release note com guia de ativação gradual (feature-flag + validação de payload legado).
|
|
843
|
-
|
|
844
|
-
## Coluna de ações (sticky)
|
|
845
|
-
|
|
846
|
-
Nota: por padrão a coluna de ações vem desabilitada. Habilite explicitamente em `actions.row.enabled` e defina as ações desejadas.
|
|
847
|
-
|
|
848
|
-
- Fixe a coluna de ações no inÃcio/fim configurando `actions.row.sticky`:
|
|
849
|
-
|
|
850
|
-
```ts
|
|
851
|
-
actions: {
|
|
852
|
-
row: {
|
|
853
|
-
enabled: true,
|
|
854
|
-
sticky: 'end', // true | 'start' | 'end'
|
|
855
|
-
width: '120px',
|
|
856
|
-
actions: [ /* ... */ ],
|
|
857
|
-
},
|
|
858
|
-
}
|
|
859
|
-
```
|
|
860
|
-
|
|
861
|
-
Observação: em modo virtualizado, sticky pode ter limitações dependendo do layout.
|
|
862
|
-
|
|
863
|
-
## Colunas de dados (sticky)
|
|
864
|
-
|
|
865
|
-
Fixe colunas especÃficas no inÃcio/fim usando `columns[].sticky`:
|
|
866
|
-
|
|
867
|
-
```ts
|
|
868
|
-
columns: [
|
|
869
|
-
{ field: 'id', header: 'ID', width: '80px', sticky: 'start' },
|
|
870
|
-
{ field: 'nome', header: 'Nome' },
|
|
871
|
-
{ field: 'status', header: 'Status', sticky: 'end' },
|
|
872
|
-
]
|
|
873
|
-
```
|
|
874
|
-
|
|
875
|
-
- Valores aceitos: `true` | `'start'` | `'end'`.
|
|
876
|
-
- `true` equivale a `'start'` (fixa no inÃcio).
|
|
877
|
-
- Dica: combine com `width` para evitar jitter de layout.
|
|
878
|
-
|
|
879
|
-
Onde configurar no Editor Visual:
|
|
880
|
-
|
|
881
|
-
- Aba “Colunas†→ grupo “Posição Fixaâ€: escolha entre Nenhum / InÃcio / Fim.
|
|
882
|
-
- Para a coluna de ações, use a aba “Barra de Ferramentas & Ações†→ grupo “Coluna de Ações†→ “Fixar coluna de açõesâ€.
|
|
883
|
-
|
|
884
|
-
## 🔒 Verificação de Schema (ETag) e Notificações (somente em customização)
|
|
885
|
-
|
|
886
|
-
Quando já existe uma configuração salva (colunas presentes), a tabela não baixa o schema bruto do servidor para montar as colunas. Em vez disso, valida se há uma nova versão do schema usando ETag/If-None-Match. Notificações são exibidas somente quando o modo de customização está ativo (`enableCustomization = true`).
|
|
887
|
-
|
|
888
|
-
### Comportamento
|
|
889
|
-
|
|
890
|
-
- Primeira vez (sem colunas):
|
|
891
|
-
- Baixa o schema (200), gera as colunas e persiste `config.meta` (incluindo `schemaId`, `serverHash`, `lastVerifiedAt`).
|
|
892
|
-
- Próximas vezes (colunas já existentes):
|
|
893
|
-
- Faz uma verificação leve em `/schemas/filtered` com `If-None-Match: "<serverHash>"`.
|
|
894
|
-
- 304 Not Modified: atualiza `config.meta.lastVerifiedAt` e segue usando a configuração local.
|
|
895
|
-
- 200 OK (hash mudou): atualiza `config.meta.serverHash/lastVerifiedAt`, marca estado `schemaOutdated=true` e (em modo de customização) exibe aviso e CTA para reconciliar. O schema bruto não é usado/armazenado nesta etapa.
|
|
896
|
-
|
|
897
|
-
### Diagrama: Verificação de Schema via ETag
|
|
898
|
-
|
|
899
|
-
```mermaid
|
|
900
|
-
sequenceDiagram
|
|
901
|
-
autonumber
|
|
902
|
-
participant PT as PraxisTable
|
|
903
|
-
participant GS as GenericCrudService (@praxisui/core)
|
|
904
|
-
participant API as Backend (/schemas/filtered)
|
|
905
|
-
|
|
906
|
-
opt Primeira vez (sem colunas)
|
|
907
|
-
PT->>GS: getSchema() (gera colunas)
|
|
908
|
-
GS->>API: GET /schemas/filtered (sem If-None-Match)
|
|
909
|
-
API-->>GS: 200 + ETag/X-Schema-Hash
|
|
910
|
-
GS-->>PT: FieldDefinition[] (normalized)
|
|
911
|
-
PT->>PT: Persiste config + meta (serverHash, lastVerifiedAt)
|
|
912
|
-
end
|
|
913
|
-
|
|
914
|
-
opt Próximas vezes (colunas existentes)
|
|
915
|
-
PT->>API: GET /schemas/filtered?path=... (If-None-Match: "<serverHash>")
|
|
916
|
-
alt Igual (sem mudanças)
|
|
917
|
-
API-->>PT: 304 Not Modified (sem body)
|
|
918
|
-
PT->>PT: Atualiza lastVerifiedAt; segue usando config
|
|
919
|
-
else Diferente (mudou)
|
|
920
|
-
API-->>PT: 200 OK (com ETag/X-Schema-Hash)
|
|
921
|
-
PT->>PT: Atualiza serverHash + lastVerifiedAt; schemaOutdated=true
|
|
922
|
-
Note over PT: Em customização: mostrar banner/snackbar + badge e CTA “Reconciliarâ€
|
|
923
|
-
end
|
|
924
|
-
end
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
### Inputs/Outputs
|
|
928
|
-
|
|
929
|
-
- Inputs (efetivos apenas quando `enableCustomization = true`):
|
|
930
|
-
- `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'`
|
|
931
|
-
- `snoozeMs: number = 86400000`
|
|
932
|
-
- `autoOpenSettingsOnOutdated: boolean = false`
|
|
933
|
-
- Outputs:
|
|
934
|
-
- `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; trigger?: string; tableId?: string }`
|
|
935
|
-
- Emitido tanto na verificação leve (304/200) quanto no bootstrap do schema (primeira carga via `loadSchema()`).
|
|
936
|
-
- `metadataChange: { trigger: string; meta: any; tableId?: string }`
|
|
937
|
-
- Emitido quando `config.meta` é atualizado (ex.: após bootstrap ou verificação ETag). Útil para sincronizar sidebars/bridges de metadados.
|
|
938
|
-
|
|
939
|
-
### Fallback Global (opcional)
|
|
940
|
-
|
|
941
|
-
- Quando os inputs do widget permanecem com os valores padrão da biblioteca e não há overrides locais equivalentes, a Tabela aplica como último fallback as preferências globais lidas via `GlobalConfigService.getSchemaPrefsGlobal()`.
|
|
942
|
-
- Cadeia de precedência (inalterada): `@Inputs (widget)` → Prefs do widget → Prefs da página → Prefs globais → Defaults.
|
|
943
|
-
- Não há persistência no widget a partir das globais; o fallback é aplicado somente em memória.
|
|
944
|
-
|
|
945
|
-
### Persistência por hash (ConfigStorage)
|
|
946
|
-
|
|
947
|
-
- `schemaIgnore:{tableId}:{serverHash}` → ignora avisos para este hash.
|
|
948
|
-
- `schemaSnooze:{tableId}:{serverHash}` → ISODate até quando não avisar (lembrar depois).
|
|
949
|
-
- `schemaNotified:{tableId}:{serverHash}` → evita snackbar duplicado.
|
|
950
|
-
|
|
951
|
-
### UX
|
|
952
|
-
|
|
953
|
-
- Banner inline (acima da tabela) quando `schemaOutdated=true`, com ações:
|
|
954
|
-
- Reconciliar (abre Configurações)
|
|
955
|
-
- Lembrar depois (snooze)
|
|
956
|
-
- Ignorar (silenciar para este hash)
|
|
957
|
-
- Snackbar opcional (uma vez por hash) com ação “Reconciliarâ€.
|
|
958
|
-
- Badge “!†na engrenagem com tooltip contextual: “Schema do servidor mudou — Reconciliarâ€.
|
|
959
|
-
|
|
960
|
-
### Exemplo de uso
|
|
961
|
-
|
|
962
|
-
```html
|
|
963
|
-
<praxis-table
|
|
964
|
-
[enableCustomization]="true"
|
|
965
|
-
[notifyIfOutdated]="'both'"
|
|
966
|
-
[snoozeMs]="12 * 60 * 60 * 1000"
|
|
967
|
-
[autoOpenSettingsOnOutdated]="false"
|
|
968
|
-
(schemaStatusChange)="onSchemaStatus($event)"
|
|
969
|
-
resourcePath="employees"
|
|
970
|
-
[config]="tableConfig">
|
|
971
|
-
</praxis-table>
|
|
972
|
-
```
|
|
973
|
-
|
|
974
|
-
```ts
|
|
975
|
-
onSchemaStatus(ev: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string }) {
|
|
976
|
-
if (ev.outdated) {
|
|
977
|
-
console.log('Schema mudou. Hash:', ev.serverHash);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
```
|
|
981
|
-
|
|
982
|
-
### Notas
|
|
983
|
-
|
|
984
|
-
- A verificação leve não grava nem usa o schema bruto quando `config.columns` já existe.
|
|
985
|
-
- Em ambientes sem customização (`enableCustomization=false`), não há notificações visuais; ainda assim `schemaStatusChange` é emitido e `config.meta.lastVerifiedAt` atualizado.
|
|
986
|
-
|
|
987
|
-
## Filtro Avançado (PraxisFilter)
|
|
988
|
-
|
|
989
|
-
### Boas práticas para estabilidade
|
|
990
|
-
|
|
991
|
-
- Evite literais em bindings para o carregador dinâmico:
|
|
992
|
-
- Não use `[fields]="[pinnedFieldMeta]"`; prefira um array estável como `pinnedFieldMetaArray` atualizado apenas quando o metadata mudar.
|
|
993
|
-
- Reutilize `FormGroup` quando possÃvel. Se precisar trocar o `FormGroup` sem mudar os campos, deixe o `DynamicFieldLoader` reatribuir os controles (rebind-only) em vez de recriar componentes.
|
|
994
|
-
- Evite recriar arrays/objetos desnecessariamente em `ngOnChanges`/`ngAfterViewInit`. Prefira atualizar valores (`setValue`, `patchValue`) e manter referências estáveis.
|
|
995
|
-
- Observers (Resize/Mutation): atualize `overlayOrigin` e largura apenas quando houver mudanças reais; isso evita loops de CD.
|
|
996
|
-
|
|
997
|
-
### Diagnóstico e Logs
|
|
998
|
-
|
|
999
|
-
- O `DynamicFieldLoader` possui logs de diagnóstico que podem ser habilitados em tempo de execução definindo `window.__PRAXIS_DEBUG_DFL__ = true` no console do navegador.
|
|
1000
|
-
- Quando desabilitado (default), o carregador reduz a verbosidade de logs.
|
|
1001
|
-
|
|
1002
|
-
### Comportamento do carregador (v20+)
|
|
1003
|
-
|
|
1004
|
-
- Guardas internas evitam re-renderizações desnecessárias:
|
|
1005
|
-
- Se o snapshot do conteúdo dos campos (nome + controlType) for idêntico e o `FormGroup` não tiver mudado, a renderização é ignorada.
|
|
1006
|
-
- Se apenas o `FormGroup` mudou (mesmo snapshot de campos), os controles são reatribuÃdos aos componentes existentes (rebind-only), preservando o estado visual e evitando recriações.
|
|
1007
|
-
|
|
1008
|
-
### Testes
|
|
1009
|
-
|
|
1010
|
-
- Há testes cobrindo:
|
|
1011
|
-
- Reatribuição de `FormControl` em troca de `FormGroup` (select/multiselect/autocomplete).
|
|
1012
|
-
- Ignorar re-render quando apenas a referência do array muda mas o conteúdo é igual.
|
|
1013
|
-
- Recriar componentes quando `controlType` muda.
|
|
1014
|
-
|
|
1015
|
-
### Fluxo de Schema e Metadados
|
|
1016
|
-
|
|
1017
|
-
- O `PraxisFilter` busca schema do DTO de filtro via `/schemas/filtered` com ETag/If-None-Match
|
|
1018
|
-
e emite `metaChanged` com `{ schemaId, serverHash, context }`.
|
|
1019
|
-
- O schema é normalizado e `advancedConfig.metadata` é preenchido para auditoria/telemetria.
|
|
1020
|
-
- Detalhes: `projects/praxis-core/docs/schema-flow.md` (canônico) e `docs/schemas/fluxo-schema.md` (resumo operacional) para cliente/caching, 200/304 e reconciliadores Form/Filter.
|
|
1021
|
-
|
|
1022
|
-
### Resolução da chave primária (idField)
|
|
1023
|
-
|
|
1024
|
-
- O backend anota o schema com `x-ui.resource.idField` (e `idFieldValid`) via `/schemas/filtered`.
|
|
1025
|
-
- A tabela adota o campo identificador automaticamente com a seguinte precedência:
|
|
1026
|
-
- `config.meta.idField` (persistido) → contexto runtime/serviço derivado do schema → `'id'`.
|
|
1027
|
-
- Se `config.meta.idField` divergir do servidor, o componente alerta o usuário e mantém o valor do TableConfig até reconciliação.
|
|
1028
|
-
- A resolução ocorre no `loadSchema()` e também é considerada em tempo de execução para evitar corridas.
|
|
1029
|
-
- Para recursos cuja PK não é `id`, defina `getIdFieldName()` no controller backend correspondente.
|
|
1030
|
-
|
|
1031
|
-
### Diagramas
|
|
1
|
+
# @praxisui/table
|
|
1032
2
|
|
|
1033
|
-
|
|
3
|
+
Enterprise data table for Praxis UI applications.
|
|
1034
4
|
|
|
1035
|
-
|
|
1036
|
-
sequenceDiagram
|
|
1037
|
-
autonumber
|
|
1038
|
-
participant PT as PraxisTable
|
|
1039
|
-
participant GS as GenericCrudService
|
|
1040
|
-
participant Docs as ApiDocsController (/schemas/filtered)
|
|
5
|
+
Use this package to render local or backend-driven data grids with `TableConfig`, filtering, sorting, pagination, selection, row actions, visual rules, configurable toolbar, rich detail rows, and optional AI-assisted authoring.
|
|
1041
6
|
|
|
1042
|
-
|
|
1043
|
-
PT->>GS: getSchema()
|
|
1044
|
-
GS->>GS: deriva path=/api/.../filter + operation=post + schemaType=response
|
|
1045
|
-
GS->>Docs: GET /schemas/filtered?path=.../filter&operation=post&schemaType=response
|
|
1046
|
-
Docs-->>GS: 200/304 schema + x-ui.resource.idField
|
|
1047
|
-
GS->>GS: cache + lastResourceMeta.idField
|
|
1048
|
-
GS-->>PT: FieldDefinition[] (normalizado)
|
|
1049
|
-
Note over PT: idField = config.meta.idField || runtime/schema || 'id'
|
|
1050
|
-
PT->>PT: construir colunas e renderizar
|
|
1051
|
-
```
|
|
7
|
+
## Official Links
|
|
1052
8
|
|
|
1053
|
-
|
|
9
|
+
- Documentation: https://praxisui.dev/docs/components
|
|
10
|
+
- Live demo: https://praxis-ui-4e602.web.app
|
|
11
|
+
- Quickstart app: https://github.com/codexrodrigues/praxis-ui-quickstart
|
|
12
|
+
- API quickstart: https://github.com/codexrodrigues/praxis-api-quickstart-public
|
|
13
|
+
- Source: https://github.com/codexrodrigues/praxis-ui-angular/tree/main/projects/praxis-table
|
|
14
|
+
- Issues: https://github.com/codexrodrigues/praxis-ui-angular/issues
|
|
1054
15
|
|
|
1055
|
-
|
|
1056
|
-
sequenceDiagram
|
|
1057
|
-
autonumber
|
|
1058
|
-
participant PT as PraxisTable
|
|
1059
|
-
participant GS as GenericCrudService
|
|
1060
|
-
participant API as Backend
|
|
16
|
+
## Install
|
|
1061
17
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
PT->>GS: delete(id)
|
|
1065
|
-
GS->>API: DELETE {resource}/{id}
|
|
1066
|
-
API-->>GS: 204 No Content
|
|
1067
|
-
GS-->>PT: sucesso
|
|
1068
|
-
PT->>PT: fetchData() em modo remoto
|
|
18
|
+
```bash
|
|
19
|
+
npm i @praxisui/table@latest
|
|
1069
20
|
```
|
|
1070
21
|
|
|
1071
|
-
|
|
22
|
+
Peer dependencies:
|
|
1072
23
|
|
|
1073
|
-
-
|
|
1074
|
-
-
|
|
1075
|
-
-
|
|
24
|
+
- `@angular/common`, `@angular/core`, `@angular/forms`, `@angular/cdk`, `@angular/material`, `@angular/router` `^21.0.0`
|
|
25
|
+
- `@praxisui/ai`, `@praxisui/core`, `@praxisui/dialog`, `@praxisui/dynamic-fields`, `@praxisui/dynamic-form`, `@praxisui/metadata-editor`, `@praxisui/rich-content`, `@praxisui/settings-panel`, `@praxisui/table-rule-builder` `^9.0.0-beta.1`
|
|
26
|
+
- `rxjs` `~7.8.0`
|
|
1076
27
|
|
|
1077
|
-
|
|
28
|
+
## Minimum Local Runtime
|
|
1078
29
|
|
|
1079
|
-
|
|
1080
|
-
Importante: o modo local está protegido por feature flag e exige `behavior.localDataMode.enabled = true` (default é `false`).
|
|
1081
|
-
Quando a flag está ativa, as operações de paginação, ordenação e filtro são executadas no lado do cliente.
|
|
30
|
+
Use local `data` and `config` when the host already owns the rows and column configuration.
|
|
1082
31
|
|
|
1083
|
-
```
|
|
1084
|
-
import {
|
|
1085
|
-
import { TableConfig } from
|
|
32
|
+
```ts
|
|
33
|
+
import { Component } from '@angular/core';
|
|
34
|
+
import { TableConfig } from '@praxisui/core';
|
|
35
|
+
import { PraxisTable } from '@praxisui/table';
|
|
1086
36
|
|
|
1087
37
|
@Component({
|
|
1088
|
-
selector: "app-example",
|
|
1089
38
|
standalone: true,
|
|
39
|
+
selector: 'app-local-table',
|
|
1090
40
|
imports: [PraxisTable],
|
|
1091
|
-
template: `
|
|
41
|
+
template: `
|
|
42
|
+
<praxis-table
|
|
43
|
+
tableId="employees-local-table"
|
|
44
|
+
[config]="config"
|
|
45
|
+
[data]="rows"
|
|
46
|
+
(rowClick)="open($event)">
|
|
47
|
+
</praxis-table>
|
|
48
|
+
`,
|
|
1092
49
|
})
|
|
1093
|
-
export class
|
|
1094
|
-
|
|
1095
|
-
|
|
50
|
+
export class LocalTableComponent {
|
|
51
|
+
rows = [
|
|
52
|
+
{ id: 1, name: 'Ana Souza', status: 'Active' },
|
|
53
|
+
{ id: 2, name: 'Bruno Lima', status: 'Inactive' },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
config: TableConfig = {
|
|
1096
57
|
columns: [
|
|
1097
|
-
{ field:
|
|
1098
|
-
{ field:
|
|
1099
|
-
{ field: "email", header: "Email", type: "string" },
|
|
58
|
+
{ field: 'name', header: 'Name', type: 'string' },
|
|
59
|
+
{ field: 'status', header: 'Status', type: 'string' },
|
|
1100
60
|
],
|
|
1101
|
-
|
|
1102
|
-
pagination: { enabled: true, pageSize: 10 },
|
|
1103
|
-
sorting: { enabled: true },
|
|
1104
|
-
filtering: { enabled: true },
|
|
1105
|
-
localDataMode: { enabled: true }, // obrigatório para fluxo local oficial
|
|
1106
|
-
},
|
|
1107
|
-
};
|
|
61
|
+
};
|
|
1108
62
|
|
|
1109
|
-
|
|
1110
|
-
{ id: 1, name: "João Silva", email: "joao@empresa.com" },
|
|
1111
|
-
{ id: 2, name: "Maria Santos", email: "maria@empresa.com" },
|
|
1112
|
-
// ... mais dados
|
|
1113
|
-
];
|
|
63
|
+
open(row: unknown): void {}
|
|
1114
64
|
}
|
|
1115
65
|
```
|
|
1116
66
|
|
|
1117
|
-
##
|
|
1118
|
-
|
|
1119
|
-
Quando a `<praxis-table>` opera com `resourcePath`, o runtime resolve o modo remoto e passa a delegar paginação, ordenação e filtros ao backend. Isso reduz tráfego, preserva a semântica do DTO de filtro e mantém o banco como fonte de verdade para `WHERE`, `ORDER BY`, `LIMIT` e `OFFSET`.
|
|
1120
|
-
|
|
1121
|
-
Importante: se você não configurar explicitamente as estratégias de paginação/ordenação no `TableConfig`, a tabela resolve automaticamente como `server` quando há `resourcePath`. Se preferir operar no cliente, defina `behavior.pagination.strategy = 'client'` e/ou `behavior.sorting.strategy = 'client'` conscientemente.
|
|
1122
|
-
|
|
1123
|
-
### Versão Pedagógica de Alto NÃvel
|
|
1124
|
-
|
|
1125
|
-
Esta é a leitura adequada para documentação pública e landing pages.
|
|
1126
|
-
|
|
1127
|
-
```mermaid
|
|
1128
|
-
sequenceDiagram
|
|
1129
|
-
participant Usuario as Usuário
|
|
1130
|
-
participant Tabela as praxis-table
|
|
1131
|
-
participant Frontend as GenericCrudService
|
|
1132
|
-
participant Backend as API Praxis
|
|
1133
|
-
participant Banco as Banco de Dados
|
|
1134
|
-
|
|
1135
|
-
Usuario->>Tabela: muda página, ordena ou aplica filtro
|
|
1136
|
-
Tabela->>Tabela: consolida filterCriteria + paginação + sort
|
|
1137
|
-
Tabela->>Frontend: filter(criteria, pageable)
|
|
1138
|
-
Frontend->>Backend: POST /api/.../filter?page&size&sort
|
|
1139
|
-
Backend->>Banco: executa consulta filtrada e paginada
|
|
1140
|
-
Banco-->>Backend: retorna página de resultados
|
|
1141
|
-
Backend-->>Frontend: responde com página de DTOs
|
|
1142
|
-
Frontend-->>Tabela: entrega Page<T>
|
|
1143
|
-
Tabela->>Tabela: atualiza linhas, paginação e estado visual
|
|
1144
|
-
```
|
|
1145
|
-
|
|
1146
|
-
Leitura pedagógica:
|
|
1147
|
-
|
|
1148
|
-
1. **A tabela captura a intenção do usuário**: página, ordenação e filtros viram estado de consulta.
|
|
1149
|
-
2. **O frontend envia o DTO de filtro para o endpoint canônico**: corpo JSON para filtro, query params para paginação e sort.
|
|
1150
|
-
3. **O backend aplica o filtro no conjunto remoto**: não é a UI que filtra localmente em modo remoto.
|
|
1151
|
-
4. **O resultado volta já paginado**: a tabela apenas renderiza a página retornada pela API.
|
|
1152
|
-
|
|
1153
|
-
### Versão Detalhada e Fiel ao Runtime Atual
|
|
1154
|
-
|
|
1155
|
-
Esta versão documenta o fluxo real do `praxis-table`, do `praxis-filter`, do `GenericCrudService` e do `praxis-metadata-starter`.
|
|
1156
|
-
|
|
1157
|
-
```mermaid
|
|
1158
|
-
sequenceDiagram
|
|
1159
|
-
participant Host as Host / bindings
|
|
1160
|
-
participant Tabela as PraxisTable
|
|
1161
|
-
participant Filtro as PraxisFilter
|
|
1162
|
-
participant Crud as GenericCrudService
|
|
1163
|
-
participant Controller as AbstractCrudController
|
|
1164
|
-
participant Service as BaseCrudService
|
|
1165
|
-
participant Builder as GenericSpecificationsBuilder
|
|
1166
|
-
participant Repo as JpaSpecificationExecutor
|
|
1167
|
-
|
|
1168
|
-
Host->>Tabela: fornece resourcePath
|
|
1169
|
-
Tabela->>Crud: configure(resourcePath)
|
|
1170
|
-
alt sem colunas prontas
|
|
1171
|
-
Tabela->>Crud: getSchema()
|
|
1172
|
-
Crud-->>Tabela: schema normalizado
|
|
1173
|
-
else com colunas prontas
|
|
1174
|
-
Tabela->>Tabela: verifyServerSchemaVersion()
|
|
1175
|
-
end
|
|
1176
|
-
|
|
1177
|
-
alt advancedFilters habilitado
|
|
1178
|
-
Tabela->>Filtro: monta praxis-filter
|
|
1179
|
-
Filtro->>Crud: getFilteredSchema(...) ou getSchema()
|
|
1180
|
-
Crud-->>Filtro: metadata de filtro
|
|
1181
|
-
end
|
|
1182
|
-
|
|
1183
|
-
alt paginação
|
|
1184
|
-
Tabela->>Tabela: onPageChange(pageIndex, pageSize)
|
|
1185
|
-
else ordenação
|
|
1186
|
-
Tabela->>Tabela: onSortChange(active, direction)
|
|
1187
|
-
Tabela->>Tabela: reseta pageIndex = 0
|
|
1188
|
-
else filtro avançado
|
|
1189
|
-
Filtro-->>Tabela: submit/change/clear
|
|
1190
|
-
Tabela->>Tabela: onAdvancedFilterSubmit(criteria)
|
|
1191
|
-
end
|
|
1192
|
-
|
|
1193
|
-
Tabela->>Tabela: fetchData()
|
|
1194
|
-
Tabela->>Tabela: monta PageableRequest com pageIndex, pageSize e sortField efetivo
|
|
1195
|
-
Tabela->>Crud: filter(filterCriteria, pageable)
|
|
1196
|
-
|
|
1197
|
-
Crud->>Crud: normalizeRangeCriteria(...)
|
|
1198
|
-
Crud->>Controller: POST /filter?page&size&sort
|
|
1199
|
-
Note over Crud,Controller: body = filterCriteria JSON
|
|
1200
|
-
|
|
1201
|
-
Controller->>Controller: lê page,size,sort de query params
|
|
1202
|
-
Controller->>Controller: PageableBuilder.from(page,size,sort,...)
|
|
1203
|
-
Controller->>Service: filterMappedWithIncludeIds(filterDTO, pageable, includeIds, toDto)
|
|
1204
|
-
|
|
1205
|
-
Service->>Builder: buildSpecification(filterDTO, pageable)
|
|
1206
|
-
Builder->>Builder: inspeciona campos @Filterable do DTO
|
|
1207
|
-
Builder-->>Service: Specification pronta
|
|
1208
|
-
Service->>Repo: findAll(specification, pageable)
|
|
1209
|
-
Repo-->>Service: Page<Entity>
|
|
1210
|
-
Service-->>Controller: Page<DTO>
|
|
1211
|
-
|
|
1212
|
-
Controller->>Controller: mapeia para EntityModel<DTO> e monta RestApiResponse.success(...)
|
|
1213
|
-
Controller-->>Crud: HTTP 200
|
|
1214
|
-
Crud->>Crud: unwrapPageResponse(...)
|
|
1215
|
-
Crud-->>Tabela: Page<T>
|
|
1216
|
-
Tabela->>Tabela: atualiza dataSubject, totalElements e paginator
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
### Pontos-Chave do Fluxo Real
|
|
1220
|
-
|
|
1221
|
-
1. **`resourcePath` não dispara só fetch**: em modo remoto, a tabela primeiro configura o `GenericCrudService` e pode carregar schema antes da primeira busca.
|
|
1222
|
-
2. **O filtro avançado vive em `praxis-filter`**: os eventos reais são `submit`, `change` e `clear`, que a tabela recebe como `onAdvancedFilterSubmit`, `onAdvancedFilterChange` e `onAdvancedFilterClear`.
|
|
1223
|
-
3. **O controller não recebe `Pageable` pronto do Spring nesse endpoint**: ele reconstrói o `Pageable` a partir de `page`, `size` e `sort`.
|
|
1224
|
-
4. **O backend do starter responde com `RestApiResponse<Page<...>>`**: o `GenericCrudService` faz `unwrapPageResponse(...)` para entregar `Page<T>` ao runtime da tabela.
|
|
1225
|
-
5. **A coluna ordenada pode mapear para `sortField`**: o `active` da UI não é necessariamente o campo final enviado ao backend.
|
|
1226
|
-
|
|
1227
|
-
## 🎨 Edição Visual da Tabela: O Poder do Low-Code
|
|
1228
|
-
|
|
1229
|
-
A `<praxis-table>` vem com um poderoso editor de configuração visual que permite personalizar quase todos os aspectos da sua tabela em tempo real, sem escrever uma única linha de código. O runtime expõe esse editor somente quando o host ativa `[enableCustomization]="true"`.
|
|
1230
|
-
|
|
1231
|
-
A seguir, veja os principais recursos que você pode configurar visualmente:
|
|
1232
|
-
|
|
1233
|
-
### 1. Gerenciamento de Colunas
|
|
1234
|
-
|
|
1235
|
-
Controle total sobre as colunas da sua tabela. Dentro do editor, você pode:
|
|
1236
|
-
|
|
1237
|
-
- **Reordenar com Arrastar e Soltar:** Simplesmente clique e arraste uma coluna para a posição desejada.
|
|
1238
|
-
- **Alterar Visibilidade:** Use a caixa de seleção ao lado de cada coluna para mostrá-la ou ocultá-la instantaneamente.
|
|
1239
|
-
- **Editar TÃtulos e Largura:** Clique em uma coluna para abrir suas propriedades e altere o texto do cabeçalho, defina uma largura fixa (ex: `150px`) ou deixe-a automática.
|
|
1240
|
-
|
|
1241
|
-
### 2. Transformação de Dados Sem Código
|
|
1242
|
-
|
|
1243
|
-
Converta dados brutos em informações claras e formatadas para o usuário.
|
|
1244
|
-
|
|
1245
|
-
- **Formatação Automática:** Selecione uma coluna e escolha seu "Tipo de Dado". Se escolher `Moeda`, os valores serão formatados como `R$ 1.234,56`. Se escolher `Data`, você pode selecionar formatos como `dd/MM/yyyy` ou `25 de janeiro de 2025`.
|
|
1246
|
-
- **Mapeamento de Valores:** Transforme códigos e valores brutos em texto legÃvel. Na seção "Mapeamento de Valores", você pode definir visualmente que o valor `true` deve ser exibido como "Ativo" e `false` como "Inativo", ou que o código `1` significa "Pendente" e `2` significa "Aprovado".
|
|
1247
|
-
|
|
1248
|
-
### 3. Colunas Calculadas com Fórmulas Visuais
|
|
1249
|
-
|
|
1250
|
-
Crie novas colunas dinamicamente a partir de dados existentes, sem precisar programar.
|
|
1251
|
-
|
|
1252
|
-
- **Concatenar Texto:** Crie uma "Coluna Calculada", escolha a fórmula "Concatenar" e selecione os campos `nome` e `sobrenome` para criar uma coluna "Nome Completo".
|
|
1253
|
-
- **Realizar Operações Matemáticas:** Use a fórmula "Operação Matemática" para criar uma coluna que calcula `preço * quantidade`.
|
|
1254
|
-
- **Criar Valores Condicionais (IF/ELSE):** Com a fórmula "Condicional", você pode criar uma coluna "NÃvel de Risco" que exibe "Alto" se o `valor` for maior que 1000, e "Baixo" caso contrário.
|
|
1255
|
-
|
|
1256
|
-
### 4. Formatação Condicional (Regras de Estilo)
|
|
1257
|
-
|
|
1258
|
-
Destaque informações importantes aplicando estilos que mudam com base nos dados da linha.
|
|
1259
|
-
|
|
1260
|
-
- **Crie Regras Visuais:** Na seção de "Formatação Condicional", crie uma nova regra.
|
|
1261
|
-
- **Defina a Condição:** Estabeleça a condição, por exemplo: "Quando a coluna `status` tiver o valor igual a 'Urgente'".
|
|
1262
|
-
- **Aplique o Estilo:** Use seletores de cor para definir que, quando a condição for verdadeira, a cor de fundo da célula ou da linha inteira deve se tornar vermelha e o texto, branco.
|
|
1263
|
-
|
|
1264
|
-
### 5. Comportamentos da Tabela
|
|
1265
|
-
|
|
1266
|
-
Habilite e configure as funcionalidades centrais da tabela com um clique. Na aba "Comportamento", você pode:
|
|
1267
|
-
|
|
1268
|
-
- **Ativar/Desativar Paginação:** Com um único interruptor, ative a paginação para tabelas com muitos dados e defina quantos itens exibir por página.
|
|
1269
|
-
- **Controlar Ordenação e Filtros:** Habilite a capacidade dos usuários de ordenar colunas e filtrar os dados com simples caixas de seleção.
|
|
1270
|
-
- **Gerenciar Seleção de Linhas:** Permita que os usuários selecionem uma ou várias linhas para realizar ações em lote.
|
|
1271
|
-
|
|
1272
|
-
### Editores Especializados
|
|
1273
|
-
|
|
1274
|
-
#### Behavior Editor
|
|
1275
|
-
|
|
1276
|
-
```typescript
|
|
1277
|
-
import { BehaviorConfigEditorComponent } from '@praxisui/table';
|
|
1278
|
-
|
|
1279
|
-
// Usar como componente standalone para edição especÃfica
|
|
1280
|
-
<behavior-config-editor
|
|
1281
|
-
[config]="tableConfig"
|
|
1282
|
-
(configChange)="onBehaviorChange($event)">
|
|
1283
|
-
</behavior-config-editor>
|
|
1284
|
-
```
|
|
1285
|
-
|
|
1286
|
-
#### Columns Editor
|
|
1287
|
-
|
|
1288
|
-
```typescript
|
|
1289
|
-
import { ColumnsConfigEditorComponent } from '@praxisui/table';
|
|
1290
|
-
|
|
1291
|
-
<columns-config-editor
|
|
1292
|
-
[config]="tableConfig"
|
|
1293
|
-
(configChange)="onColumnsChange($event)"
|
|
1294
|
-
(columnChange)="onColumnChange($event)">
|
|
1295
|
-
</columns-config-editor>
|
|
1296
|
-
```
|
|
1297
|
-
|
|
1298
|
-
## 🔧 Configuração Avançada
|
|
67
|
+
## Minimum Remote Runtime
|
|
1299
68
|
|
|
1300
|
-
|
|
69
|
+
Use `resourcePath` when the host wants the table to enter backend schema/data mode.
|
|
1301
70
|
|
|
1302
|
-
```
|
|
1303
|
-
const highVolumeConfig: TableConfig = {
|
|
1304
|
-
columns: [...],
|
|
1305
|
-
performance: {
|
|
1306
|
-
virtualization: {
|
|
1307
|
-
enabled: true,
|
|
1308
|
-
itemHeight: 48,
|
|
1309
|
-
bufferSize: 10,
|
|
1310
|
-
minContainerHeight: 400,
|
|
1311
|
-
strategy: 'fixed'
|
|
1312
|
-
},
|
|
1313
|
-
lazyLoading: {
|
|
1314
|
-
threshold: 100,
|
|
1315
|
-
images: true,
|
|
1316
|
-
components: true
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
};
|
|
1320
|
-
```
|
|
1321
|
-
|
|
1322
|
-
### Acessibilidade Personalizada
|
|
1323
|
-
|
|
1324
|
-
```typescript
|
|
1325
|
-
const accessibleConfig: TableConfig = {
|
|
1326
|
-
columns: [...],
|
|
1327
|
-
accessibility: {
|
|
1328
|
-
enabled: true,
|
|
1329
|
-
announcements: {
|
|
1330
|
-
dataChanges: true,
|
|
1331
|
-
userActions: true,
|
|
1332
|
-
loadingStates: true,
|
|
1333
|
-
liveRegion: 'polite'
|
|
1334
|
-
},
|
|
1335
|
-
keyboard: {
|
|
1336
|
-
shortcuts: true,
|
|
1337
|
-
tabNavigation: true,
|
|
1338
|
-
arrowNavigation: true,
|
|
1339
|
-
skipLinks: true,
|
|
1340
|
-
focusTrap: false
|
|
1341
|
-
},
|
|
1342
|
-
highContrast: false,
|
|
1343
|
-
reduceMotion: false
|
|
1344
|
-
}
|
|
1345
|
-
};
|
|
1346
|
-
```
|
|
1347
|
-
|
|
1348
|
-
### Aparência Customizada
|
|
1349
|
-
|
|
1350
|
-
```typescript
|
|
1351
|
-
const styledConfig: TableConfig = {
|
|
1352
|
-
columns: [...],
|
|
1353
|
-
appearance: {
|
|
1354
|
-
density: 'compact',
|
|
1355
|
-
borders: {
|
|
1356
|
-
showRowBorders: true,
|
|
1357
|
-
showColumnBorders: false,
|
|
1358
|
-
showOuterBorder: true,
|
|
1359
|
-
style: 'solid',
|
|
1360
|
-
width: 1,
|
|
1361
|
-
color: '#e0e0e0'
|
|
1362
|
-
},
|
|
1363
|
-
elevation: {
|
|
1364
|
-
level: 2,
|
|
1365
|
-
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
|
1366
|
-
},
|
|
1367
|
-
spacing: {
|
|
1368
|
-
cellPadding: '8px 16px',
|
|
1369
|
-
headerPadding: '12px 16px'
|
|
1370
|
-
},
|
|
1371
|
-
typography: {
|
|
1372
|
-
fontWeight: '400',
|
|
1373
|
-
fontSize: '14px',
|
|
1374
|
-
headerFontWeight: '500',
|
|
1375
|
-
headerFontSize: '14px'
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
};
|
|
1379
|
-
```
|
|
1380
|
-
|
|
1381
|
-
## 🎯 Event Handling
|
|
1382
|
-
|
|
1383
|
-
### Eventos da Tabela
|
|
1384
|
-
|
|
1385
|
-
```typescript
|
|
71
|
+
```html
|
|
1386
72
|
<praxis-table
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
(
|
|
1392
|
-
(
|
|
1393
|
-
(dataFilter)="onDataFilter($event)"
|
|
1394
|
-
(dataSort)="onDataSort($event)"
|
|
1395
|
-
(pageChange)="onPageChange($event)">
|
|
73
|
+
tableId="employees-table"
|
|
74
|
+
resourcePath="/api/employees"
|
|
75
|
+
[filterCriteria]="{ status: 'ACTIVE' }"
|
|
76
|
+
[enableCustomization]="true"
|
|
77
|
+
(selectionChange)="onSelection($event)"
|
|
78
|
+
(rowAction)="onRowAction($event)">
|
|
1396
79
|
</praxis-table>
|
|
1397
80
|
```
|
|
1398
81
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
```typescript
|
|
1402
|
-
export class MyComponent {
|
|
1403
|
-
onRowClick(event: { row: any; index: number }) {
|
|
1404
|
-
console.log("Row clicked:", event.row);
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
onRowSelect(event: { selectedRows: any[]; isSelectAll: boolean }) {
|
|
1408
|
-
console.log("Selection changed:", event.selectedRows);
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
onBulkAction(event: { action: string; selectedRows: any[] }) {
|
|
1412
|
-
switch (event.action) {
|
|
1413
|
-
case "deleteSelected":
|
|
1414
|
-
this.deleteMultiple(event.selectedRows);
|
|
1415
|
-
break;
|
|
1416
|
-
// Handle other bulk actions
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
onConfigChange(newConfig: TableConfig) {
|
|
1421
|
-
this.tableConfig = newConfig;
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
```
|
|
1425
|
-
|
|
1426
|
-
## ðŸ› ï¸ Utilitários e Helpers
|
|
1427
|
-
|
|
1428
|
-
### Helper Functions
|
|
1429
|
-
|
|
1430
|
-
```typescript
|
|
1431
|
-
import { createDefaultTableConfig, isValidTableConfig, cloneTableConfig, mergeTableConfigs } from "@praxisui/core";
|
|
1432
|
-
|
|
1433
|
-
// Criar configuração padrão
|
|
1434
|
-
const defaultConfig = createDefaultTableConfig();
|
|
1435
|
-
|
|
1436
|
-
// Validar configuração
|
|
1437
|
-
if (isValidTableConfig(myConfig)) {
|
|
1438
|
-
// Configuração válida
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
// Clonar configuração
|
|
1442
|
-
const clonedConfig = cloneTableConfig(originalConfig);
|
|
1443
|
-
|
|
1444
|
-
// Merge configurações
|
|
1445
|
-
const mergedConfig = mergeTableConfigs(baseConfig, overrides);
|
|
1446
|
-
```
|
|
1447
|
-
|
|
1448
|
-
### Service Integration
|
|
1449
|
-
|
|
1450
|
-
```typescript
|
|
1451
|
-
import { TableConfigService } from '@praxisui/core';
|
|
1452
|
-
|
|
1453
|
-
@Component({...})
|
|
1454
|
-
export class MyComponent {
|
|
1455
|
-
constructor(private configService: TableConfigService) {}
|
|
1456
|
-
|
|
1457
|
-
ngOnInit() {
|
|
1458
|
-
// Usar serviço para gerenciar configuração
|
|
1459
|
-
this.configService.setConfig(this.tableConfig);
|
|
1460
|
-
|
|
1461
|
-
// Verificar recursos disponÃveis
|
|
1462
|
-
// No runtime atual, multiSort permanece schema-only.
|
|
1463
|
-
const hasMultiSort = false;
|
|
1464
|
-
const hasBulkActions = this.configService.isFeatureEnabled('bulkActions');
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
```
|
|
1468
|
-
|
|
1469
|
-
## 🧪 Testes
|
|
1470
|
-
|
|
1471
|
-
### Unit Tests
|
|
1472
|
-
|
|
1473
|
-
```typescript
|
|
1474
|
-
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
|
1475
|
-
import { PraxisTable } from "@praxisui/table";
|
|
1476
|
-
import { TableConfig } from "@praxisui/core";
|
|
1477
|
-
|
|
1478
|
-
describe("PraxisTable", () => {
|
|
1479
|
-
let component: PraxisTable;
|
|
1480
|
-
let fixture: ComponentFixture<PraxisTable>;
|
|
1481
|
-
|
|
1482
|
-
beforeEach(() => {
|
|
1483
|
-
TestBed.configureTestingModule({
|
|
1484
|
-
imports: [PraxisTable],
|
|
1485
|
-
});
|
|
1486
|
-
|
|
1487
|
-
fixture = TestBed.createComponent(PraxisTable);
|
|
1488
|
-
component = fixture.componentInstance;
|
|
1489
|
-
});
|
|
1490
|
-
|
|
1491
|
-
it("should create", () => {
|
|
1492
|
-
expect(component).toBeTruthy();
|
|
1493
|
-
});
|
|
1494
|
-
|
|
1495
|
-
it("should handle configuration changes", () => {
|
|
1496
|
-
const config: TableConfig = {
|
|
1497
|
-
columns: [{ field: "test", header: "Test" }],
|
|
1498
|
-
};
|
|
1499
|
-
|
|
1500
|
-
component.config = config;
|
|
1501
|
-
fixture.detectChanges();
|
|
1502
|
-
|
|
1503
|
-
expect(component.config).toEqual(config);
|
|
1504
|
-
});
|
|
1505
|
-
});
|
|
1506
|
-
```
|
|
1507
|
-
|
|
1508
|
-
## 📋 Migration Guide
|
|
1509
|
-
|
|
1510
|
-
### Migração da Arquitetura V1/V2
|
|
1511
|
-
|
|
1512
|
-
Se você estava usando as versões anteriores com dual architecture, aqui estão as principais mudanças:
|
|
1513
|
-
|
|
1514
|
-
#### Imports Atualizados
|
|
1515
|
-
|
|
1516
|
-
```typescript
|
|
1517
|
-
// Antes
|
|
1518
|
-
import { TableConfigV1, TableConfigV2, TableConfigUnified } from "@praxisui/core";
|
|
1519
|
-
|
|
1520
|
-
// Depois
|
|
1521
|
-
import { TableConfig } from "@praxisui/core";
|
|
1522
|
-
```
|
|
1523
|
-
|
|
1524
|
-
#### Serviços Removidos
|
|
1525
|
-
|
|
1526
|
-
```typescript
|
|
1527
|
-
// Antes
|
|
1528
|
-
import { TableConfigAdapterService } from "@praxisui/table";
|
|
82
|
+
`resourcePath` is enough only when the host already provides the Praxis API/CRUD wiring expected by the table runtime. In remote mode, the table derives columns from the backend schema contract and data from the backend resource/filter contract.
|
|
1529
83
|
|
|
1530
|
-
|
|
1531
|
-
// Uso direto da configuração
|
|
1532
|
-
```
|
|
84
|
+
Optional collection operations such as export are not implied by a base route. Expose them only when backend capabilities or HATEOAS links prove that the operation is available.
|
|
1533
85
|
|
|
1534
|
-
|
|
86
|
+
## Runtime Inputs And Outputs
|
|
1535
87
|
|
|
1536
|
-
|
|
1537
|
-
// Antes
|
|
1538
|
-
config: TableConfigUnified;
|
|
88
|
+
Common inputs:
|
|
1539
89
|
|
|
1540
|
-
|
|
1541
|
-
config
|
|
1542
|
-
|
|
90
|
+
- `tableId`: stable table instance id
|
|
91
|
+
- `config`: `TableConfig`
|
|
92
|
+
- `data`: local row array
|
|
93
|
+
- `resourcePath`: backend resource path for remote mode
|
|
94
|
+
- `componentInstanceId`
|
|
95
|
+
- `configPersistenceStrategy`
|
|
96
|
+
- `title`, `subtitle`, `icon`
|
|
97
|
+
- `filterCriteria`, `queryContext`, `crudContext`
|
|
98
|
+
- `aiContext`
|
|
99
|
+
- `enableCustomization`
|
|
100
|
+
- `horizontalScroll`, `dense`
|
|
101
|
+
- `notifyIfOutdated`, `snoozeMs`, `autoOpenSettingsOnOutdated`
|
|
1543
102
|
|
|
1544
|
-
|
|
103
|
+
Common outputs:
|
|
1545
104
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
105
|
+
- `rowClick`, `rowDoubleClick`, `rowExpansionChange`
|
|
106
|
+
- `rowAction`, `toolbarAction`, `bulkAction`, `exportAction`
|
|
107
|
+
- `selectionChange`
|
|
108
|
+
- `columnReorder`, `columnReorderAttempt`
|
|
109
|
+
- `beforeDelete`, `afterDelete`, `deleteError`
|
|
110
|
+
- `beforeBulkDelete`, `afterBulkDelete`, `bulkDeleteError`
|
|
111
|
+
- `schemaStatusChange`, `configChange`, `metadataChange`
|
|
112
|
+
- `loadingStateChange`, `collectionLinksChange`
|
|
113
|
+
- `widgetEvent`
|
|
1549
114
|
|
|
1550
|
-
##
|
|
115
|
+
## TableConfig Boundaries
|
|
1551
116
|
|
|
1552
|
-
|
|
117
|
+
`TableConfig` comes from `@praxisui/core` and is the public table configuration contract. It covers columns, behavior, appearance, toolbar, messages, filtering, selection, expansion, export configuration, conditional styles/renderers, and other table semantics.
|
|
1553
118
|
|
|
1554
|
-
|
|
119
|
+
The table owns table orchestration and rendering. It does not own backend resource semantics, form payloads, page composition, or business rules outside the table contract.
|
|
1555
120
|
|
|
1556
|
-
|
|
1557
|
-
// Verificar se a configuração é válida
|
|
1558
|
-
import { isValidTableConfig } from "@praxisui/core";
|
|
121
|
+
## Filtering And Row Actions
|
|
1559
122
|
|
|
1560
|
-
|
|
1561
|
-
console.error("Configuração inválida:", myConfig);
|
|
1562
|
-
}
|
|
1563
|
-
```
|
|
123
|
+
The package exports both the table runtime and `PraxisFilter` integration surfaces.
|
|
1564
124
|
|
|
1565
|
-
|
|
125
|
+
- Inline and advanced filter controls use `@praxisui/dynamic-fields` contracts.
|
|
126
|
+
- Row actions can be declared in `config.actions.row.actions`.
|
|
127
|
+
- Contextual row discovery can use backend HATEOAS/capabilities when enabled.
|
|
128
|
+
- `visibleWhen` and `disabledWhen` use canonical JSON Logic.
|
|
1566
129
|
|
|
1567
|
-
```
|
|
1568
|
-
// Habilitar virtualização para grandes datasets
|
|
130
|
+
```ts
|
|
1569
131
|
const config: TableConfig = {
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
132
|
+
columns: [{ field: 'name', header: 'Name' }],
|
|
133
|
+
actions: {
|
|
134
|
+
row: {
|
|
1573
135
|
enabled: true,
|
|
1574
|
-
|
|
1575
|
-
|
|
136
|
+
display: 'buttons',
|
|
137
|
+
discovery: { enabled: false },
|
|
138
|
+
actions: [
|
|
139
|
+
{
|
|
140
|
+
id: 'open-detail',
|
|
141
|
+
label: 'Open detail',
|
|
142
|
+
action: 'navigation.openRoute',
|
|
143
|
+
visibleWhen: { '===': [{ var: 'status' }, 'ACTIVE'] },
|
|
144
|
+
},
|
|
145
|
+
],
|
|
1576
146
|
},
|
|
1577
147
|
},
|
|
1578
148
|
};
|
|
1579
149
|
```
|
|
1580
150
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
```typescript
|
|
1584
|
-
// Garantir que acessibilidade está habilitada
|
|
1585
|
-
const config: TableConfig = {
|
|
1586
|
-
// ...
|
|
1587
|
-
accessibility: {
|
|
1588
|
-
enabled: true,
|
|
1589
|
-
announcements: { dataChanges: true, userActions: true, loadingStates: true, liveRegion: "polite" },
|
|
1590
|
-
},
|
|
1591
|
-
};
|
|
1592
|
-
```
|
|
1593
|
-
|
|
1594
|
-
## 📚 API Reference
|
|
1595
|
-
|
|
1596
|
-
### Interfaces Principais
|
|
1597
|
-
|
|
1598
|
-
#### TableConfig
|
|
1599
|
-
|
|
1600
|
-
Interface principal para configuração da tabela.
|
|
1601
|
-
|
|
1602
|
-
#### ColumnDefinition
|
|
1603
|
-
|
|
1604
|
-
Define configuração individual de colunas.
|
|
1605
|
-
|
|
1606
|
-
#### TableBehaviorConfig
|
|
1607
|
-
|
|
1608
|
-
Configurações de comportamento (paginação, ordenação, etc.).
|
|
1609
|
-
|
|
1610
|
-
#### TableAppearanceConfig
|
|
1611
|
-
|
|
1612
|
-
Configurações de aparência visual.
|
|
1613
|
-
|
|
1614
|
-
Para documentação completa da API, consulte `projects/praxis-core/README.md`.
|
|
1615
|
-
|
|
1616
|
-
## 🤠Contribuição
|
|
1617
|
-
|
|
1618
|
-
### Como Contribuir
|
|
1619
|
-
|
|
1620
|
-
1. Fork o projeto
|
|
1621
|
-
2. Crie branch para feature (`git checkout -b feature/nova-funcionalidade`)
|
|
1622
|
-
3. Commit mudanças (`git commit -m 'Add: nova funcionalidade'`)
|
|
1623
|
-
4. Push para branch (`git push origin feature/nova-funcionalidade`)
|
|
1624
|
-
5. Abra Pull Request
|
|
1625
|
-
|
|
1626
|
-
### Guidelines
|
|
1627
|
-
|
|
1628
|
-
- Seguir Angular Style Guide
|
|
1629
|
-
- Adicionar testes para novas features
|
|
1630
|
-
- Manter documentação atualizada
|
|
1631
|
-
- Usar TypeScript strict mode
|
|
1632
|
-
|
|
1633
|
-
## 🔠Exemplo de Integração com PraxisFilter
|
|
1634
|
-
|
|
1635
|
-
O `PraxisFilter` pode ser acoplado à barra de ferramentas da tabela. O exemplo abaixo mostra a busca de pessoas por CPF e status.
|
|
1636
|
-
|
|
1637
|
-
```html
|
|
1638
|
-
<praxis-filter [resourcePath]="'pessoas'" [formId]="'pessoas-filter'" [persistenceKey]="'pessoas-filter-v1'" [alwaysVisibleFields]="['status']" (requestSearch)="onFilter($event)"></praxis-filter> <praxis-table [data]="tableData"></praxis-table>
|
|
1639
|
-
```
|
|
1640
|
-
|
|
1641
|
-
```ts
|
|
1642
|
-
onFilter(dto: any) {
|
|
1643
|
-
this.crud.configure('pessoas', ApiEndpoint.HumanResources);
|
|
1644
|
-
this.crud.filter(dto, this.pageable).subscribe(page => {
|
|
1645
|
-
this.tableData = page.content;
|
|
1646
|
-
});
|
|
1647
|
-
}
|
|
1648
|
-
```
|
|
1649
|
-
|
|
1650
|
-
### âš™ï¸ Configuração do Filtro
|
|
1651
|
-
|
|
1652
|
-
O `PraxisFilter` pode ser configurado por inputs/JSON de configuração. O atalho
|
|
1653
|
-
visual por Ãcone de engrenagem não é exposto por padrão na tabela/demo.
|
|
1654
|
-
Quando necessário, também é possÃvel abrir o painel programaticamente via
|
|
1655
|
-
`openSettings()`. Nesse painel é possÃvel ajustar:
|
|
1656
|
-
|
|
1657
|
-
- **alwaysVisibleFields** – campos que permanecem sempre visÃveis
|
|
1658
|
-
- **alwaysVisibleFieldMetadataOverrides** – patch de metadata por campo sempre visÃvel (controle, clearButton, inlineAutoSize etc.)
|
|
1659
|
-
- **showAdvanced** – define se a seção avançada inicia aberta
|
|
1660
|
-
|
|
1661
|
-
```ts
|
|
1662
|
-
@ViewChild(PraxisFilter) filter!: PraxisFilter;
|
|
1663
|
-
|
|
1664
|
-
abrirConfiguracoes() {
|
|
1665
|
-
this.filter.openSettings();
|
|
1666
|
-
}
|
|
1667
|
-
```
|
|
1668
|
-
|
|
1669
|
-
Ao aplicar ou salvar, as escolhas são validadas contra os metadados
|
|
1670
|
-
do **DTO de filtro** (schema de `POST /{resource}/filter`). Campos ausentes
|
|
1671
|
-
no DTO não são aplicados em `alwaysVisibleFields`.
|
|
1672
|
-
O componente exibe uma barra de progresso durante o processo de persistência e
|
|
1673
|
-
mensagens de sucesso ou erro via _snack bar_, garantindo uma experiência
|
|
1674
|
-
consistente.
|
|
1675
|
-
|
|
1676
|
-
### 🔖 Mini‑guia: Atalhos do Filtro (tags)
|
|
1677
|
-
|
|
1678
|
-
Atalhos são “chips†que guardam um conjunto de filtros (DTO) para reuso rápido.
|
|
1679
|
-
|
|
1680
|
-
- Criar/Salvar
|
|
1681
|
-
- Modal (padrão): o host do diálogo exibe o botão quando `allowSaveTags === true`.
|
|
1682
|
-
- Gaveta (Drawer): o Adapter recebe `allowSaveTags?`, `i18nSaveAsShortcut?` e `onSaveShortcut?` (opcionais). O host deve exibir o botão e chamar `onSaveShortcut(clean({ ...initialDto, ...lastValue }))`.
|
|
1683
|
-
- O DTO é limpo antes de persistir (remove `'' | null | undefined`).
|
|
1684
|
-
- Aplicar
|
|
1685
|
-
- Clique/Enter no chip aplica imediatamente `tag.patch` (substitui o estado atual), emite `submit` e persiste.
|
|
1686
|
-
- O chip ativo é destacado (cor/borda + Ãcone de “checkâ€) quando o DTO atual é igual ao patch do atalho.
|
|
1687
|
-
- Editar/Renomear
|
|
1688
|
-
- Apenas atalhos do usuário exibem Ãcone de lápis. Predefinidos mostram Ãcone de “cadeado†(somente leitura).
|
|
1689
|
-
- Excluir (com Undo)
|
|
1690
|
-
- Exclusão remove imediatamente o atalho do usuário e exibe snackbar “Atalho removido†com ação “Desfazer†quando `confirmTagDelete === true`.
|
|
1691
|
-
- Ao clicar em “Desfazerâ€, o atalho é restaurado na mesma posição e os eventos são reemitidos.
|
|
1692
|
-
- i18n
|
|
1693
|
-
- Chaves úteis: `saveAsShortcut`, `renameShortcut`, `removeShortcut`, `shortcutSaved`, `shortcutRemoved`, `undo`, `readonlyShortcut`.
|
|
1694
|
-
|
|
1695
|
-
Veja também: `docs/host-crud-integration.md` (guia operacional host) e `projects/praxis-crud/docs/host-crud-runtime-and-openmode.md` (contrato técnico canônico) para detalhes do contrato do Adapter.
|
|
1696
|
-
|
|
1697
|
-
### Novos Inputs/Outputs (PraxisFilter)
|
|
1698
|
-
|
|
1699
|
-
- Inputs (efetivos apenas quando `enableCustomization = true`):
|
|
1700
|
-
- `enableCustomization: boolean` — habilita o gate de customização para notificações de schema.
|
|
1701
|
-
- `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'` — seleciona como o runtime publica avisos de drift quando a polÃtica operacional estiver habilitada.
|
|
1702
|
-
- `snoozeMs: number = 86400000` — tempo de soneca para avisos (ms).
|
|
1703
|
-
- `autoOpenSettingsOnOutdated: boolean = false` — abre Configurações ao detectar schema desatualizado.
|
|
1704
|
-
- Output:
|
|
1705
|
-
- `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; tableId?: string }` — emitido após verificação leve (304/200).
|
|
1706
|
-
|
|
1707
|
-
#### Fallback Global (opcional)
|
|
1708
|
-
|
|
1709
|
-
- Quando os inputs do componente permanecem com os valores padrão e não há overrides locais, o Filter utiliza como último fallback as preferências globais via `GlobalConfigService.getSchemaPrefsGlobal()`.
|
|
1710
|
-
- Cadeia de precedência (inalterada): `@Inputs (widget)` → Prefs do widget → Prefs da página → Prefs globais → Defaults.
|
|
1711
|
-
- O fallback global não é persistido no componente; serve apenas para defaults em memória.
|
|
1712
|
-
|
|
1713
|
-
Notas rápidas (Flow ETag no Filter):
|
|
1714
|
-
- Verificação leve sempre no init (sem baixar o corpo): usa ETag/If-None-Match em `/schemas/filtered` (path=`.../filter`, operation=`post`, schemaType=`request`, `includeInternalSchemas=true`).
|
|
1715
|
-
- 304: atualiza `lastVerifiedAt`; emite `schemaStatusChange(outdated=false)`.
|
|
1716
|
-
- 200: atualiza `serverHash/lastVerifiedAt`; marca `outdated=true` apenas quando em customização; não aplica o schema automaticamente.
|
|
1717
|
-
- O corpo do schema é baixado apenas quando necessário para renderização (ex.: `alwaysVisibleFields` ou ao abrir o painel Avançado).
|
|
1718
|
-
|
|
1719
|
-
## Formatação de Colunas (format)
|
|
1720
|
-
|
|
1721
|
-
Cada coluna pode declarar `type` e uma string de formatação `format` consumida pelo `DataFormattingService`. Os tipos suportados são: `string`, `number`, `currency`, `percentage`, `date`, `datetime`, `time`, `boolean`, `custom`.
|
|
1722
|
-
|
|
1723
|
-
Regra geral:
|
|
1724
|
-
- `format` explÃcito continua com precedência máxima.
|
|
1725
|
-
- Para `number`, `currency`, `percentage`, `date`, `datetime` e `time`, a tabela também consegue derivar formatação básica a partir de `type + config.localization`, mesmo quando `format` estiver ausente.
|
|
1726
|
-
- `string`, `boolean` e `custom` continuam conservadores; nesses casos, use `format` explÃcito quando precisar de transformação.
|
|
1727
|
-
|
|
1728
|
-
Nota de plataforma:
|
|
1729
|
-
- o contrato público da tabela continua sendo `column.type + config.localization + format`.
|
|
1730
|
-
- a tabela converge internamente para a semântica canônica horizontal do core, mas não expõe `valuePresentation` como bloco público de configuração de coluna.
|
|
1731
|
-
|
|
1732
|
-
Tipos e padrões de `format`:
|
|
1733
|
-
- number (DecimalPipe)
|
|
1734
|
-
- Padrões: `minInt.minFrac-maxFrac`
|
|
1735
|
-
- Exemplos: `1.0-0`, `1.2-2`, `1.0-3|nosep` (remove separador de milhar)
|
|
1736
|
-
- currency (CurrencyPipe)
|
|
1737
|
-
- Sintaxe: `CURRENCY|DISPLAY|DECIMALS[|nosep]`
|
|
1738
|
-
- Exemplos: `BRL|symbol|2`, `USD|code|0`, `EUR|symbol|2|nosep`
|
|
1739
|
-
- percentage (PercentPipe/DecimalPipe)
|
|
1740
|
-
- Sem multiplicador: `1.0-0` (PercentPipe)
|
|
1741
|
-
- Multiplicar por 100: `1.1-1|x100` (DecimalPipe×100 + `%`)
|
|
1742
|
-
- date (DatePipe)
|
|
1743
|
-
- Tokens Angular: `shortDate`, `mediumDate`, `longDate`, `fullDate`, `short`, `shortTime`
|
|
1744
|
-
- Padrões customizados: `dd/MM/yyyy`, `yyyy-MM-dd`, `dd/MM/yyyy HH:mm`
|
|
1745
|
-
- string (transformações + truncamento)
|
|
1746
|
-
- Transform: `uppercase` | `lowercase` | `titlecase` | `capitalize` | `none`
|
|
1747
|
-
- Transform + truncar: `<transform>|truncate|<max>|<suffix>`
|
|
1748
|
-
- Exemplos: `uppercase|truncate|50|...`, `titlecase`
|
|
1749
|
-
- boolean (pré-definidos ou custom)
|
|
1750
|
-
- Presets: `true-false` | `yes-no` | `active-inactive` | `on-off` | `enabled-disabled`
|
|
1751
|
-
- Custom: `custom|Verdadeiro|Falso`
|
|
1752
|
-
|
|
1753
|
-
Exemplos de ColumnDefinition:
|
|
1754
|
-
```ts
|
|
1755
|
-
{ field: 'salario', type: 'currency', format: 'BRL|symbol|2', header: 'Salário' }
|
|
1756
|
-
{ field: 'desconto', type: 'percentage', format: '1.1-1|x100', header: 'Desconto' }
|
|
1757
|
-
{ field: 'criadoEm', type: 'date', format: 'dd/MM/yyyy', header: 'Criado em' }
|
|
1758
|
-
{ field: 'nome', type: 'string', format: 'titlecase|truncate|32|…', header: 'Nome' }
|
|
1759
|
-
{ field: 'ativo', type: 'boolean', format: 'yes-no', header: 'Ativo' }
|
|
1760
|
-
```
|
|
1761
|
-
|
|
1762
|
-
Exemplos com defaults implÃcitos por `type + localization`:
|
|
1763
|
-
```ts
|
|
1764
|
-
{
|
|
1765
|
-
localization: {
|
|
1766
|
-
locale: 'pt-BR',
|
|
1767
|
-
currency: { code: 'BRL', symbol: 'R$', position: 'before', spacing: true, precision: 2 },
|
|
1768
|
-
dateTime: { dateFormat: 'dd/MM/yyyy', timeFormat: 'HH:mm', dateTimeFormat: 'dd/MM/yyyy HH:mm', firstDayOfWeek: 1 },
|
|
1769
|
-
number: { decimalSeparator: ',', thousandsSeparator: '.', defaultPrecision: 2, negativeSign: '-', negativeSignPosition: 'before' },
|
|
1770
|
-
},
|
|
1771
|
-
columns: [
|
|
1772
|
-
{ field: 'salario', type: 'currency', header: 'Salário' },
|
|
1773
|
-
{ field: 'criadoEm', type: 'date', header: 'Criado em' },
|
|
1774
|
-
{ field: 'taxa', type: 'percentage', header: 'Taxa' },
|
|
1775
|
-
],
|
|
1776
|
-
}
|
|
1777
|
-
```
|
|
1778
|
-
|
|
1779
|
-
Observações:
|
|
1780
|
-
- `|nosep` remove separadores de milhar da saÃda formatada.
|
|
1781
|
-
- Para datas inválidas ou valores não numéricos, o serviço retorna o valor original (com aviso no console).
|
|
1782
|
-
- Colunas com renderizador `custom` não passam por formatação automática.
|
|
1783
|
-
- Precedência de locale para formatação: `config.localization.locale` -> `LOCALE_ID` do host.
|
|
1784
|
-
- Quando `config.localization.currency` estiver presente, a tabela usa esse bloco como override para código, sÃmbolo, posição, espaçamento e precisão da moeda.
|
|
1785
|
-
- Quando `config.localization.number` estiver presente, a tabela respeita `decimalSeparator`, `thousandsSeparator`, `defaultPrecision`, `negativeSign` e `negativeSignPosition` em number/currency/percentage.
|
|
1786
|
-
|
|
1787
|
-
Exemplos práticos no workspace (rotas):
|
|
1788
|
-
- Regras Visuais (Simples): `/table-rules-simple`
|
|
1789
|
-
- Regras Visuais (Complexas): `/table-rules-complex`
|
|
1790
|
-
|
|
1791
|
-
## Auto-render de Avatar (strict mode)
|
|
1792
|
-
|
|
1793
|
-
No bootstrap de schema (quando `config.columns` esta vazio), a tabela detecta automaticamente colunas de avatar e aplica `renderer.type = 'avatar'`.
|
|
1794
|
-
|
|
1795
|
-
Deteccao:
|
|
1796
|
-
- somente quando `FieldDefinition.controlType === 'avatar'`
|
|
1797
|
-
|
|
1798
|
-
Renderer aplicado:
|
|
1799
|
-
- `renderer: { type: 'avatar', avatar: { srcField, initialsField: 'nomeCompleto', shape: 'circle', size: 32 } }`
|
|
1800
|
-
- alinhamento central e largura compacta (~56px)
|
|
1801
|
-
- `initialsField` deriva as iniciais a partir de um campo textual bruto
|
|
1802
|
-
|
|
1803
|
-
Avatar explicito:
|
|
1804
|
-
- `renderer: { type: 'avatar', avatar: { srcField, initialsExpr, initialsField, shape: 'circle', size: 28 } }`
|
|
1805
|
-
- `initialsField` deriva automaticamente as iniciais de um campo textual quando `srcField` nao resolve imagem e `initialsExpr` nao foi informado
|
|
1806
|
-
- `initialsExpr` deve retornar as iniciais prontas; ele nao transforma automaticamente um campo bruto em iniciais
|
|
1807
|
-
- quando quiser derivar iniciais a partir de um campo textual, prefira `initialsField`
|
|
1808
|
-
- exemplo recomendado: `avatar: { srcField: 'photoUrl', initialsField: 'name' }`
|
|
1809
|
-
- exemplo de `initialsExpr` explicito: `avatar: { initialsExpr: 'initials' }`
|
|
1810
|
-
|
|
1811
|
-
Comportamento importante:
|
|
1812
|
-
- a autodeteccao ocorre apenas no bootstrap (nao sobrescreve configuracoes de colunas persistidas)
|
|
1813
|
-
- se quiser desabilitar, defina renderer explicito na coluna (`image` ou `text`)
|
|
1814
|
-
- para cenarios sem `x-ui`, exponha renderer no config ou ajuste backend para enviar `x-ui.controlType`
|
|
1815
|
-
|
|
1816
|
-
Ajustes comuns:
|
|
1817
|
-
- `renderer.avatar.shape`: `'square' | 'rounded' | 'circle'`
|
|
1818
|
-
- `renderer.avatar.size`: numero em px
|
|
1819
|
-
- `renderer.avatar.backgroundColor`: cor de fundo do circulo
|
|
1820
|
-
- `renderer.avatar.textColor`: cor das iniciais
|
|
1821
|
-
- `renderer.avatar.alt` / `altField`
|
|
1822
|
-
- use `conditionalRenderers` para variar renderer por linha
|
|
1823
|
-
- exemplo condicional: `condition: { ">": [{ "var": "salario" }, 30000] }` com `avatar: { initialsField: 'name', backgroundColor: '#D32F2F', textColor: '#FFFFFF' }`
|
|
1824
|
-
|
|
1825
|
-
A11y, seguranca e performance:
|
|
1826
|
-
- `alt`/`altField` sao respeitados
|
|
1827
|
-
- fallback para iniciais quando imagem falha/nao existe
|
|
1828
|
-
- URL da imagem sanitizada (bloqueia esquemas perigosos)
|
|
1829
|
-
- imagem com `loading=\"lazy\"`
|
|
1830
|
-
|
|
1831
|
-
## 📊 Roadmap
|
|
1832
|
-
|
|
1833
|
-
### Próximas Versões
|
|
1834
|
-
|
|
1835
|
-
- ✅ Arquitetura unificada (v2.0.0)
|
|
1836
|
-
- 🔄 Enhanced mobile support (v2.1.0)
|
|
1837
|
-
- 📋 Advanced export options (v2.2.0)
|
|
1838
|
-
- 🎨 Theme customization (v2.3.0)
|
|
151
|
+
## Visual Authoring
|
|
1839
152
|
|
|
1840
|
-
|
|
153
|
+
Use `PraxisTableConfigEditor` or the table settings surfaces when `enableCustomization` is true.
|
|
1841
154
|
|
|
1842
|
-
|
|
155
|
+
Main authoring areas include:
|
|
1843
156
|
|
|
1844
|
-
|
|
157
|
+
- columns
|
|
158
|
+
- behavior
|
|
159
|
+
- toolbar actions
|
|
160
|
+
- filters
|
|
161
|
+
- messages/localization
|
|
162
|
+
- visual rules
|
|
163
|
+
- value mapping
|
|
164
|
+
- JSON config editing
|
|
1845
165
|
|
|
1846
|
-
|
|
1847
|
-
**Versão**: 2.0.0 (Unified Architecture)
|
|
1848
|
-
**Compatibilidade**: Angular 18+
|
|
166
|
+
The package also exports `PRAXIS_TABLE_AUTHORING_MANIFEST` for governed AI edits and `TABLE_AI_CAPABILITIES` / `TABLE_COMPONENT_AI_CAPABILITIES` for component capability discovery.
|
|
1849
167
|
|
|
1850
|
-
##
|
|
168
|
+
## Analytics And Rich Content
|
|
1851
169
|
|
|
1852
|
-
|
|
1853
|
-
experiência corporativa estritamente curada pelo host. Nesse modo, o runtime
|
|
1854
|
-
mantém somente as ações declaradas em `actions.row.actions[]` e não adiciona
|
|
1855
|
-
ações descobertas dinamicamente por HATEOAS/capabilities.
|
|
170
|
+
The table can materialize analytic table projections produced by the canonical `x-ui.analytics` decision in `@praxisui/core`. Services such as `AnalyticsTableContractService` help hosts resolve analytic table contracts and data without reimplementing that projection.
|
|
1856
171
|
|
|
1857
|
-
|
|
1858
|
-
actions: {
|
|
1859
|
-
row: {
|
|
1860
|
-
enabled: true,
|
|
1861
|
-
display: 'buttons',
|
|
1862
|
-
discovery: { enabled: false },
|
|
1863
|
-
actions: [
|
|
1864
|
-
{ id: 'briefing', label: 'Briefing', action: 'inspect-surfaces' },
|
|
1865
|
-
{ id: 'capabilities', label: 'Capabilities', action: 'inspect-actions' },
|
|
1866
|
-
],
|
|
1867
|
-
},
|
|
1868
|
-
}
|
|
1869
|
-
```
|
|
1870
|
-
|
|
1871
|
-
O campo controla apenas a descoberta contextual de row actions. Ele não desativa
|
|
1872
|
-
`behavior.expansion.detail.source.mode="hypermedia"`; quando a expansão
|
|
1873
|
-
hypermedia estiver ativa, o detail ainda pode resolver `surfaces` e `actions`
|
|
1874
|
-
sob demanda ao expandir a linha. O default continua habilitado quando
|
|
1875
|
-
`actions.row.discovery.enabled` é omitido.
|
|
172
|
+
Detail rows can host governed rich content surfaces. Rich content semantics belong to the shared rich content/core contracts; the table provides the row-detail shell and host-mediated dispatch.
|
|
1876
173
|
|
|
1877
|
-
##
|
|
174
|
+
## Public API
|
|
1878
175
|
|
|
1879
|
-
|
|
1880
|
-
interna levando o identificador do registro selecionado.
|
|
1881
|
-
|
|
1882
|
-
```ts
|
|
1883
|
-
actions: {
|
|
1884
|
-
row: {
|
|
1885
|
-
enabled: true,
|
|
1886
|
-
display: 'buttons',
|
|
1887
|
-
discovery: { enabled: false },
|
|
1888
|
-
actions: [
|
|
1889
|
-
{
|
|
1890
|
-
id: 'open-detail',
|
|
1891
|
-
label: 'Abrir detalhe',
|
|
1892
|
-
action: 'navigation.openRoute',
|
|
1893
|
-
effects: [
|
|
1894
|
-
{
|
|
1895
|
-
kind: 'global-action',
|
|
1896
|
-
globalAction: {
|
|
1897
|
-
actionId: 'navigation.openRoute',
|
|
1898
|
-
payload: {
|
|
1899
|
-
path: '/clientes/detalhe',
|
|
1900
|
-
query: {
|
|
1901
|
-
id: '${row.id}',
|
|
1902
|
-
},
|
|
1903
|
-
},
|
|
1904
|
-
},
|
|
1905
|
-
},
|
|
1906
|
-
],
|
|
1907
|
-
globalAction: {
|
|
1908
|
-
actionId: 'navigation.openRoute',
|
|
1909
|
-
payload: {
|
|
1910
|
-
path: '/clientes/detalhe',
|
|
1911
|
-
query: {
|
|
1912
|
-
id: '${row.id}',
|
|
1913
|
-
},
|
|
1914
|
-
},
|
|
1915
|
-
},
|
|
1916
|
-
},
|
|
1917
|
-
],
|
|
1918
|
-
},
|
|
1919
|
-
}
|
|
1920
|
-
```
|
|
176
|
+
Main exports:
|
|
1921
177
|
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
178
|
+
- `PraxisTable`
|
|
179
|
+
- `PraxisTableToolbar`
|
|
180
|
+
- `PraxisTableConfigEditor`
|
|
181
|
+
- `PraxisTableWidgetConfigEditor`
|
|
182
|
+
- `PraxisFilter`
|
|
183
|
+
- filter settings and widget config editor
|
|
184
|
+
- `DataFormattingService`, `DataFormatterComponent`
|
|
185
|
+
- analytics table services
|
|
186
|
+
- rich content and action utilities
|
|
187
|
+
- table editor document/capability models
|
|
188
|
+
- behavior, columns, toolbar, messages, value mapping, JSON, filter, and rules editor components
|
|
189
|
+
- `providePraxisTableMetadata`
|
|
190
|
+
- `TABLE_AI_CAPABILITIES`, `TABLE_COMPONENT_AI_CAPABILITIES`
|
|
191
|
+
- `PRAXIS_TABLE_AUTHORING_MANIFEST`
|
|
192
|
+
- table component edit-plan helpers
|
|
1931
193
|
|
|
1932
|
-
|
|
1933
|
-
`disabledWhen`. O editor de acoes preserva esses objetos no fluxo
|
|
1934
|
-
abrir -> aplicar/salvar -> reabrir, por exemplo:
|
|
194
|
+
## Notes
|
|
1935
195
|
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
visibleWhen: { '===': [{ var: 'status' }, 'Ativo'] },
|
|
1942
|
-
disabledWhen: { '===': [{ var: 'status' }, 'Bloqueado'] },
|
|
1943
|
-
}
|
|
1944
|
-
```
|
|
196
|
+
- Separate local data mode from backend resource mode before deciding the minimum setup.
|
|
197
|
+
- `enableCustomization` is opt-in and gates settings/authoring surfaces; it does not change the data mode.
|
|
198
|
+
- Treat export as a governed optional operation, not as automatic support implied by `resourcePath`.
|
|
199
|
+
- Theme the table through Material/Praxis tokens instead of targeting internal descendants.
|
|
200
|
+
- Use the official documentation for full recipes on filters, rich detail rows, rules, formatting, AI authoring, and backend capabilities.
|