@praxisui/table 3.0.0-beta.0 → 3.0.0-beta.10

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
@@ -379,9 +379,30 @@ Quando `resourcePath` é fornecido, a tabela se torna "inteligente":
379
379
  Neste exemplo:
380
380
 
381
381
  - `resourcePath="human-resources/departamentos"` instrui a tabela a se comunicar com o endpoint `/api/human-resources/departamentos`.
382
- - A tabela fará requisições como `POST /api/human-resources/departamentos/filter` para obter os dados e `GET /api/human-resources/departamentos/schemas` para obter a configuração das colunas.
382
+ - 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/all&operation=get&schemaType=response` para obter o schema estrutural usado no bootstrap das colunas.
383
383
  - `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.
384
384
 
385
+ ### Consulta Declarativa com `queryContext`
386
+
387
+ Para orquestração entre widgets em `praxis-dynamic-page`, o contrato semântico primário da tabela passa a ser `queryContext`.
388
+
389
+ ```html
390
+ <praxis-table
391
+ resourcePath="human-resources/departamentos"
392
+ [queryContext]="{
393
+ filters: { status: 'ACTIVE' },
394
+ sort: ['nome,asc'],
395
+ page: { index: 0, size: 25 }
396
+ }"
397
+ ></praxis-table>
398
+ ```
399
+
400
+ Regras atuais:
401
+
402
+ - `queryContext.filters` já participa do pipeline efetivo da tabela
403
+ - `filterCriteria` continua aceito como bridge legada de compatibilidade
404
+ - em novo authoring, examples e bindings, prefira `queryContext`
405
+
385
406
  ### Empty State + Quick Connect (sem `resourcePath`)
386
407
 
387
408
  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:
@@ -394,20 +415,22 @@ Ao aplicar/salvar, a `<praxis-table>` é automaticamente configurada para buscar
394
415
 
395
416
  ### Fluxo de Comunicação do `resourcePath`
396
417
 
397
- O diagrama abaixo ilustra como a propriedade `resourcePath` conecta o componente frontend ao controller do backend. O fluxo de inicialização ocorre em três etapas principais: **Configurar**, **Carregar Schema** e **Buscar Dados**.
418
+ 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**.
398
419
 
399
420
  ```mermaid
400
421
  sequenceDiagram
401
422
  participant FE_Component as Componente Angular<br>(departamentos.html)
402
423
  participant Praxis_Table as @praxisui/table<br>(praxis-table.ts)
403
424
  participant Crud_Service as @praxisui/core<br>(generic-crud.service.ts)
425
+ participant Docs_Controller as ApiDocsController<br>(/schemas/filtered)
404
426
  participant BE_Controller as Backend Controller<br>(DepartamentoController.java)
405
- participant Abstract_Controller as AbstractCrudController
427
+ participant Abstract_Controller as AbstractCrudController<br>(/filter)
406
428
 
407
429
  FE_Component->>Praxis_Table: Usa o componente com <br> <praxis-table resourcePath="human-resources/departamentos">
408
430
 
409
431
  activate Praxis_Table
410
- Praxis_Table->>Praxis_Table: ngOnChanges() detecta o @Input() resourcePath
432
+ Praxis_Table->>Praxis_Table: ngOnInit()/ngOnChanges() resolve dataMode
433
+ Note over Praxis_Table: remote quando há resourcePath efetivo
411
434
  Praxis_Table->>Crud_Service: 1. Chama crudService.configure("human-resources/departamentos")
412
435
 
413
436
  activate Crud_Service
@@ -418,24 +441,19 @@ sequenceDiagram
418
441
  Praxis_Table->>Crud_Service: Chama crudService.getSchema()
419
442
 
420
443
  activate Crud_Service
421
- Crud_Service->>Crud_Service: getEndpointUrl('schema') constrói a URL: <br> "/api/human-resources/departamentos/schemas"
422
- Crud_Service->>BE_Controller: Requisição HTTP GET para .../schemas
444
+ Crud_Service->>Crud_Service: schemaUrl() preserva o base path canônico
445
+ Crud_Service->>Crud_Service: Deriva path="/api/human-resources/departamentos/all"<br>operation="get", schemaType="response"
446
+ Crud_Service->>Docs_Controller: GET /schemas/filtered?path=.../all&operation=get&schemaType=response
423
447
  deactivate Crud_Service
424
448
 
425
- activate BE_Controller
426
- Note over BE_Controller: @RequestMapping("/human-resources/departamentos")
427
- BE_Controller->>Abstract_Controller: Herda o método que lida com @GetMapping("/schemas")
428
-
429
- activate Abstract_Controller
430
- Abstract_Controller->>Abstract_Controller: Gera e retorna o Schema da UI
431
- Abstract_Controller-->>BE_Controller: Retorna o Schema
432
- deactivate Abstract_Controller
433
-
434
- BE_Controller-->>Crud_Service: Resposta HTTP com o JSON do Schema
435
- deactivate BE_Controller
449
+ activate Docs_Controller
450
+ Docs_Controller->>Docs_Controller: Resolve grupo OpenAPI a partir do path
451
+ Docs_Controller->>Docs_Controller: Filtra payload estrutural + x-ui
452
+ Docs_Controller-->>Crud_Service: 200/304 com schema + ETag/X-Schema-Hash
453
+ deactivate Docs_Controller
436
454
 
437
455
  activate Crud_Service
438
- Crud_Service-->>Praxis_Table: Retorna um Observable com as<br>definições de colunas (FieldDefinition[])
456
+ Crud_Service-->>Praxis_Table: Normaliza schema e retorna FieldDefinition[]
439
457
  deactivate Crud_Service
440
458
 
441
459
  Praxis_Table->>Praxis_Table: Constrói as colunas da tabela<br>a partir do schema recebido
@@ -460,13 +478,20 @@ sequenceDiagram
460
478
  deactivate BE_Controller
461
479
 
462
480
  activate Crud_Service
463
- Crud_Service-->>Praxis_Table: Retorna um Observable com os dados
481
+ Crud_Service-->>Praxis_Table: Retorna Page<T> com os dados paginados
464
482
  deactivate Crud_Service
465
483
 
466
484
  Praxis_Table->>Praxis_Table: Atualiza o dataSource da tabela com os dados recebidos
467
485
  deactivate Praxis_Table
468
486
  ```
