@praxisui/table 8.0.0-beta.21 → 8.0.0-beta.22
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.
|
@@ -118,6 +118,55 @@ Essas chaves são as mais importantes na prática:
|
|
|
118
118
|
- `allowSaveTags`: habilita tags persistidas/visíveis;
|
|
119
119
|
- `changeDebounceMs`: regula a cadência de emissão de `change` e `submit`.
|
|
120
120
|
|
|
121
|
+
#### Padronizar `materialDesign` dos campos de filtro
|
|
122
|
+
|
|
123
|
+
O `praxis-filter` normaliza a metadata efetiva de filtros com uma politica
|
|
124
|
+
visual consistente para toolbar compacta e formulario avancado. Quando o host
|
|
125
|
+
fornece metadata propria ou overrides em `alwaysVisibleFieldMetadataOverrides`,
|
|
126
|
+
ele deve preservar essa politica para campos com icones, prefixos, sufixos,
|
|
127
|
+
datepicker toggle, simbolo de moeda, seletor de cor ou clear button.
|
|
128
|
+
|
|
129
|
+
Configuracao recomendada:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const filterFieldMaterialDesign = {
|
|
133
|
+
floatLabel: 'always',
|
|
134
|
+
subscriptSizing: 'dynamic',
|
|
135
|
+
} as const;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Exemplo de override:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
advancedFilters: {
|
|
142
|
+
settings: {
|
|
143
|
+
alwaysVisibleFieldMetadataOverrides: {
|
|
144
|
+
cpf: {
|
|
145
|
+
prefixIcon: 'fingerprint',
|
|
146
|
+
materialDesign: filterFieldMaterialDesign,
|
|
147
|
+
},
|
|
148
|
+
dataNascimento: {
|
|
149
|
+
materialDesign: filterFieldMaterialDesign,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Nao trate isso como ajuste cosmetico local. O Angular Material recomenda
|
|
157
|
+
`floatLabel="always"` para campos `fill`/`outline` com prefixos/sufixos porque o
|
|
158
|
+
label em repouso nao compartilha o mesmo alinhamento do valor do input. A regra
|
|
159
|
+
de plataforma e aplicar essa politica na metadata efetiva de filtro antes de
|
|
160
|
+
recorrer a CSS; o runtime ja faz essa normalizacao para a barra compacta e para
|
|
161
|
+
o formulario avancado.
|
|
162
|
+
|
|
163
|
+
Campos compostos de faixa (`priceRange`, `dateRange`, `dateTimeRange` e
|
|
164
|
+
`timeRange`) devem ocupar uma linha completa no formulario avancado. Eles
|
|
165
|
+
materializam mais de um controle interno e, quando competem lado a lado com um
|
|
166
|
+
campo simples, perdem alinhamento vertical e tornam o hint ambíguo. O
|
|
167
|
+
`praxis-filter` aplica essa classe de layout automaticamente ao montar o
|
|
168
|
+
`FormConfig` avancado.
|
|
169
|
+
|
|
121
170
|
### 3. Definir política do painel avançado
|
|
122
171
|
|
|
123
172
|
Os principais knobs são:
|
|
@@ -103,6 +103,13 @@ O `PraxisFilter` converte `controlType` genérico em variante inline por padrão
|
|
|
103
103
|
|
|
104
104
|
Para monetário, `priceRange` já aponta para a experiência compacta específica do filtro.
|
|
105
105
|
|
|
106
|
+
### Formulario avancado
|
|
107
|
+
|
|
108
|
+
No formulario avancado, ranges compostos devem ocupar a linha completa. A regra
|
|
109
|
+
evita comparar verticalmente um controle com duas entradas internas contra um
|
|
110
|
+
campo simples, preserva leitura de label/hint e mantem espaco suficiente para
|
|
111
|
+
`Min`/`Max`, icones e sufixos.
|
|
112
|
+
|
|
106
113
|
### Filter settings
|
|
107
114
|
|
|
108
115
|
`filter-settings.component.ts` participa da história porque ele controla:
|
|
@@ -118,6 +118,43 @@ O shape enviado não respeita o contrato ou foi interpretado de maneira diferent
|
|
|
118
118
|
|
|
119
119
|
Compare o payload com o guia de ranges antes de tentar “corrigir no controller”.
|
|
120
120
|
|
|
121
|
+
## Sintoma: label conflita com icone, prefixo ou sufixo
|
|
122
|
+
|
|
123
|
+
### Causa provável
|
|
124
|
+
|
|
125
|
+
Campos Material `fill`/`outline` com `prefixIcon`, `suffixIcon`, `clearButton`,
|
|
126
|
+
datepicker toggle, simbolo de moeda, seletor de cor ou outros
|
|
127
|
+
`matPrefix`/`matSuffix` podem exibir o label em repouso sobreposto ou desalinhado.
|
|
128
|
+
|
|
129
|
+
Esse comportamento vem da propria geometria do `mat-form-field`: em `fill` e
|
|
130
|
+
`outline`, o label em repouso e o valor do input nao usam o mesmo alinhamento. O
|
|
131
|
+
Angular Material recomenda `floatLabel="always"` nesses casos.
|
|
132
|
+
|
|
133
|
+
### Onde olhar
|
|
134
|
+
|
|
135
|
+
- metadata efetiva do campo no filtro avancado;
|
|
136
|
+
- `alwaysVisibleFieldMetadataOverrides`;
|
|
137
|
+
- politica global aplicada pelo `praxis-filter` para materializar campos de filtro;
|
|
138
|
+
- `metadata.materialDesign.floatLabel`;
|
|
139
|
+
- `metadata.materialDesign.subscriptSizing`.
|
|
140
|
+
|
|
141
|
+
### Ação
|
|
142
|
+
|
|
143
|
+
Configure a metadata do campo ou a politica global do filtro:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"materialDesign": {
|
|
148
|
+
"floatLabel": "always",
|
|
149
|
+
"subscriptSizing": "dynamic"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Nao corrija deslocando label, prefixo, sufixo ou notch por CSS local. Esse tipo
|
|
155
|
+
de patch acopla o host a detalhes internos do Angular Material e tende a quebrar
|
|
156
|
+
em upgrades.
|
|
157
|
+
|
|
121
158
|
## Sintoma: campo inline não aparece na barra compacta
|
|
122
159
|
|
|
123
160
|
### Causa provável
|
|
@@ -5679,7 +5679,10 @@ class DataFormattingService {
|
|
|
5679
5679
|
if (value instanceof Date)
|
|
5680
5680
|
return value;
|
|
5681
5681
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
5682
|
-
let date =
|
|
5682
|
+
let date = this.parseDateOnlyString(value);
|
|
5683
|
+
if (!date) {
|
|
5684
|
+
date = new Date(this.normalizeNumericDateInput(value));
|
|
5685
|
+
}
|
|
5683
5686
|
if (typeof value === 'string' && isNaN(date.getTime())) {
|
|
5684
5687
|
const parts = value.split(',');
|
|
5685
5688
|
if (parts.length === 3) {
|
|
@@ -5722,6 +5725,22 @@ class DataFormattingService {
|
|
|
5722
5725
|
return value;
|
|
5723
5726
|
}
|
|
5724
5727
|
}
|
|
5728
|
+
parseDateOnlyString(value) {
|
|
5729
|
+
if (typeof value !== 'string') {
|
|
5730
|
+
return null;
|
|
5731
|
+
}
|
|
5732
|
+
const match = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(value.trim());
|
|
5733
|
+
if (!match) {
|
|
5734
|
+
return null;
|
|
5735
|
+
}
|
|
5736
|
+
const year = Number(match[1]);
|
|
5737
|
+
const month = Number(match[2]) - 1;
|
|
5738
|
+
const day = Number(match[3]);
|
|
5739
|
+
const date = new Date(year, month, day);
|
|
5740
|
+
return date.getFullYear() === year && date.getMonth() === month && date.getDate() === day
|
|
5741
|
+
? date
|
|
5742
|
+
: null;
|
|
5743
|
+
}
|
|
5725
5744
|
normalizeNumericDateInput(value) {
|
|
5726
5745
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
5727
5746
|
return value;
|
|
@@ -34636,7 +34655,7 @@ class PraxisFilter {
|
|
|
34636
34655
|
const shortcuts = shouldUseInlineDateRange
|
|
34637
34656
|
? (existingShortcuts.length ? existingShortcuts : [...INLINE_DATE_RANGE_CORPORATE_SHORTCUTS])
|
|
34638
34657
|
: meta.shortcuts;
|
|
34639
|
-
return {
|
|
34658
|
+
return this.withFilterMaterialDesignPolicy({
|
|
34640
34659
|
...meta,
|
|
34641
34660
|
controlType: inlineControlType,
|
|
34642
34661
|
granularity: inlineControlType === INLINE_PERIOD_RANGE_CONTROL_TYPE
|
|
@@ -34651,15 +34670,10 @@ class PraxisFilter {
|
|
|
34651
34670
|
shortcuts,
|
|
34652
34671
|
updateOn: 'change',
|
|
34653
34672
|
density: 'compact',
|
|
34654
|
-
|
|
34655
|
-
...(meta.materialDesign || {}),
|
|
34656
|
-
floatLabel: 'always',
|
|
34657
|
-
subscriptSizing: 'dynamic',
|
|
34658
|
-
},
|
|
34659
|
-
};
|
|
34673
|
+
});
|
|
34660
34674
|
};
|
|
34661
34675
|
const alwaysWithDensity = initialAlways
|
|
34662
|
-
.map((meta) => this.mergeAlwaysVisibleMetadata(withToolbarMaterialDesign(meta), alwaysVisibleOverrides[meta.name]));
|
|
34676
|
+
.map((meta) => this.withFilterMaterialDesignPolicy(this.mergeAlwaysVisibleMetadata(withToolbarMaterialDesign(meta), alwaysVisibleOverrides[meta.name])));
|
|
34663
34677
|
const selectedWithDensity = initialSelected.map(withToolbarMaterialDesign);
|
|
34664
34678
|
// Separar toggles (booleans simples) para a faixa de ações, se habilitado
|
|
34665
34679
|
const isToggle = (m) => {
|
|
@@ -34744,16 +34758,9 @@ class PraxisFilter {
|
|
|
34744
34758
|
.filter((m) => !this.alwaysVisibleFields?.includes(m.name))
|
|
34745
34759
|
.filter((m) => !this.selectedFieldIds?.includes(m.name))
|
|
34746
34760
|
.map((m) => ({ ...m, updateOn: 'change' }));
|
|
34747
|
-
//
|
|
34748
|
-
|
|
34749
|
-
|
|
34750
|
-
const ct = m.controlType;
|
|
34751
|
-
if (ct === 'date' || ct === 'dateRange' || ct === 'dateTime' || ct === 'dateTimeRange') {
|
|
34752
|
-
return { ...m, touchUi: true };
|
|
34753
|
-
}
|
|
34754
|
-
return m;
|
|
34755
|
-
});
|
|
34756
|
-
}
|
|
34761
|
+
// Material's compact datepicker popup is unreliable inside the advanced dialog/drawer overlay.
|
|
34762
|
+
// Use touch UI by default there, while preserving explicit metadata overrides.
|
|
34763
|
+
advancedMetas = advancedMetas.map((m) => this.withAdvancedDatePickerTouchPolicy(m));
|
|
34757
34764
|
const hasClearOverride = this.advancedClearButtonsEnabled !== undefined;
|
|
34758
34765
|
const clearButtonsEnabled = this.advancedClearButtonsEnabled !== false;
|
|
34759
34766
|
if (advancedMetas.length) {
|
|
@@ -34775,6 +34782,7 @@ class PraxisFilter {
|
|
|
34775
34782
|
return m;
|
|
34776
34783
|
});
|
|
34777
34784
|
}
|
|
34785
|
+
advancedMetas = advancedMetas.map((m) => this.withFilterMaterialDesignPolicy(m));
|
|
34778
34786
|
this.logFilterWarn('[PFILTER] applySchemaMetas: advancedMetas', {
|
|
34779
34787
|
count: advancedMetas.length,
|
|
34780
34788
|
names: advancedMetas.map((m) => m.name),
|
|
@@ -34787,7 +34795,7 @@ class PraxisFilter {
|
|
|
34787
34795
|
id: 'advanced',
|
|
34788
34796
|
rows: [
|
|
34789
34797
|
{
|
|
34790
|
-
columns: advancedMetas.map((m) => (
|
|
34798
|
+
columns: advancedMetas.map((m) => this.buildAdvancedFilterColumn(m)),
|
|
34791
34799
|
},
|
|
34792
34800
|
],
|
|
34793
34801
|
},
|
|
@@ -34820,6 +34828,57 @@ class PraxisFilter {
|
|
|
34820
34828
|
catch { }
|
|
34821
34829
|
});
|
|
34822
34830
|
}
|
|
34831
|
+
withFilterMaterialDesignPolicy(meta) {
|
|
34832
|
+
return {
|
|
34833
|
+
...meta,
|
|
34834
|
+
materialDesign: {
|
|
34835
|
+
...(meta.materialDesign || {}),
|
|
34836
|
+
floatLabel: 'always',
|
|
34837
|
+
subscriptSizing: 'dynamic',
|
|
34838
|
+
},
|
|
34839
|
+
};
|
|
34840
|
+
}
|
|
34841
|
+
withAdvancedDatePickerTouchPolicy(meta) {
|
|
34842
|
+
if (!this.isAdvancedDatePickerControl(meta)) {
|
|
34843
|
+
return meta;
|
|
34844
|
+
}
|
|
34845
|
+
if (Object.prototype.hasOwnProperty.call(meta, 'touchUi')) {
|
|
34846
|
+
return meta;
|
|
34847
|
+
}
|
|
34848
|
+
if (this.advancedOpenMode !== 'modal' && this.advancedOpenMode !== 'drawer') {
|
|
34849
|
+
return meta;
|
|
34850
|
+
}
|
|
34851
|
+
return { ...meta, touchUi: true };
|
|
34852
|
+
}
|
|
34853
|
+
isAdvancedDatePickerControl(meta) {
|
|
34854
|
+
const token = normalizeControlTypeToken(meta?.controlType || '');
|
|
34855
|
+
return (token === 'date' ||
|
|
34856
|
+
token === 'daterange' ||
|
|
34857
|
+
token === 'datetime' ||
|
|
34858
|
+
token === 'datetimerange' ||
|
|
34859
|
+
token === 'materialdatepicker' ||
|
|
34860
|
+
token === 'materialdaterange');
|
|
34861
|
+
}
|
|
34862
|
+
buildAdvancedFilterColumn(meta) {
|
|
34863
|
+
const column = {
|
|
34864
|
+
id: `advanced-${meta.name}`,
|
|
34865
|
+
fields: [meta.name],
|
|
34866
|
+
};
|
|
34867
|
+
if (this.isCompositeRangeFilterField(meta)) {
|
|
34868
|
+
column.className = 'filter-column--full filter-column--range';
|
|
34869
|
+
}
|
|
34870
|
+
return column;
|
|
34871
|
+
}
|
|
34872
|
+
isCompositeRangeFilterField(meta) {
|
|
34873
|
+
const token = normalizeControlTypeToken(meta?.controlType || '');
|
|
34874
|
+
return (token === 'pricerange' ||
|
|
34875
|
+
token === 'daterange' ||
|
|
34876
|
+
token === 'datetimerange' ||
|
|
34877
|
+
token === 'timerange' ||
|
|
34878
|
+
token === 'materialpricerange' ||
|
|
34879
|
+
token === 'materialdaterange' ||
|
|
34880
|
+
token === 'materialtimerange');
|
|
34881
|
+
}
|
|
34823
34882
|
pickMetasByOrder(metas, orderedNames) {
|
|
34824
34883
|
if (!orderedNames?.length)
|
|
34825
34884
|
return [];
|
package/index.d.ts
CHANGED
|
@@ -217,6 +217,7 @@ declare class DataFormattingService {
|
|
|
217
217
|
*/
|
|
218
218
|
formatValue(value: any, columnType: ColumnDataType$1, formatString: string, options?: DataFormattingOptions): any;
|
|
219
219
|
private coerceValueToType;
|
|
220
|
+
private parseDateOnlyString;
|
|
220
221
|
private normalizeNumericDateInput;
|
|
221
222
|
private formatDate;
|
|
222
223
|
private formatNumber;
|
|
@@ -647,6 +648,11 @@ declare class PraxisFilter implements OnInit, OnChanges, AfterViewInit, OnDestro
|
|
|
647
648
|
retrySchemaLoad(): void;
|
|
648
649
|
private applySchemaMetas;
|
|
649
650
|
private applySchemaMetasNow;
|
|
651
|
+
private withFilterMaterialDesignPolicy;
|
|
652
|
+
private withAdvancedDatePickerTouchPolicy;
|
|
653
|
+
private isAdvancedDatePickerControl;
|
|
654
|
+
private buildAdvancedFilterColumn;
|
|
655
|
+
private isCompositeRangeFilterField;
|
|
650
656
|
private pickMetasByOrder;
|
|
651
657
|
private hasRemoteOptionSource;
|
|
652
658
|
private withInferredFilterControlType;
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/table",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.22",
|
|
4
4
|
"description": "Advanced data table for Angular (Praxis UI) with editing, filtering, sorting, virtualization, and settings panel integration.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.0.0",
|
|
7
7
|
"@angular/core": "^20.0.0",
|
|
8
|
-
"@praxisui/ai": "^8.0.0-beta.
|
|
9
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
10
|
-
"@praxisui/dynamic-fields": "^8.0.0-beta.
|
|
11
|
-
"@praxisui/dynamic-form": "^8.0.0-beta.
|
|
12
|
-
"@praxisui/metadata-editor": "^8.0.0-beta.
|
|
13
|
-
"@praxisui/rich-content": "^8.0.0-beta.
|
|
14
|
-
"@praxisui/settings-panel": "^8.0.0-beta.
|
|
15
|
-
"@praxisui/table-rule-builder": "^8.0.0-beta.
|
|
8
|
+
"@praxisui/ai": "^8.0.0-beta.22",
|
|
9
|
+
"@praxisui/core": "^8.0.0-beta.22",
|
|
10
|
+
"@praxisui/dynamic-fields": "^8.0.0-beta.22",
|
|
11
|
+
"@praxisui/dynamic-form": "^8.0.0-beta.22",
|
|
12
|
+
"@praxisui/metadata-editor": "^8.0.0-beta.22",
|
|
13
|
+
"@praxisui/rich-content": "^8.0.0-beta.22",
|
|
14
|
+
"@praxisui/settings-panel": "^8.0.0-beta.22",
|
|
15
|
+
"@praxisui/table-rule-builder": "^8.0.0-beta.22",
|
|
16
16
|
"@angular/cdk": "^20.0.0",
|
|
17
17
|
"@angular/forms": "^20.0.0",
|
|
18
18
|
"@angular/material": "^20.0.0",
|
|
19
19
|
"@angular/router": "^20.0.0",
|
|
20
|
-
"@praxisui/dialog": "^8.0.0-beta.
|
|
20
|
+
"@praxisui/dialog": "^8.0.0-beta.22",
|
|
21
21
|
"rxjs": "~7.8.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|