@praxisui/table 3.0.0-beta.9 → 5.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,6 +50,8 @@ last_updated: "2026-03-07"
50
50
 
51
51
  # @praxisui/table
52
52
 
53
+ > Estado canônico do workspace Angular: JSON Logic é o único contrato ativo de regras. Menções a DSL neste documento existem apenas como contexto histórico de migração ou referência para payloads legados.
54
+
53
55
  ## Documentation
54
56
 
55
57
  - Documentação oficial: https://praxisui.dev
@@ -62,6 +64,37 @@ last_updated: "2026-03-07"
62
64
  - Governar configuracao de colunas, toolbar e comportamento em runtime
63
65
  - Integrar tabelas a fluxos CRUD, IA e editores visuais do ecossistema Praxis UI
64
66
 
67
+ ## `x-ui.analytics` como fallback tabular canônico
68
+
69
+ O `@praxisui/table` materializa a familia `analytic-table` quando o `@praxisui/core`
70
+ resolve que uma projection de `x-ui.analytics` continua valida, mas nao e um chart
71
+ inequivoco.
72
+
73
+ Regras:
74
+
75
+ - a decisao da familia continua no `@praxisui/core`
76
+ - o `@praxisui/table` nao decide `chart` vs `analytic-table`
77
+ - a tabela apenas materializa a decision/projection recebida
78
+ - a materializacao preserva `dimension`, `metrics`, `sort`, `limit` e agregacao sem
79
+ inventar defaults semanticos no host
80
+ - `primaryMetrics` e `secondaryMetrics` entram na mesma trilha canonica de execucao
81
+ - projections `timeseries` preservam o valor temporal bruto para manter ordenacao
82
+ cronologica consistente no fallback tabular
83
+
84
+ Superficies publicas:
85
+
86
+ - `AnalyticsTableConfigAdapterService`: converte projection em `TableConfig` local
87
+ - `AnalyticsTableStatsApiService`: executa `praxis.stats` e devolve linhas tabulares
88
+ - `AnalyticsTableContractService`: fachada publica para hosts que querem resolver
89
+ contrato + dados de tabela analitica sem reimplementar leitura de `x-ui.analytics`
90
+
91
+ Filtro runtime:
92
+
93
+ - a projection continua sendo a fonte canonica da semantica analitica
94
+ - o host pode passar `queryContext.filters` em `loadTableViews(..., { queryContexts })`
95
+ - somente `filters` entram no request remoto nesta fase
96
+ - `sort`, `limit` e agregacao continuam vindo da projection publicada
97
+
65
98
  > Componente de tabela empresarial avançado com arquitetura unificada
66
99
 
67
100
  ## Customization Mode Contract