469
487
 
488
+ Notas importantes:
489
+
490
+ - 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.
491
+ - 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`.
492
+ - O `praxis-filter` acoplado acompanha o mesmo `resourcePath` em modo remoto e deriva seu schema via `/schemas/filtered?path=.../filter&operation=post&schemaType=request`.
493
+ - Sem `resourcePath` efetivo, a tabela não entra nesse fluxo remoto; ela cai em `local` ou `empty` conforme a precedência canônica.
494
+
470
495
  ## ℹ️ Dicas de UX (Tabela)
471
496
 
472
497
  - Multi‑sort: habilite em Comportamento → Ordenação. No uso, mantenha Ctrl/Cmd pressionado ao clicar em múltiplas colunas.
@@ -805,12 +830,13 @@ sequenceDiagram
805
830
  autonumber
806
831
  participant PT as PraxisTable
807
832
  participant GS as GenericCrudService
808
- participant API as Backend
833
+ participant Docs as ApiDocsController (/schemas/filtered)
809
834
 
810
835
  PT->>GS: configure(resourcePath)
811
836
  PT->>GS: getSchema()
812
- GS->>API: GET {resource}/schemas 302 → /schemas/filtered
813
- API-->>GS: 200/304 schema + x-ui.resource.idField
837
+ GS->>GS: deriva path=/api/.../all + schemaType=response
838
+ GS->>Docs: GET /schemas/filtered?path=.../all&operation=get&schemaType=response
839
+ Docs-->>GS: 200/304 schema + x-ui.resource.idField
814
840
  GS->>GS: cache + lastResourceMeta.idField
815
841
  GS-->>PT: FieldDefinition[] (normalizado)
816
842
  Note over PT: idField = input || context || GS.getResourceIdField() || 'id'
@@ -827,11 +853,12 @@ sequenceDiagram
827
853
  participant API as Backend
828
854
 
829
855
  PT->>PT: key = getIdField() (precedência)
830
- PT->>PT: id = row[key] || row['id'] || heurísticas
856
+ PT->>PT: id = getNestedPropertyValue(row, key)
831
857
  PT->>GS: delete(id)
832
858
  GS->>API: DELETE {resource}/{id}
833
859
  API-->>GS: 204 No Content
834
- GS-->>PT: sucesso → refresh
860
+ GS-->>PT: sucesso
861
+ PT->>PT: fetchData() em modo remoto
835
862
  ```
836
863
 
837
864
  ### Troubleshooting rápido (idField)
