@praxisui/table 1.0.0-beta.30 → 1.0.0-beta.40
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 +112 -55
- package/fesm2022/praxisui-table-filter-form-dialog-host.component-CLF05-__.mjs +166 -0
- package/fesm2022/praxisui-table-filter-form-dialog-host.component-CLF05-__.mjs.map +1 -0
- package/fesm2022/praxisui-table-table-ai.adapter-BuNW3YSp.mjs +804 -0
- package/fesm2022/praxisui-table-table-ai.adapter-BuNW3YSp.mjs.map +1 -0
- package/fesm2022/praxisui-table.mjs +17382 -3514
- package/fesm2022/praxisui-table.mjs.map +1 -1
- package/index.d.ts +1164 -430
- package/package.json +7 -6
- package/fesm2022/praxisui-table-filter-form-dialog-host.component-Du_IEq0W.mjs +0 -93
- package/fesm2022/praxisui-table-filter-form-dialog-host.component-Du_IEq0W.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -13,6 +13,20 @@ Para ver esta biblioteca em funcionamento em uma aplicação completa, utilize o
|
|
|
13
13
|
|
|
14
14
|
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.
|
|
15
15
|
|
|
16
|
+
## 🎨 Tema M3 (tokens mínimos)
|
|
17
|
+
|
|
18
|
+
Para garantir que a tabela, filtros e editores reflitam o tema do app host:
|
|
19
|
+
|
|
20
|
+
- Superfícies: `--md-sys-color-surface`, `--md-sys-color-surface-variant`, `--md-sys-color-surface-container-*`
|
|
21
|
+
- 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`
|
|
22
|
+
- Semânticos: `--md-sys-color-primary`, `--md-sys-color-secondary`, `--md-sys-color-tertiary`, `--md-sys-color-error`
|
|
23
|
+
- Containers: `--md-sys-color-primary-container`, `--md-sys-color-secondary-container`, `--md-sys-color-tertiary-container`, `--md-sys-color-error-container`
|
|
24
|
+
- Overlay: `--md-sys-color-scrim`
|
|
25
|
+
- Elevação: `--md-sys-elevation-level1`–`--md-sys-elevation-level3`
|
|
26
|
+
|
|
27
|
+
Observação: tokens `--md-sys-*` e `--mdc-theme-*` continuam aceitos quando presentes.
|
|
28
|
+
Nota: a classe de tema é decisão do host (`.dark-theme` ou `.theme-dark`/`.theme-light`); mantenha tokens e componentes no mesmo escopo.
|
|
29
|
+
|
|
16
30
|
## ✨ Características Principais
|
|
17
31
|
|
|
18
32
|
### 🏗️ Arquitetura Unificada
|
|
@@ -42,7 +56,7 @@ A biblioteca `@praxisui/table` fornece um componente de tabela robusto e altamen
|
|
|
42
56
|
- **Messages Editor**: Textos e localização
|
|
43
57
|
|
|
44
58
|
Nota (Regras)
|
|
45
|
-
- A antiga aba visual genérica foi removida deste pacote.
|
|
59
|
+
- 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.
|
|
46
60
|
|
|
47
61
|
### 🧩 Editor de Regras (column-first)
|
|
48
62
|
|
|
@@ -70,24 +84,26 @@ Notas sobre enum/CSV:
|
|
|
70
84
|
- Para campos enumerados, as opções são inferidas de `valueMapping` da coluna ou de `fields?.options`.
|
|
71
85
|
- Para `in/not in` em números/strings, use CSV (ex.: `10, 20, 30` ou `Alice, Bob`).
|
|
72
86
|
|
|
73
|
-
#### Extensões de DSL (
|
|
87
|
+
#### Extensões de DSL (runtime nativo + custom)
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
O runtime da tabela já inclui helpers de JSON/tempo/listas/texto sem configuração extra:
|
|
76
90
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
- JSON: `jsonGet`, `hasJsonKey`, `jsonPathMatches`, `jsonEq`, `jsonIn`, `jsonType`
|
|
92
|
+
- Texto/valor: `isBlank`, `eqIgnoreCase`, `coalesce`
|
|
93
|
+
- Faixas: `between`, `dateBetween`
|
|
94
|
+
- Coleções: `containsAny`, `containsAll`, `len`, `lenEq`, `lenGt`, `lenLt`, `lenAtLeast`
|
|
95
|
+
- Tempo: `today`, `now`, `dateAdd`, `dateFormat`, `isToday`, `inLast`, `isPast`, `isPastOrNow`, `isFuture`, `isFutureOrNow`, `weekdayIn`
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
Exemplos de DSL (canônica):
|
|
98
|
+
- `jsonEq(payload, '$.user.name', 'Alice')`
|
|
99
|
+
- `jsonIn(customer, '$.id', [10, 20])`
|
|
100
|
+
- `eqIgnoreCase(status, 'ATIVO')`
|
|
101
|
+
- `between(amount, 10, 20)`
|
|
102
|
+
- `dateBetween(createdAt, '2025-01-01', '2025-12-31')`
|
|
103
|
+
- `containsAll(tags, ['vip', 'gold'])`
|
|
86
104
|
|
|
87
|
-
|
|
88
|
-
- `
|
|
89
|
-
- `hasJsonKey(payload, '$.meta.etag')`
|
|
90
|
-
- `jsonPathMatches(payload, '$.roles[0]', '^admin$')`
|
|
105
|
+
Observação de gramática:
|
|
106
|
+
- comparações diretas sobre retorno de função (`fn(...) == 'x'`) não são suportadas pela gramática atual; prefira funções predicativas (booleanas) na `condition`.
|
|
91
107
|
|
|
92
108
|
Guia completo: `projects/praxis-table/docs/DSL-Extensions-Guide.md`
|
|
93
109
|
|
|
@@ -118,8 +134,26 @@ Campos relacionais (FK/objetos) podem ser editados no Rules Editor com busca ass
|
|
|
118
134
|
Boas práticas
|
|
119
135
|
- Informe `resourcePath` (ou `endpoint`) no `FieldDefinition` para habilitar o lookup via `GenericCrudService` do host.
|
|
120
136
|
- Defina `valueField` (chave estável, tipicamente `id`) e `displayField` (label amigável, como `name`).
|
|
137
|
+
|
|
138
|
+
## 🔎 Filtro: Atalhos para Período (DateRange)
|
|
139
|
+
|
|
140
|
+
Habilite atalhos de período no filtro dinâmico sem alterar o contrato do `praxis-filter` — apenas via metadata do campo:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// Em FilterConfig (ou na metadata de campos do filtro)
|
|
144
|
+
{
|
|
145
|
+
name: 'period',
|
|
146
|
+
label: 'Período',
|
|
147
|
+
controlType: 'dateRange',
|
|
148
|
+
showShortcuts: true,
|
|
149
|
+
shortcuts: ['thisMonth','lastMonth','thisYear'],
|
|
150
|
+
// Opcional:
|
|
151
|
+
// i18nShortcuts: { thisMonth: 'Este mês', lastMonth: 'Mês passado' },
|
|
152
|
+
// applyOnShortcutClick: true
|
|
153
|
+
}
|
|
154
|
+
```
|
|
121
155
|
- Para múltipla seleção, use `multiple: true` no `FieldDefinition` quando aplicável.
|
|
122
|
-
- No runtime
|
|
156
|
+
- No runtime da tabela não é necessário registrar funções JSON/data manualmente; os helpers já são embutidos.
|
|
123
157
|
- APIs com busca: suporte a um parâmetro livre de texto (por ex. `search`) facilita a experiência do autocomplete.
|
|
124
158
|
|
|
125
159
|
Exemplo de FieldDefinition (lookup)
|
|
@@ -142,8 +176,8 @@ const fields: FieldDefinition[] = [
|
|
|
142
176
|
```
|
|
143
177
|
|
|
144
178
|
Operadores e DSL gerada (relacional)
|
|
145
|
-
- `id ==` → `
|
|
146
|
-
- `id in` → `
|
|
179
|
+
- `id ==` → `jsonEq(customer, '$.id', 10)`
|
|
180
|
+
- `id in` → `jsonIn(customer, '$.id', [10, 20])`
|
|
147
181
|
- `has property` → `hasJsonKey(customer, '$.meta.etag')`
|
|
148
182
|
- Condições por caminho (join simples): use `key ==` (JSON Path + valor)
|
|
149
183
|
|
|
@@ -161,7 +195,7 @@ Para que o editor/preview e o runtime avaliem corretamente as regras relacionais
|
|
|
161
195
|
const row = {
|
|
162
196
|
id: 123,
|
|
163
197
|
customer: {
|
|
164
|
-
id: 10, // ← usado em id == / id in via
|
|
198
|
+
id: 10, // ← usado em id == / id in via jsonEq/jsonIn
|
|
165
199
|
name: 'ACME', // ← usado para exibir label no autocomplete/chip
|
|
166
200
|
meta: { etag: 'v1' },
|
|
167
201
|
},
|
|
@@ -169,13 +203,13 @@ const row = {
|
|
|
169
203
|
|
|
170
204
|
// Regras DSL comuns
|
|
171
205
|
// id ==
|
|
172
|
-
//
|
|
206
|
+
// jsonEq(customer, '$.id', 10)
|
|
173
207
|
// id in
|
|
174
|
-
//
|
|
208
|
+
// jsonIn(customer, '$.id', [10, 20])
|
|
175
209
|
// has property
|
|
176
210
|
// hasJsonKey(customer, '$.meta.etag')
|
|
177
211
|
// caminho/valor (join simples)
|
|
178
|
-
//
|
|
212
|
+
// jsonEq(customer, '$.name', 'ACME')
|
|
179
213
|
```
|
|
180
214
|
|
|
181
215
|
Notas
|
|
@@ -191,36 +225,33 @@ Onde é consumido (preview e runtime)
|
|
|
191
225
|
- Parser interno do componente: projects/praxis-table/src/lib/praxis-table.ts:282
|
|
192
226
|
|
|
193
227
|
Observação
|
|
194
|
-
-
|
|
228
|
+
- O runtime da tabela já injeta o registry DSL padrão para validação/parse/execução.
|
|
229
|
+
- Para funções realmente custom, injete via input `[dslFunctionRegistry]` no componente.
|
|
195
230
|
|
|
196
231
|
Exemplo de Provider (recomendado)
|
|
197
232
|
|
|
198
233
|
```ts
|
|
199
234
|
import { Injectable } from '@angular/core';
|
|
200
|
-
import {
|
|
201
|
-
import { registerJsonDslFunctions, registerDateDslFunctions } from '@praxisui/table';
|
|
235
|
+
import { FunctionRegistry } from '@praxisui/specification';
|
|
202
236
|
|
|
203
237
|
@Injectable({ providedIn: 'root' })
|
|
204
|
-
export class
|
|
205
|
-
private
|
|
238
|
+
export class TableDslRegistryFactory {
|
|
239
|
+
private readonly registry = FunctionRegistry.getInstance<any>('orders-table-rules');
|
|
206
240
|
|
|
207
241
|
constructor() {
|
|
208
|
-
this.
|
|
209
|
-
|
|
210
|
-
|
|
242
|
+
this.registry.clear();
|
|
243
|
+
this.registry.register('isStatus', (_row: any, value: any, expected: any) =>
|
|
244
|
+
String(value ?? '').toLowerCase() === String(expected ?? '').toLowerCase(),
|
|
245
|
+
);
|
|
211
246
|
}
|
|
212
247
|
|
|
213
|
-
get():
|
|
214
|
-
return this.
|
|
248
|
+
get(): FunctionRegistry<any> {
|
|
249
|
+
return this.registry;
|
|
215
250
|
}
|
|
216
251
|
}
|
|
217
252
|
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
// const parser = factory.get();
|
|
221
|
-
// const spec = parser.parse("jsonGet(payload, '$.user.name') == 'Alice'");
|
|
222
|
-
// const ok = spec.isSatisfiedBy({ payload: { user: { name: 'Alice' } } });
|
|
223
|
-
// }
|
|
253
|
+
// Uso no host:
|
|
254
|
+
// <praxis-table [dslFunctionRegistry]="tableDslRegistryFactory.get()" ... />
|
|
224
255
|
```
|
|
225
256
|
|
|
226
257
|
### ⚙️ Painel de Configurações
|
|
@@ -641,7 +672,7 @@ onSchemaStatus(ev: { outdated: boolean; serverHash?: string; lastVerifiedAt?: st
|
|
|
641
672
|
### Boas práticas para estabilidade
|
|
642
673
|
|
|
643
674
|
- Evite literais em bindings para o carregador dinâmico:
|
|
644
|
-
- Não use `[fields]="[
|
|
675
|
+
- Não use `[fields]="[pinnedFieldMeta]"`; prefira um array estável como `pinnedFieldMetaArray` atualizado apenas quando o metadata mudar.
|
|
645
676
|
- 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.
|
|
646
677
|
- Evite recriar arrays/objetos desnecessariamente em `ngOnChanges`/`ngAfterViewInit`. Prefira atualizar valores (`setValue`, `patchValue`) e manter referências estáveis.
|
|
647
678
|
- Observers (Resize/Mutation): atualize `overlayOrigin` e largura apenas quando houver mudanças reais; isso evita loops de CD.
|
|
@@ -726,7 +757,9 @@ sequenceDiagram
|
|
|
726
757
|
|
|
727
758
|
### Uso com Dados Locais (Client-Side)
|
|
728
759
|
|
|
729
|
-
Se você precisar fornecer os dados manualmente (por exemplo, de uma fonte que não é uma API Praxis), pode usar o input `[data]` e omitir o `resourcePath`.
|
|
760
|
+
Se você precisar fornecer os dados manualmente (por exemplo, de uma fonte que não é uma API Praxis), pode usar o input `[data]` e omitir o `resourcePath`.
|
|
761
|
+
Importante: o modo local está protegido por feature flag e exige `behavior.localDataMode.enabled = true` (default é `false`).
|
|
762
|
+
Quando a flag está ativa, as operações de paginação, ordenação e filtro são executadas no lado do cliente.
|
|
730
763
|
|
|
731
764
|
```typescript
|
|
732
765
|
import { PraxisTable } from "@praxisui/table";
|
|
@@ -746,12 +779,13 @@ export class ExampleComponent {
|
|
|
746
779
|
{ field: "name", header: "Nome", type: "string" },
|
|
747
780
|
{ field: "email", header: "Email", type: "string" },
|
|
748
781
|
],
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
782
|
+
behavior: {
|
|
783
|
+
pagination: { enabled: true, pageSize: 10 },
|
|
784
|
+
sorting: { enabled: true },
|
|
785
|
+
filtering: { enabled: true },
|
|
786
|
+
localDataMode: { enabled: true }, // obrigatório para fluxo local oficial
|
|
787
|
+
},
|
|
788
|
+
};
|
|
755
789
|
|
|
756
790
|
employees = [
|
|
757
791
|
{ id: 1, name: "João Silva", email: "joao@empresa.com" },
|
|
@@ -1246,7 +1280,7 @@ Para documentação completa da API, consulte a [documentação da @praxisui/cor
|
|
|
1246
1280
|
O `PraxisFilter` pode ser acoplado à barra de ferramentas da tabela. O exemplo abaixo mostra a busca de pessoas por CPF e status.
|
|
1247
1281
|
|
|
1248
1282
|
```html
|
|
1249
|
-
<praxis-filter [resourcePath]="'pessoas'" [formId]="'pessoas-filter'" [persistenceKey]="'pessoas-filter-v1'" [
|
|
1283
|
+
<praxis-filter [resourcePath]="'pessoas'" [formId]="'pessoas-filter'" [persistenceKey]="'pessoas-filter-v1'" [alwaysVisibleFields]="['status']" (submit)="onFilter($event)"></praxis-filter> <praxis-table [data]="tableData"></praxis-table>
|
|
1250
1284
|
```
|
|
1251
1285
|
|
|
1252
1286
|
```ts
|
|
@@ -1258,15 +1292,15 @@ onFilter(dto: any) {
|
|
|
1258
1292
|
}
|
|
1259
1293
|
```
|
|
1260
1294
|
|
|
1261
|
-
### ⚙️
|
|
1295
|
+
### ⚙️ Configuração do Filtro
|
|
1262
1296
|
|
|
1263
|
-
O `PraxisFilter`
|
|
1264
|
-
|
|
1297
|
+
O `PraxisFilter` pode ser configurado por inputs/JSON de configuração. O atalho
|
|
1298
|
+
visual por ícone de engrenagem não é exposto por padrão na tabela/demo.
|
|
1299
|
+
Quando necessário, também é possível abrir o painel programaticamente via
|
|
1265
1300
|
`openSettings()`. Nesse painel é possível ajustar:
|
|
1266
1301
|
|
|
1267
|
-
- **quickField** – campo utilizado para a busca rápida
|
|
1268
1302
|
- **alwaysVisibleFields** – campos que permanecem sempre visíveis
|
|
1269
|
-
- **
|
|
1303
|
+
- **alwaysVisibleFieldMetadataOverrides** – patch de metadata por campo sempre visível (controle, clearButton, inlineAutoSize etc.)
|
|
1270
1304
|
- **showAdvanced** – define se a seção avançada inicia aberta
|
|
1271
1305
|
|
|
1272
1306
|
```ts
|
|
@@ -1278,9 +1312,32 @@ abrirConfiguracoes() {
|
|
|
1278
1312
|
```
|
|
1279
1313
|
|
|
1280
1314
|
Ao aplicar ou salvar, as escolhas são validadas contra os metadados
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
uma
|
|
1315
|
+
do **DTO de filtro** (schema de `POST /{resource}/filter`). Campos ausentes
|
|
1316
|
+
no DTO não são aplicados em `alwaysVisibleFields`.
|
|
1317
|
+
O componente exibe uma barra de progresso durante o processo de persistência e
|
|
1318
|
+
mensagens de sucesso ou erro via _snack bar_, garantindo uma experiência
|
|
1319
|
+
consistente.
|
|
1320
|
+
|
|
1321
|
+
### 🔖 Mini‑guia: Atalhos do Filtro (tags)
|
|
1322
|
+
|
|
1323
|
+
Atalhos são “chips” que guardam um conjunto de filtros (DTO) para reuso rápido.
|
|
1324
|
+
|
|
1325
|
+
- Criar/Salvar
|
|
1326
|
+
- Modal (padrão): o host do diálogo exibe o botão quando `allowSaveTags === true`.
|
|
1327
|
+
- Gaveta (Drawer): o Adapter recebe `allowSaveTags?`, `i18nSaveAsShortcut?` e `onSaveShortcut?` (opcionais). O host deve exibir o botão e chamar `onSaveShortcut(clean({ ...initialDto, ...lastValue }))`.
|
|
1328
|
+
- O DTO é limpo antes de persistir (remove `'' | null | undefined`).
|
|
1329
|
+
- Aplicar
|
|
1330
|
+
- Clique/Enter no chip aplica imediatamente `tag.patch` (substitui o estado atual), emite `submit` e persiste.
|
|
1331
|
+
- O chip ativo é destacado (cor/borda + ícone de “check”) quando o DTO atual é igual ao patch do atalho.
|
|
1332
|
+
- Editar/Renomear
|
|
1333
|
+
- Apenas atalhos do usuário exibem ícone de lápis. Predefinidos mostram ícone de “cadeado” (somente leitura).
|
|
1334
|
+
- Excluir (com Undo)
|
|
1335
|
+
- Exclusão remove imediatamente o atalho do usuário e exibe snackbar “Atalho removido” com ação “Desfazer” quando `confirmTagDelete === true`.
|
|
1336
|
+
- Ao clicar em “Desfazer”, o atalho é restaurado na mesma posição e os eventos são reemitidos.
|
|
1337
|
+
- i18n
|
|
1338
|
+
- Chaves úteis: `saveAsShortcut`, `renameShortcut`, `removeShortcut`, `shortcutSaved`, `shortcutRemoved`, `undo`, `readonlyShortcut`.
|
|
1339
|
+
|
|
1340
|
+
Veja também: “Guia de Integração — Hosts (filtro em gaveta)” em `docs/host-crud-integration.md` para detalhes do contrato do Adapter.
|
|
1284
1341
|
|
|
1285
1342
|
### Novos Inputs/Outputs (PraxisFilter)
|
|
1286
1343
|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Inject, Component } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import * as i1 from '@angular/material/dialog';
|
|
6
|
+
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
|
7
|
+
import * as i3 from '@angular/material/button';
|
|
8
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
9
|
+
import * as i15 from '@angular/material/progress-bar';
|
|
10
|
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
11
|
+
import * as i5 from '@angular/material/icon';
|
|
12
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
13
|
+
import { PraxisFilterForm } from '@praxisui/dynamic-form';
|
|
14
|
+
|
|
15
|
+
class FilterFormDialogHostComponent {
|
|
16
|
+
data;
|
|
17
|
+
ref;
|
|
18
|
+
valid = true;
|
|
19
|
+
lastValue = {};
|
|
20
|
+
formGroup = null;
|
|
21
|
+
canSave = false;
|
|
22
|
+
constructor(data, ref) {
|
|
23
|
+
this.data = data;
|
|
24
|
+
this.ref = ref;
|
|
25
|
+
}
|
|
26
|
+
onReady(ev) {
|
|
27
|
+
try {
|
|
28
|
+
this.formGroup = ev?.formGroup || null;
|
|
29
|
+
const dto = this.data?.initialDto || {};
|
|
30
|
+
if (ev?.formGroup && dto && Object.keys(dto).length) {
|
|
31
|
+
ev.formGroup.patchValue(dto, { emitEvent: false });
|
|
32
|
+
}
|
|
33
|
+
this.lastValue = this.formGroup?.getRawValue?.() ?? this.lastValue;
|
|
34
|
+
this.updateCanSave();
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
}
|
|
38
|
+
onChange(ev) {
|
|
39
|
+
this.lastValue = ev?.formData ?? {};
|
|
40
|
+
this.updateCanSave();
|
|
41
|
+
}
|
|
42
|
+
onValidity(v) {
|
|
43
|
+
this.valid = v;
|
|
44
|
+
this.updateCanSave();
|
|
45
|
+
}
|
|
46
|
+
apply() {
|
|
47
|
+
const formData = this.formGroup?.getRawValue?.() ??
|
|
48
|
+
this.lastValue ??
|
|
49
|
+
this.data?.initialDto ??
|
|
50
|
+
{};
|
|
51
|
+
this.ref.close({ formData });
|
|
52
|
+
}
|
|
53
|
+
close() { this.ref.close(); }
|
|
54
|
+
clean(obj) {
|
|
55
|
+
const out = {};
|
|
56
|
+
Object.keys(obj || {}).forEach((k) => {
|
|
57
|
+
const v = obj[k];
|
|
58
|
+
if (v !== '' && v !== null && v !== undefined)
|
|
59
|
+
out[k] = v;
|
|
60
|
+
});
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
buildDtoForSave() {
|
|
64
|
+
const base = this.data?.initialDto || {};
|
|
65
|
+
const merged = { ...base, ...(this.lastValue || {}) };
|
|
66
|
+
return this.clean(merged);
|
|
67
|
+
}
|
|
68
|
+
updateCanSave() {
|
|
69
|
+
this.canSave = Object.keys(this.buildDtoForSave() || {}).length > 0;
|
|
70
|
+
}
|
|
71
|
+
saveShortcut() {
|
|
72
|
+
try {
|
|
73
|
+
const dto = this.buildDtoForSave();
|
|
74
|
+
if (!Object.keys(dto).length)
|
|
75
|
+
return;
|
|
76
|
+
this.data?.onSaveShortcut?.(dto);
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
}
|
|
80
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: FilterFormDialogHostComponent, deps: [{ token: MAT_DIALOG_DATA }, { token: i1.MatDialogRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
81
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: FilterFormDialogHostComponent, isStandalone: true, selector: "praxis-filter-form-dialog-host", ngImport: i0, template: `
|
|
82
|
+
<div mat-dialog-title class="pfx-dialog-title" id="filterDialogTitle">
|
|
83
|
+
<div class="pfx-dialog-title-text">
|
|
84
|
+
<mat-icon>tune</mat-icon>
|
|
85
|
+
<span>{{ data.title || 'Filtro avançado' }}</span>
|
|
86
|
+
</div>
|
|
87
|
+
<button mat-icon-button type="button" class="pfx-dialog-close" (click)="close()"
|
|
88
|
+
[attr.aria-label]="data.i18n?.cancel || 'Fechar'">
|
|
89
|
+
<mat-icon>close</mat-icon>
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
<mat-dialog-content class="pfx-filter-dialog-content" aria-labelledby="filterDialogTitle">
|
|
93
|
+
<mat-progress-bar *ngIf="data?.schemaLoading" mode="indeterminate"></mat-progress-bar>
|
|
94
|
+
<praxis-filter-form
|
|
95
|
+
*ngIf="data?.config"
|
|
96
|
+
[formId]="data.formId"
|
|
97
|
+
[resourcePath]="data.resourcePath"
|
|
98
|
+
[mode]="'edit'"
|
|
99
|
+
[config]="data.config"
|
|
100
|
+
(formReady)="onReady($event)"
|
|
101
|
+
(valueChange)="onChange($event)"
|
|
102
|
+
(validityChange)="onValidity($event)"
|
|
103
|
+
></praxis-filter-form>
|
|
104
|
+
<p *ngIf="!data?.config && !data?.schemaLoading" class="pfx-empty-state">{{ data.i18n?.noData || 'Nenhum dado' }}</p>
|
|
105
|
+
</mat-dialog-content>
|
|
106
|
+
<mat-dialog-actions align="end" class="pfx-dialog-actions">
|
|
107
|
+
<button mat-button type="button"
|
|
108
|
+
*ngIf="data?.allowSaveTags"
|
|
109
|
+
[disabled]="!canSave"
|
|
110
|
+
(click)="saveShortcut()">
|
|
111
|
+
{{ data.i18n?.saveAsShortcut || 'Salvar como atalho' }}
|
|
112
|
+
</button>
|
|
113
|
+
<button mat-stroked-button type="button" (click)="close()">{{ data.i18n?.cancel || 'Cancelar' }}</button>
|
|
114
|
+
<button mat-flat-button color="primary" (click)="apply()" [disabled]="!valid">
|
|
115
|
+
{{ data.i18n?.apply || 'Aplicar' }}
|
|
116
|
+
</button>
|
|
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"] }] });
|
|
119
|
+
}
|
|
120
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: FilterFormDialogHostComponent, decorators: [{
|
|
121
|
+
type: Component,
|
|
122
|
+
args: [{ selector: 'praxis-filter-form-dialog-host', standalone: true, imports: [CommonModule, MatDialogModule, MatButtonModule, MatProgressBarModule, MatIconModule, PraxisFilterForm], template: `
|
|
123
|
+
<div mat-dialog-title class="pfx-dialog-title" id="filterDialogTitle">
|
|
124
|
+
<div class="pfx-dialog-title-text">
|
|
125
|
+
<mat-icon>tune</mat-icon>
|
|
126
|
+
<span>{{ data.title || 'Filtro avançado' }}</span>
|
|
127
|
+
</div>
|
|
128
|
+
<button mat-icon-button type="button" class="pfx-dialog-close" (click)="close()"
|
|
129
|
+
[attr.aria-label]="data.i18n?.cancel || 'Fechar'">
|
|
130
|
+
<mat-icon>close</mat-icon>
|
|
131
|
+
</button>
|
|
132
|
+
</div>
|
|
133
|
+
<mat-dialog-content class="pfx-filter-dialog-content" aria-labelledby="filterDialogTitle">
|
|
134
|
+
<mat-progress-bar *ngIf="data?.schemaLoading" mode="indeterminate"></mat-progress-bar>
|
|
135
|
+
<praxis-filter-form
|
|
136
|
+
*ngIf="data?.config"
|
|
137
|
+
[formId]="data.formId"
|
|
138
|
+
[resourcePath]="data.resourcePath"
|
|
139
|
+
[mode]="'edit'"
|
|
140
|
+
[config]="data.config"
|
|
141
|
+
(formReady)="onReady($event)"
|
|
142
|
+
(valueChange)="onChange($event)"
|
|
143
|
+
(validityChange)="onValidity($event)"
|
|
144
|
+
></praxis-filter-form>
|
|
145
|
+
<p *ngIf="!data?.config && !data?.schemaLoading" class="pfx-empty-state">{{ data.i18n?.noData || 'Nenhum dado' }}</p>
|
|
146
|
+
</mat-dialog-content>
|
|
147
|
+
<mat-dialog-actions align="end" class="pfx-dialog-actions">
|
|
148
|
+
<button mat-button type="button"
|
|
149
|
+
*ngIf="data?.allowSaveTags"
|
|
150
|
+
[disabled]="!canSave"
|
|
151
|
+
(click)="saveShortcut()">
|
|
152
|
+
{{ data.i18n?.saveAsShortcut || 'Salvar como atalho' }}
|
|
153
|
+
</button>
|
|
154
|
+
<button mat-stroked-button type="button" (click)="close()">{{ data.i18n?.cancel || 'Cancelar' }}</button>
|
|
155
|
+
<button mat-flat-button color="primary" (click)="apply()" [disabled]="!valid">
|
|
156
|
+
{{ data.i18n?.apply || 'Aplicar' }}
|
|
157
|
+
</button>
|
|
158
|
+
</mat-dialog-actions>
|
|
159
|
+
`, 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"] }]
|
|
160
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
161
|
+
type: Inject,
|
|
162
|
+
args: [MAT_DIALOG_DATA]
|
|
163
|
+
}] }, { type: i1.MatDialogRef }] });
|
|
164
|
+
|
|
165
|
+
export { FilterFormDialogHostComponent };
|
|
166
|
+
//# sourceMappingURL=praxisui-table-filter-form-dialog-host.component-CLF05-__.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"praxisui-table-filter-form-dialog-host.component-CLF05-__.mjs","sources":["../../../projects/praxis-table/src/lib/components/praxis-filter/filter-form-dialog-host.component.ts"],"sourcesContent":["import { Component, Inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { MatIconModule } from '@angular/material/icon';\nimport { FormGroup } from '@angular/forms';\nimport { PraxisFilterForm } from '@praxisui/dynamic-form';\n\n@Component({\n selector: 'praxis-filter-form-dialog-host',\n standalone: true,\n imports: [CommonModule, MatDialogModule, MatButtonModule, MatProgressBarModule, MatIconModule, PraxisFilterForm],\n template: `\n <div mat-dialog-title class=\"pfx-dialog-title\" id=\"filterDialogTitle\">\n <div class=\"pfx-dialog-title-text\">\n <mat-icon>tune</mat-icon>\n <span>{{ data.title || 'Filtro avançado' }}</span>\n </div>\n <button mat-icon-button type=\"button\" class=\"pfx-dialog-close\" (click)=\"close()\"\n [attr.aria-label]=\"data.i18n?.cancel || 'Fechar'\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n <mat-dialog-content class=\"pfx-filter-dialog-content\" aria-labelledby=\"filterDialogTitle\">\n <mat-progress-bar *ngIf=\"data?.schemaLoading\" mode=\"indeterminate\"></mat-progress-bar>\n <praxis-filter-form\n *ngIf=\"data?.config\"\n [formId]=\"data.formId\"\n [resourcePath]=\"data.resourcePath\"\n [mode]=\"'edit'\"\n [config]=\"data.config\"\n (formReady)=\"onReady($event)\"\n (valueChange)=\"onChange($event)\"\n (validityChange)=\"onValidity($event)\"\n ></praxis-filter-form>\n <p *ngIf=\"!data?.config && !data?.schemaLoading\" class=\"pfx-empty-state\">{{ data.i18n?.noData || 'Nenhum dado' }}</p>\n </mat-dialog-content>\n <mat-dialog-actions align=\"end\" class=\"pfx-dialog-actions\">\n <button mat-button type=\"button\"\n *ngIf=\"data?.allowSaveTags\"\n [disabled]=\"!canSave\"\n (click)=\"saveShortcut()\">\n {{ data.i18n?.saveAsShortcut || 'Salvar como atalho' }}\n </button>\n <button mat-stroked-button type=\"button\" (click)=\"close()\">{{ data.i18n?.cancel || 'Cancelar' }}</button>\n <button mat-flat-button color=\"primary\" (click)=\"apply()\" [disabled]=\"!valid\">\n {{ data.i18n?.apply || 'Aplicar' }}\n </button>\n </mat-dialog-actions>\n `,\n styles: [`\n .pfx-dialog-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding-right: 8px;\n }\n .pfx-dialog-title-text {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n color: var(--md-sys-color-on-surface);\n }\n .pfx-dialog-close {\n margin-left: auto;\n }\n .pfx-filter-dialog-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding-top: 8px;\n }\n .pfx-empty-state {\n margin: 8px 0 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n .pfx-dialog-actions {\n padding: var(--pdx-dialog-actions-padding, 12px 24px 16px);\n border-top: 1px solid var(--md-sys-color-outline-variant);\n background: transparent;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n `],\n})\nexport class FilterFormDialogHostComponent {\n valid = true;\n private lastValue: any = {};\n private formGroup: FormGroup<Record<string, any>> | null = null;\n canSave = false;\n constructor(\n @Inject(MAT_DIALOG_DATA)\n public data: {\n formId: string;\n resourcePath: string;\n config: any;\n title?: string;\n schemaLoading?: boolean;\n initialDto?: Record<string, any>;\n allowSaveTags?: boolean;\n i18n?: { saveAsShortcut?: string; cancel?: string; apply?: string; noData?: string };\n onSaveShortcut?: (dto: Record<string, any>) => void;\n },\n private ref: MatDialogRef<FilterFormDialogHostComponent>,\n ) {}\n\n onReady(ev: { formGroup: any }): void {\n try {\n this.formGroup = (ev?.formGroup as FormGroup<Record<string, any>>) || null;\n const dto = this.data?.initialDto || {};\n if (ev?.formGroup && dto && Object.keys(dto).length) {\n ev.formGroup.patchValue(dto, { emitEvent: false });\n }\n this.lastValue = this.formGroup?.getRawValue?.() ?? this.lastValue;\n this.updateCanSave();\n } catch {}\n }\n onChange(ev: { formData: Record<string, any> }): void {\n this.lastValue = ev?.formData ?? {};\n this.updateCanSave();\n }\n onValidity(v: boolean): void {\n this.valid = v;\n this.updateCanSave();\n }\n apply(): void {\n const formData =\n this.formGroup?.getRawValue?.() ??\n this.lastValue ??\n this.data?.initialDto ??\n {};\n this.ref.close({ formData });\n }\n close(): void { this.ref.close(); }\n\n private clean(obj: Record<string, any> | undefined | null): Record<string, any> {\n const out: Record<string, any> = {};\n Object.keys(obj || {}).forEach((k) => {\n const v = (obj as any)[k];\n if (v !== '' && v !== null && v !== undefined) out[k] = v;\n });\n return out;\n }\n private buildDtoForSave(): Record<string, any> {\n const base = this.data?.initialDto || {};\n const merged = { ...base, ...(this.lastValue || {}) };\n return this.clean(merged);\n }\n private updateCanSave(): void {\n this.canSave = Object.keys(this.buildDtoForSave() || {}).length > 0;\n }\n saveShortcut(): void {\n try {\n const dto = this.buildDtoForSave();\n if (!Object.keys(dto).length) return;\n this.data?.onSaveShortcut?.(dto);\n } catch {}\n }\n}\n"],"names":["i2","i4"],"mappings":"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"}
|