@@ -115,6 +148,24 @@ Nota: a classe de tema é decisão do host (`.dark-theme` ou `.theme-dark`/`.the
115
148
  - **Acessibilidade**: WCAG 2.1 AA compliant
116
149
  - **Verificação de Schema**: ETag/If-None-Match com notificações (somente em customização)
117
150
 
151
+ ## Expansion Hypermedia
152
+
153
+ O primeiro corte de `behavior.expansion.detail.source.mode = "hypermedia"` é
154
+ `capabilities-first`:
155
+
156
+ - a row expandida começa em `_links.capabilities`
157
+ - `surfaces`, `actions` e operações canônicas vêm do snapshot agregado retornado
158
+ pelo backend
159
+ - o detail renderiza um resumo contextual somente leitura, com foco em UX de
160
+ negócio e affordances disponíveis naquele registro
161
+ - este corte não executa `surface.open` inline nem dispara workflow inline
162
+ - quando o item não expõe `rel="capabilities"`, o runtime aplica fail-closed
163
+
164
+ Observação de escala:
165
+ - o prefetch por linha continua ativo quando `actions.row.enabled=true`
166
+ - quando apenas o expansion `hypermedia` está ligado, o discovery contextual é
167
+ resolvido sob demanda ao expandir a linha
168
+
118
169
  ### 📄 Contrato Inline do Filtro
119
170
 
120
171
  - Referencia canonica: `projects/praxis-dynamic-fields/docs/dynamic-fields-inline-components-guide.md` (slug publicado: `dynamic-fields-inline-components-guide`)
@@ -143,17 +194,17 @@ O novo editor é "column-first" e usa uma tipagem compartilhada com o Editor de
143
194
  - Editores de valor dinâmicos: inputs numéricos, listas (CSV/múltipla seleção), datepickers.
144
195
  - Grupos lógicos: construa condições com AND/OR/NOT e reordene via arrastar-e-soltar (CDK DragDrop).
145
196
  - Preview: execute “Testar” para ver quantas linhas seriam afetadas pelas regras ativas.
146
- - Import/Export: exporta JSON sem o campo `enabled`; importa com validação de DSL e sanitização de estilos (allowlist).
197
+ - Import/Export: exporta JSON sem o campo `enabled`; importa com validação estrutural de JSON Logic e sanitização de estilos (allowlist).
147
198
 
148
- Exemplos de DSL:
199
+ Exemplos de JSON Logic:
149
200
 
150
- ```text
151
- contains(status, 'Ativo')
152
- not (status in ['A', 'I'])
153
- (price >= 10 and price <= 20)
154
- (createdAt >= '2024-10-01' and createdAt <= '2024-10-31')
155
- (name == null or name == '')
156
- active == true
201
+ ```json
202
+ { "contains": [{ "var": "status" }, "Ativo"] }
203
+ { "!": [{ "in": [{ "var": "status" }, ["A", "I"]] }] }
204
+ { "and": [{ ">=": [{ "var": "price" }, 10] }, { "<=": [{ "var": "price" }, 20] }] }
205
+ { "and": [{ ">=": [{ "var": "createdAt" }, "2024-10-01"] }, { "<=": [{ "var": "createdAt" }, "2024-10-31"] }] }
206
+ { "or": [{ "==": [{ "var": "name" }, null] }, { "==": [{ "var": "name" }, ""] }] }
207
+ { "==": [{ "var": "active" }, true] }
157
208
  ```
158
209
 
159
210
  Notas sobre enum/CSV:
@@ -294,41 +345,15 @@ Notas
294
345
 
295
346
  Onde é consumido (preview e runtime)
296
347
 
297
- - Preview/validação no Editor de Regras (usa `DslParser` interno):
298
- - Validação da expressão (parse): projects/praxis-table/src/lib/rules-editor/table-rules-editor.component.ts:536
299
- - Preview “Testar” (parse + evaluate): projects/praxis-table/src/lib/rules-editor/table-rules-editor.component.ts:909
348
+ - Preview/validação no Editor de Regras:
349
+ - Authoring canônico manual e visual em JSON Logic: `projects/praxis-table/src/lib/rules-editor/table-rules-editor.component.ts`
300
350
  - Runtime na Tabela (aplica `rowConditionalStyles`/`conditionalStyles`):
301
- - Parser interno do componente: projects/praxis-table/src/lib/praxis-table.ts:282
351
+ - JSON Logic canônico + compatibilidade transitória de leitura DSL: `projects/praxis-table/src/lib/praxis-table.ts`
302
352
 
303
353
  Observação
304
- - O runtime da tabela injeta o registry DSL padrão para validação/parse/execução.
305
- - Para funções realmente custom, injete via input `[dslFunctionRegistry]` no componente.
306
-
307
- Exemplo de Provider (recomendado)
354
+ - JSON Logic é o contrato canônico para novas regras e para o editor de regras da tabela.
355
+ - O runtime ainda mantém compatibilidade transitória para regras DSL antigas.
308
356
 
309
- ```ts
310
- import { Injectable } from '@angular/core';
311
- import { FunctionRegistry } from '@praxisui/specification';
312
-
313
- @Injectable({ providedIn: 'root' })
314
- export class TableDslRegistryFactory {
315
- private readonly registry = FunctionRegistry.getInstance<any>('orders-table-rules');
316
-
317
- constructor() {
318
- this.registry.clear();
319
- this.registry.register('isStatus', (_row: any, value: any, expected: any) =>
320
- String(value ?? '').toLowerCase() === String(expected ?? '').toLowerCase(),
321
- );
322
- }
323
-
324
- get(): FunctionRegistry<any> {
325
- return this.registry;
326
- }
327
- }
328
-
329
- // Uso no host:
330
- // <praxis-table [dslFunctionRegistry]="tableDslRegistryFactory.get()" ... />
331
- ```
332
357
 
333
358
  ### ⚙️ Painel de Configurações
334
359
 
@@ -722,7 +747,6 @@ sequenceDiagram
722
747
  - `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'`
723
748
  - `snoozeMs: number = 86400000`
724
749
  - `autoOpenSettingsOnOutdated: boolean = false`
725
- - `dslFunctionRegistry: FunctionRegistry<any> | null = null`
726
750
  - Outputs:
727
751
  - `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; trigger?: string; tableId?: string }`
728
752
  - Emitido tanto na verificação leve (304/200) quanto no bootstrap do schema (primeira carga via `loadSchema()`).
@@ -1494,7 +1518,6 @@ Veja também: `docs/host-crud-integration.md` (guia operacional host) e `project
1494
1518
  - `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'` — seleciona como o runtime publica avisos de drift quando a política operacional estiver habilitada.
1495
1519
  - `snoozeMs: number = 86400000` — tempo de soneca para avisos (ms).
1496
1520
  - `autoOpenSettingsOnOutdated: boolean = false` — abre Configurações ao detectar schema desatualizado.
1497
- - `dslFunctionRegistry: FunctionRegistry<any> | null = null` — adiciona funções DSL custom sem substituir o registry nativo.
1498
1521
  - Output:
1499
1522
  - `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; tableId?: string }` — emitido após verificação leve (304/200).
1500
1523
 
@@ -1637,3 +1660,4 @@ Apache-2.0 — consulte `LICENSE` na raiz do workspace para detalhes.
1637
1660
  **Parte do Praxis UI Workspace**
1638
1661
  **Versão**: 2.0.0 (Unified Architecture)
1639
1662
  **Compatibilidade**: Angular 18+
1663
+
@@ -591,6 +591,20 @@ Columns Analysis:
591
591
  'behavior.expansion.detail.source.inlineSchema',
592
592
  'behavior.expansion.detail.source.resourcePath.paramsMap',
593
593
  ]);