@@ -882,78 +909,113 @@ export class ExampleComponent {
882
909
 
883
910
  ## ⚙️ Fluxo de Paginação e Filtros (Server-Side)
884
911
 
885
- Quando a `<praxis-table>` é conectada a um `resourcePath`, as operações de paginação, ordenação e filtro são delegadas ao backend. Isso garante alta performance, pois apenas os dados visíveis na tela são trafegados pela rede.
912
+ 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`.
886
913
 
887
914
  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.
888
915
 
889
- O diagrama abaixo detalha a sequência de eventos, desde a interação do usuário na UI até a construção da consulta JPA no servidor.
916
+ ### Versão Pedagógica de Alto Nível
917
+
918
+ Esta é a leitura adequada para documentação pública e landing pages.
890
919
 
891
920
  ```mermaid
892
921
  sequenceDiagram
893
- participant UI as @praxisui/table (UI)
894
- participant CrudService as @praxisui/core (GenericCrudService)
895
- participant Controller as Backend (AbstractCrudController)
896
- participant Service as Backend (BaseCrudService)
897
- participant SpecBuilder as Backend (GenericSpecificationsBuilder)
898
- participant Repository as Spring Data JPA (JpaSpecificationExecutor)
899
-
900
- UI->>UI: Usuário clica na página 2 e<br>digita "Tech" no filtro de nome.
901
-
902
- UI->>UI: onPageChange({pageIndex: 1, pageSize: 10})<br>onFilterChange({nome: 'Tech'})
903
-
904
- UI->>UI: Chama fetchData() com:<br>filterCriteria = {nome: 'Tech'}<br>pageable = {pageNumber: 1, pageSize: 10, sort: 'nome,asc'}
905
-
906
- UI->>CrudService: Chama .filter({nome: 'Tech'}, pageable)
907
-
908
- activate CrudService
909
- CrudService->>CrudService: Cria HttpParams:<br>page=1, size=10, sort=nome,asc
910
- CrudService->>Controller: Requisição POST para /api/.../filter<br>Body: {nome: 'Tech'}<br>Params: ?page=1&size=10&sort=nome,asc
911
- deactivate CrudService
912
-
913
- activate Controller
914
- Controller->>Controller: Spring injeta o corpo no FilterDTO<br>e os params no objeto Pageable.
915
- Controller->>Service: Chama .filter(filterDTO, pageable)
916
- deactivate Controller
917
-
918
- activate Service
919
- Service->>SpecBuilder: Chama .buildSpecification(filterDTO)
922
+ participant Usuario as Usuário
923
+ participant Tabela as praxis-table
924
+ participant Frontend as GenericCrudService
925
+ participant Backend as API Praxis
926
+ participant Banco as Banco de Dados
927
+
928
+ Usuario->>Tabela: muda página, ordena ou aplica filtro
929
+ Tabela->>Tabela: consolida filterCriteria + paginação + sort
930
+ Tabela->>Frontend: filter(criteria, pageable)
931
+ Frontend->>Backend: POST /api/.../filter?page&size&sort
932
+ Backend->>Banco: executa consulta filtrada e paginada
933
+ Banco-->>Backend: retorna página de resultados
934
+ Backend-->>Frontend: responde com página de DTOs
935
+ Frontend-->>Tabela: entrega Page<T>
936
+ Tabela->>Tabela: atualiza linhas, paginação e estado visual
937
+ ```
920
938
 
921
- activate SpecBuilder
922
- SpecBuilder->>SpecBuilder: Itera nos campos do FilterDTO.<br>Encontra @Filterable no campo 'nome'.
923
- SpecBuilder->>SpecBuilder: Cria um Predicate JPA:<br> `criteriaBuilder.like(root.get("nome"), "%tech%")`
924
- SpecBuilder->>Service: Retorna a Specification JPA construída.
925
- deactivate SpecBuilder
939
+ Leitura pedagógica:
926
940
 
927
- Service->>Repository: Chama .findAll(specification, pageable)
941
+ 1. **A tabela captura a intenção do usuário**: página, ordenação e filtros viram estado de consulta.
942
+ 2. **O frontend envia o DTO de filtro para o endpoint canônico**: corpo JSON para filtro, query params para paginação e sort.
943
+ 3. **O backend aplica o filtro no conjunto remoto**: não é a UI que filtra localmente em modo remoto.
944
+ 4. **O resultado volta já paginado**: a tabela apenas renderiza a página retornada pela API.
928
945
 
929
- activate Repository
930
- Repository->>Repository: Spring Data JPA executa a<br>query SQL com WHERE, LIMIT, OFFSET, ORDER BY.
931
- Repository-->>Service: Retorna um objeto Page<Entity> do BD.
932
- deactivate Repository
946
+ ### Versão Detalhada e Fiel ao Runtime Atual
933
947
 
934
- Service-->>Controller: Retorna o Page<Entity>
935
- deactivate Service
948
+ Esta versão documenta o fluxo real do `praxis-table`, do `praxis-filter`, do `GenericCrudService` e do `praxis-metadata-starter`.
936
949
 
937
- activate Controller
938
- Controller->>Controller: Mapeia Page<Entity> para Page<DTO><br>e encapsula em RestApiResponse.
939
- Controller-->>CrudService: Resposta HTTP 200 com<br>JSON do RestApiResponse.
940
- deactivate Controller
950
+ ```mermaid
951
+ sequenceDiagram
952
+ participant Host as Host / bindings
953
+ participant Tabela as PraxisTable
954
+ participant Filtro as PraxisFilter
955
+ participant Crud as GenericCrudService
956
+ participant Controller as AbstractCrudController
957
+ participant Service as BaseCrudService
958
+ participant Builder as GenericSpecificationsBuilder
959
+ participant Repo as JpaSpecificationExecutor
960
+
961
+ Host->>Tabela: fornece resourcePath
962
+ Tabela->>Crud: configure(resourcePath)
963
+ alt sem colunas prontas
964
+ Tabela->>Crud: getSchema()
965
+ Crud-->>Tabela: schema normalizado
966
+ else com colunas prontas
967
+ Tabela->>Tabela: verifyServerSchemaVersion()
968
+ end
941
969
 
942
- activate CrudService
943
- CrudService-->>UI: Retorna Observable<Page<DTO>>
944
- deactivate CrudService
970
+ alt advancedFilters habilitado
971
+ Tabela->>Filtro: monta praxis-filter
972
+ Filtro->>Crud: getFilteredSchema(...) ou getSchema()
973
+ Crud-->>Filtro: metadata de filtro
974
+ end
945
975
 
946
- UI->>UI: Atualiza a tabela com os novos dados e o paginador.
976
+ alt paginação
977
+ Tabela->>Tabela: onPageChange(pageIndex, pageSize)
978
+ else ordenação
979
+ Tabela->>Tabela: onSortChange(active, direction)
980
+ Tabela->>Tabela: reseta pageIndex = 0
981
+ else filtro avançado
982
+ Filtro-->>Tabela: submit/change/clear
983
+ Tabela->>Tabela: onAdvancedFilterSubmit(criteria)
984
+ end
947
985
 
986
+ Tabela->>Tabela: fetchData()
987
+ Tabela->>Tabela: monta PageableRequest com pageIndex, pageSize e sortField efetivo
988
+ Tabela->>Crud: filter(filterCriteria, pageable)
989
+
990
+ Crud->>Crud: normalizeRangeCriteria(...)
991
+ Crud->>Controller: POST /filter?page&size&sort
992
+ Note over Crud,Controller: body = filterCriteria JSON
993
+
994
+ Controller->>Controller: lê page,size,sort de query params
995
+ Controller->>Controller: PageableBuilder.from(page,size,sort,...)
996
+ Controller->>Service: filterMappedWithIncludeIds(filterDTO, pageable, includeIds, toDto)
997
+
998
+ Service->>Builder: buildSpecification(filterDTO, pageable)
999
+ Builder->>Builder: inspeciona campos @Filterable do DTO
1000
+ Builder-->>Service: Specification pronta
1001
+ Service->>Repo: findAll(specification, pageable)
1002
+ Repo-->>Service: Page<Entity>
1003
+ Service-->>Controller: Page<DTO>
1004
+
1005
+ Controller->>Controller: mapeia para EntityModel<DTO> e monta RestApiResponse.success(...)
1006
+ Controller-->>Crud: HTTP 200
1007
+ Crud->>Crud: unwrapPageResponse(...)
1008
+ Crud-->>Tabela: Page<T>
1009
+ Tabela->>Tabela: atualiza dataSubject, totalElements e paginator
948
1010
  ```
949
1011
 
950
- ### Pontos-Chave do Fluxo:
1012
+ ### Pontos-Chave do Fluxo Real
951
1013
 
952
- 1. **UI (`@praxisui/table`)**: Captura eventos do usuário e os traduz em objetos `filterCriteria` e `pageable`.
953
- 2. **Serviço Frontend (`@praxisui/core`)**: O `GenericCrudService` serializa o `pageable` como parâmetros de URL e o `filterCriteria` como corpo de uma requisição POST.
954
- 3. **Controller Backend**: O `AbstractCrudController` recebe a requisição. O Spring Boot automaticamente popula o DTO de filtro com o corpo da requisição e o objeto `Pageable` com os parâmetros da URL.
955
- 4. **Serviço Backend (`praxis-metadata-core`)**: O `GenericSpecificationsBuilder` inspeciona as anotações `@Filterable` no DTO de filtro para construir uma `Specification` JPA dinâmica.
956
- 5. **Repositório (Spring Data JPA)**: O `JpaSpecificationExecutor` (geralmente estendido pelo seu repositório) usa a `Specification` e o `Pageable` para gerar e executar a consulta SQL final, otimizada para o banco de dados.
1014
+ 1. **`resourcePath` não dispara só fetch**: em modo remoto, a tabela primeiro configura o `GenericCrudService` e pode carregar schema antes da primeira busca.
1015
+ 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`.
1016
+ 3. **O controller não recebe `Pageable` pronto do Spring nesse endpoint**: ele reconstrói o `Pageable` a partir de `page`, `size` e `sort`.
1017
+ 4. **O backend do starter responde com `RestApiResponse<Page<...>>`**: o `GenericCrudService` faz `unwrapPageResponse(...)` para entregar `Page<T>` ao runtime da tabela.
1018
+ 5. **A coluna ordenada pode mapear para `sortField`**: o `active` da UI não é necessariamente o campo final enviado ao backend.
957
1019
 
958
1020
  ## 🎨 Edição Visual da Tabela: O Poder do Low-Code
959
1021
 
@@ -1366,7 +1428,7 @@ Para documentação completa da API, consulte `projects/praxis-core/README.md`.
1366
1428
  O `PraxisFilter` pode ser acoplado à barra de ferramentas da tabela. O exemplo abaixo mostra a busca de pessoas por CPF e status.
1367
1429
 
1368
1430
  ```html
1369
- <praxis-filter [resourcePath]="'pessoas'" [formId]="'pessoas-filter'" [persistenceKey]="'pessoas-filter-v1'" [alwaysVisibleFields]="['status']" (submit)="onFilter($event)"></praxis-filter> <praxis-table [data]="tableData"></praxis-table>
1431
+ <praxis-filter [resourcePath]="'pessoas'" [formId]="'pessoas-filter'" [persistenceKey]="'pessoas-filter-v1'" [alwaysVisibleFields]="['status']" (requestSearch)="onFilter($event)"></praxis-filter> <praxis-table [data]="tableData"></praxis-table>
1370
1432
  ```
1371
1433
 
1372
1434
  ```ts
@@ -1450,9 +1512,16 @@ Notas rápidas (Flow ETag no Filter):
1450
1512
 
1451
1513
  ## Formatação de Colunas (format)
1452
1514
 
1453
- 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`, `boolean`, `custom`.
1515
+ 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`.
1516
+
1517
+ Regra geral:
1518
+ - `format` explícito continua com precedência máxima.
1519
+ - 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.
1520
+ - `string`, `boolean` e `custom` continuam conservadores; nesses casos, use `format` explícito quando precisar de transformação.
1454
1521
 
1455
- Regra geral: a formatação só é aplicada quando há `format` não vazio e `type !== 'custom'`.
1522
+ Nota de plataforma:
1523
+ - o contrato público da tabela continua sendo `column.type + config.localization + format`.
1524
+ - 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.
1456
1525
 
1457
1526
  Tipos e padrões de `format`:
1458
1527
  - number (DecimalPipe)
@@ -1484,10 +1553,30 @@ Exemplos de ColumnDefinition:
1484
1553
  { field: 'ativo', type: 'boolean', format: 'yes-no', header: 'Ativo' }
1485
1554
  ```
1486
1555
 
1556
+ Exemplos com defaults implícitos por `type + localization`:
1557
+ ```ts
1558
+ {
1559
+ localization: {
1560
+ locale: 'pt-BR',
1561
+ currency: { code: 'BRL', symbol: 'R$', position: 'before', spacing: true, precision: 2 },
1562
+ dateTime: { dateFormat: 'dd/MM/yyyy', timeFormat: 'HH:mm', dateTimeFormat: 'dd/MM/yyyy HH:mm', firstDayOfWeek: 1 },
1563
+ number: { decimalSeparator: ',', thousandsSeparator: '.', defaultPrecision: 2, negativeSign: '-', negativeSignPosition: 'before' },
1564
+ },
1565
+ columns: [
1566
+ { field: 'salario', type: 'currency', header: 'Salário' },
1567
+ { field: 'criadoEm', type: 'date', header: 'Criado em' },
1568
+ { field: 'taxa', type: 'percentage', header: 'Taxa' },
1569
+ ],
1570
+ }
1571
+ ```
1572
+
1487
1573
  Observações:
1488
1574
  - `|nosep` remove separadores de milhar da saída formatada.
1489
1575
  - Para datas inválidas ou valores não numéricos, o serviço retorna o valor original (com aviso no console).
1490
1576
  - Colunas com renderizador `custom` não passam por formatação automática.
1577
+ - Precedência de locale para formatação: `config.localization.locale` -> `LOCALE_ID` do host.
1578
+ - 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.
1579
+ - Quando `config.localization.number` estiver presente, a tabela respeita `decimalSeparator`, `thousandsSeparator`, `defaultPrecision`, `negativeSign` e `negativeSignPosition` em number/currency/percentage.
1491
1580
 
1492
1581
  Exemplos práticos no workspace (rotas):
1493
1582
  - Regras Visuais (Simples): `/table-rules-simple`
@@ -15,4 +15,3 @@ const FILTER_DRAWER_ADAPTER = getFilterDrawerAdapterToken();
15
15
  */
16
16
 
17
17
  export { FILTER_DRAWER_ADAPTER };
18
- //# sourceMappingURL=praxisui-table-filter-drawer-adapter.mjs.map
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Inject, Component } from '@angular/core';
3
- import * as i1$1 from '@angular/common';
3
+ import * as i2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i1 from '@angular/material/dialog';
6
6
  import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