594
+ const jsonLogicObjectPaths = new Set([
595
+ 'columns[].computed.expression',
596
+ 'columns[].conditionalStyles[].condition',
597
+ 'columns[].conditionalRenderers[].condition',
598
+ 'rowConditionalStyles[].condition',
599
+ 'toolbar.actions[].visibleWhen',
600
+ 'toolbar.actions[].children[].visibleWhen',
601
+ 'actions.row.actions[].visibleWhen',
602
+ 'actions.row.actions[].disabledWhen',
603
+ 'columns[].renderer.button.disabledCondition',
604
+ 'columns[].renderer.toggle.disabledCondition',
605
+ 'columns[].conditionalRenderers[].renderer.button.disabledCondition',
606
+ 'columns[].conditionalRenderers[].renderer.toggle.disabledCondition',
607
+ ]);
594
608
  const recurse = (obj, currentPath) => {
595
609
  if (typeof obj !== 'object' || obj === null)
596
610
  return obj;
@@ -613,30 +627,26 @@ Columns Analysis:
613
627
  const prefixMatch = Array.from(allowedPaths).some(p => p.startsWith(newPath + '.') || p.startsWith(newPath + '['));
614
628
  if (exactMatch || prefixMatch) {
615
629
  if (exactMatch
616
- && passthroughObjectPaths.has(newPath)
630
+ && (passthroughObjectPaths.has(newPath) || jsonLogicObjectPaths.has(newPath))
617
631
  && typeof obj[key] === 'object'
618
- && obj[key] !== null) {
632
+ && obj[key] !== null
633
+ && !Array.isArray(obj[key])) {
619
634
  cleanObj[key] = obj[key];
620
635
  continue;
621
636
  }
622
- const val = recurse(obj[key], newPath);
623
637
  if (newPath === 'columns[].computed.expression') {
624
- if (typeof val !== 'string') {
625
- warnings.push(`Computed expression inválida: ${newPath} (não é string)`);
638
+ if (!obj[key] || typeof obj[key] !== 'object' || Array.isArray(obj[key])) {
639
+ warnings.push(`Computed expression inválida: ${newPath} (deve ser Json Logic)`);
626
640
  continue;
627
641
  }
628
- const trimmed = val.trim();
629
- if (!trimmed) {
642
+ if (Object.keys(obj[key]).length === 0) {
630
643
  warnings.push(`Computed expression vazia: ${newPath}`);
631
644
  continue;
632
645
  }
633
- if (trimmed.length > 200) {
634
- warnings.push(`Computed expression muito longa: ${newPath}`);
635
- continue;
636
- }
637
- cleanObj[key] = trimmed;
646
+ cleanObj[key] = obj[key];
638
647
  continue;
639
648
  }
649
+ const val = recurse(obj[key], newPath);
640
650
  // Se for objeto vazio após limpeza, não inclui (salvo se for intenção explicita de limpar config, mas patch geralmente é aditivo)
641
651
  if (typeof val === 'object' && val !== null && !Array.isArray(val) && Object.keys(val).length === 0) {
642
652
  // ignora