@@ -115,7 +115,7 @@ class FilterFormDialogHostComponent {
115
115
  {{ data.i18n?.apply || 'Aplicar' }}
116
116
  </button>
117
117
  </mat-dialog-actions>
118
- `, isInline: true, styles: [".pfx-dialog-title{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-right:8px}.pfx-dialog-title-text{display:inline-flex;align-items:center;gap:8px;font-weight:600;color:var(--md-sys-color-on-surface)}.pfx-dialog-close{margin-left:auto}.pfx-filter-dialog-content{display:flex;flex-direction:column;gap:12px;padding-top:8px}.pfx-empty-state{margin:8px 0 0;color:var(--md-sys-color-on-surface-variant)}.pfx-dialog-actions{padding:var(--pdx-dialog-actions-padding, 12px 24px 16px);border-top:1px solid var(--md-sys-color-outline-variant);background:transparent;display:flex;align-items:center;gap:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i15.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisFilterForm, selector: "praxis-filter-form", inputs: ["config", "formId", "resourcePath", "mode"], outputs: ["formReady", "valueChange", "submit", "validityChange"] }] });
118
+ `, isInline: true, styles: [".pfx-dialog-title{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-right:8px}.pfx-dialog-title-text{display:inline-flex;align-items:center;gap:8px;font-weight:600;color:var(--md-sys-color-on-surface)}.pfx-dialog-close{margin-left:auto}.pfx-filter-dialog-content{display:flex;flex-direction:column;gap:12px;padding-top:8px}.pfx-empty-state{margin:8px 0 0;color:var(--md-sys-color-on-surface-variant)}.pfx-dialog-actions{padding:var(--pdx-dialog-actions-padding, 12px 24px 16px);border-top:1px solid var(--md-sys-color-outline-variant);background:transparent;display:flex;align-items:center;gap:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i15.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisFilterForm, selector: "praxis-filter-form", inputs: ["config", "formId", "resourcePath", "mode"], outputs: ["formReady", "valueChange", "submit", "requestSearch", "validityChange"] }] });
119
119
  }
120
120
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FilterFormDialogHostComponent, decorators: [{
121
121
  type: Component,
@@ -163,4 +163,3 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
163
163
  }] }, { type: i1.MatDialogRef }] });
164
164
 
165
165
  export { FilterFormDialogHostComponent };
166
- //# sourceMappingURL=praxisui-table-filter-form-dialog-host.component-C2rQnHE1.mjs.map
@@ -819,4 +819,3 @@ Columns Analysis:
819
819
  }
820
820
 
821
821
  export { TableAiAdapter };
822
- //# sourceMappingURL=praxisui-table-table-ai.adapter-C5rjLb8E.mjs.map