@praxisui/table 8.0.0-beta.2 → 8.0.0-beta.20

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.
Files changed (37) hide show
  1. package/README.md +81 -8
  2. package/docs/DSL-Extensions-Guide.md +23 -0
  3. package/docs/adr/2026-03-dynamic-filter-cross-lib-coupling.md +107 -0
  4. package/docs/adr/2026-03-filter-drawer-adapter-light-entrypoint.md +105 -0
  5. package/docs/adr/2026-03-table-editor-idfield-decision.md +85 -0
  6. package/docs/column-resize-reorder-implementation-plan.md +338 -0
  7. package/docs/column-resize-reorder-review-prompt.md +34 -0
  8. package/docs/dynamic-filter-architecture-overview.md +207 -0
  9. package/docs/dynamic-filter-backend-contract-cheatsheet.md +167 -0
  10. package/docs/dynamic-filter-editor-settings-guide.md +229 -0
  11. package/docs/dynamic-filter-host-integration-guide.md +217 -0
  12. package/docs/dynamic-filter-payload-contract.md +331 -0
  13. package/docs/dynamic-filter-range-filters-guide.md +289 -0
  14. package/docs/dynamic-filter-troubleshooting-guide.md +220 -0
  15. package/docs/dynamic-inline-filter-catalog.md +147 -0
  16. package/docs/e2e-column-drag-playwright.md +62 -0
  17. package/docs/expandable-rows-enterprise-big-leagues-plan.md +1080 -0
  18. package/docs/json-logic-operators-and-helpers.md +57 -0
  19. package/docs/local-data-mode-precedence.md +12 -0
  20. package/docs/local-data-pre-implementation-baseline.md +22 -0
  21. package/docs/local-data-preimplementation-go-no-go.md +39 -0
  22. package/docs/local-data-support-implementation-plan.md +524 -0
  23. package/docs/local-data-support-pr-package.md +66 -0
  24. package/docs/localization-persistence-merge.md +22 -0
  25. package/docs/performance-hardening-v2-implementation-plan.md +479 -0
  26. package/docs/playground-scenario-curation-plan.md +482 -0
  27. package/docs/playground-scenario-second-opinion-prompt.md +121 -0
  28. package/docs/playground-scenario-second-opinion-review.md +234 -0
  29. package/docs/release-notes-p1-hardening.md +76 -0
  30. package/docs/table-authoring-document-completeness-checklist.md +120 -0
  31. package/docs/table-editor-capability-review-prompt.md +349 -0
  32. package/docs/visual-rules-editor-transition.md +29 -0
  33. package/fesm2022/{praxisui-table-table-ai.adapter-DxjDaQqy.mjs → praxisui-table-table-ai.adapter-fS74fZ7o.mjs} +14 -5
  34. package/fesm2022/praxisui-table.mjs +3650 -324
  35. package/index.d.ts +120 -51
  36. package/package.json +15 -9
  37. package/src/lib/praxis-table.json-api.md +1315 -0
@@ -0,0 +1,338 @@
1
+ # Column Resize + Reorder Implementation Plan
2
+
3
+ ## Objective
4
+
5
+ Allow users to resize and move columns in `praxis-table` without conflicting with sorting, while keeping behavior predictable, accessible, and persistent.
6
+
7
+ ## Guiding Principles
8
+
9
+ - `sort` belongs to a dedicated sortable label/container, not to the entire `th`
10
+ - `reorder` belongs to a dedicated drag handle
11
+ - `resize` belongs to a dedicated handle on the right edge of the header cell
12
+ - runtime mutations should reuse the existing persistence pipeline through a shared column-mutation coordinator
13
+ - width persistence should normalize to `px`
14
+ - keep the existing config model and avoid introducing parallel API knobs
15
+
16
+ ## Current Architecture Notes
17
+
18
+ Today the table header combines multiple behaviors on the same `th`:
19
+
20
+ - `mat-sort-header`
21
+ - `cdkDrag`
22
+ - reorder affordance/indicator
23
+ - sticky start/end support
24
+ - horizontal scroll modes already used in production
25
+
26
+ Relevant files:
27
+
28
+ - `/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.html`
29
+ - `/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.scss`
30
+ - `/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/src/lib/praxis-table.ts`
31
+
32
+ This is the main reason resize must not be introduced as another gesture on the same interaction zone. It also means sticky and horizontal scroll behavior must be treated as first-order implementation concerns, not deferred work.
33
+
34
+ Additional constraints from the current implementation:
35
+
36
+ - reorder keyboard parity is attached to the `th`, not to a standalone handle
37
+ - reorder aria-label and tooltip are currently attached to the `th`
38
+ - persistence currently saves the full `config` snapshot for each runtime mutation
39
+ - width styling may already come from `column.width`, `headerStyle`, or `style`
40
+
41
+ Current reorder affordance note:
42
+
43
+ - the drag handle is rendered before the header text in draggable headers so the grip appears first visually without changing the current `cdkDrag` root on the `th`
44
+ - this is a visual/layout choice only in the current implementation slice; the handle is still non-interactive and the full draggable header remains the active drag target
45
+
46
+ The plan must explicitly unwind those couplings instead of treating them as incidental details.
47
+
48
+ ## Implementation Phases
49
+
50
+ ### Phase 1: Split Header Interaction Zones
51
+
52
+ Refactor the header so each interaction has an explicit zone:
53
+
54
+ - sortable label container: sort
55
+ - drag icon/handle: reorder
56
+ - right-edge handle: resize
57
+
58
+ Changes in `praxis-table.html`:
59
+
60
+ - keep `.praxis-header-label-text`
61
+ - keep the drag handle rendered before the label text while the current reorder UX still relies on the full `th` as drag root
62
+ - add a sortable wrapper such as `.praxis-header-sort-trigger`
63
+ - keep `.praxis-column-drag-handle`
64
+ - add `.praxis-column-resize-handle`
65
+ - keep `cdkDrag` on the `th`
66
+ - add `cdkDragHandle` to the reorder handle so the existing drag root, preview, placeholder sizing, and index semantics stay intact
67
+ - move reorder `aria-label`, tooltip, and keyboard listeners from the `th` to the reorder handle
68
+ - ensure the resize handle and reorder handle both stop `click`, `pointerdown`, and keyboard bubbling into the sort trigger
69
+
70
+ Changes in `praxis-table.scss`:
71
+
72
+ - make the sortable label container the only area that shows sort cursor/hover treatment
73
+ - keep drag handle aligned next to the label text
74
+ - add resize handle positioned at the far right edge
75
+ - use `cursor: grab` for reorder
76
+ - use `cursor: col-resize` for resize
77
+ - use a hit area around `8px` to `12px` for resize
78
+ - remove `pointer-events: none` from the drag handle and make it a real interactive control
79
+
80
+ Sorting strategy:
81
+
82
+ - do not keep the full `th` as the primary sort interaction target
83
+ - move the sort trigger to the label/container inside the header cell
84
+ - preserve keyboard sort semantics on that dedicated sort target
85
+ - treat suppression of `click` on reorder/resize handles as a hard requirement, not a best-effort safeguard
86
+
87
+ ### Phase 2: Add Runtime Resize State
88
+
89
+ Add state to `praxis-table.ts`:
90
+
91
+ - `activeResizeColumnField: string | null`
92
+ - `resizeStartX: number`
93
+ - `resizeStartWidthPx: number`
94
+ - `isResizingColumn: boolean`
95
+ - `resizePointerId: number | null`
96
+
97
+ Add internal defaults:
98
+
99
+ - derive defaults from the existing config model first
100
+ - use `behavior.resizing.minColumnWidth` and `behavior.resizing.maxColumnWidth` when present
101
+ - fall back to sane internal defaults only when config is absent
102
+
103
+ Add handlers:
104
+
105
+ - `onColumnResizePointerDown(event, column)`
106
+ - `onColumnResizePointerMove(event)`
107
+ - `onColumnResizePointerUp(event)`
108
+ - `onColumnResizePointerCancel(event)`
109
+ - `cancelColumnResize()`
110
+
111
+ Add runtime width state:
112
+
113
+ - keep transient width changes separate from persisted `config.columns[]` until commit
114
+ - resolve whether a column is resizable from both `behavior.resizing.enabled` and `column.resizable !== false`
115
+ - exclude non-data columns (`_select`, `_expander`, `_actions`) from resize by design
116
+
117
+ ### Phase 3: Prevent Interaction Conflicts
118
+
119
+ On resize start:
120
+
121
+ - call `preventDefault()`
122
+ - call `stopPropagation()`
123
+ - capture the active pointer
124
+ - temporarily block sort and reorder while `isResizingColumn === true`
125
+
126
+ Behavioral rule:
127
+
128
+ - dragging only starts from the reorder handle
129
+ - sorting only starts from the dedicated sortable label/container
130
+ - resizing only starts from the right-edge resize handle
131
+
132
+ During resize:
133
+
134
+ - apply a class such as `pfx-column-resizing`
135
+ - temporarily disable `cdkDropList`
136
+ - ignore sort triggers from the header
137
+ - ensure sticky columns and resize handles preserve usable hit areas and z-index ordering
138
+ - preserve horizontal scroll behavior while pointer capture is active
139
+ - support abort paths: `Escape`, `pointercancel`, lost capture, and pointerup outside the viewport
140
+
141
+ ### Phase 4: Normalize and Apply Widths
142
+
143
+ Add width utilities to `praxis-table.ts`:
144
+
145
+ - `resolveColumnWidthPx(column, headerEl): number`
146
+ - `clampColumnWidthPx(width: number): number`
147
+ - `formatColumnWidthPx(width: number): string`
148
+
149
+ Width rules:
150
+
151
+ - if `column.width` is already in `px`, use it directly
152
+ - if width is `%`, `auto`, or empty, measure the current header width at first resize and convert to `px`
153
+ - persist resized widths as `px`
154
+ - explicitly define precedence when `headerStyle` or `style` also carry width declarations
155
+ - document that first user resize converts responsive width intent into fixed runtime width intent
156
+ - validate width behavior under sticky start/end columns and horizontal scroll mode
157
+
158
+ Keep `getColumnWidthStyle(column)` as the main rendering output, now backed by updated runtime values.
159
+
160
+ ### Phase 5: Persistence
161
+
162
+ Reuse the existing runtime mutation persistence flow in `praxis-table.ts`, but do not let resize and reorder persist independently against unrelated config snapshots.
163
+
164
+ Add a shared column runtime mutation coordinator:
165
+
166
+ - monotonic `columnMutationOperationId`
167
+ - mutation kind: `column-reorder` | `column-resize`
168
+ - per-operation snapshot merge before `saveConfig`
169
+ - stale-callback rejection shared by reorder and resize
170
+ - serialized commit semantics when reorder and resize overlap in time
171
+
172
+ Refactor `persistTableConfigAfterRuntimeMutation(...)` usage behind a narrower helper such as:
173
+
174
+ - `persistColumnRuntimeMutation(trigger, mutateConfig, options)`
175
+
176
+ Add a new trigger:
177
+
178
+ - `column-resize`
179
+
180
+ Persist width by updating the matching entry in `config.columns[]`:
181
+
182
+ - locate by `field`
183
+ - set `width: "${px}px"`
184
+
185
+ Resize persistence policy:
186
+
187
+ - persist on commit only, not continuously during pointer move
188
+ - commit point is `pointerup` for pointer interactions
189
+ - keyboard resize is not part of the first delivery slice
190
+ - when keyboard resize is added later, commit semantics must be explicit (`Enter`, `blur`, or stepwise save), not implied
191
+ - define stale-callback protection for resize and reorder through the same shared coordinator
192
+ - do not add undo in the first slice unless a concrete UX requirement appears, but keep the runtime contract compatible with future undo support
193
+
194
+ Emit a resize event after persistence:
195
+
196
+ - `columnResize`
197
+
198
+ Suggested payload:
199
+
200
+ - `tableId`
201
+ - `field`
202
+ - `header`
203
+ - `previousWidth`
204
+ - `currentWidth`
205
+ - `persisted`
206
+
207
+ ### Phase 6: Public Configuration API
208
+
209
+ Align strictly with the existing config model. Do not introduce a second naming scheme.
210
+
211
+ Use the existing behavior flags in the core model:
212
+
213
+ - `behavior.dragging.columns`
214
+ - `behavior.resizing.enabled`
215
+ - `behavior.resizing.minColumnWidth`
216
+ - `behavior.resizing.maxColumnWidth`
217
+ - `behavior.resizing.persistWidths`
218
+ - `ColumnDefinition.resizable`
219
+
220
+ Optional future additions should only happen if the current model proves insufficient.
221
+
222
+ Recommended rollout:
223
+
224
+ - keep `dragging.columns = true`
225
+ - ship resize behind `behavior.resizing.enabled`
226
+ - honor `behavior.resizing.persistWidths` when deciding whether to save width runtime mutations
227
+ - keep `column.resizable !== false` as the per-column opt-out
228
+ - do not add new public API until the current model proves insufficient under real usage
229
+
230
+ ### Phase 7: Accessibility
231
+
232
+ Resize handle:
233
+
234
+ - `role="separator"`
235
+ - `aria-orientation="vertical"`
236
+ - `aria-label="Resize column X"`
237
+ - `tabindex="0"`
238
+ - expose current/min/max width semantics explicitly when implemented
239
+ - announce committed width changes through the existing live-region pattern or an equivalent status channel
240
+
241
+ Keyboard behavior for resize handle:
242
+
243
+ - defer keyboard resize to a follow-up slice after pointer behavior and persistence are stable
244
+ - when implemented, define `ArrowLeft`, `ArrowRight`, `Shift + Arrow`, `Home`, `End`, and `Escape`
245
+ - define exactly when the resize is committed and when it is cancelled
246
+
247
+ Reorder handle:
248
+
249
+ - make the reorder handle a real focusable control
250
+ - move existing Alt/Ctrl+Arrow reorder behavior onto the handle
251
+ - move existing reorder aria-label and tooltip onto the handle
252
+ - preserve visible focus styling and screen-reader parity after removing those affordances from the `th`
253
+
254
+ ### Phase 8: Testing
255
+
256
+ Add focused tests for interaction conflicts:
257
+
258
+ - resize does not trigger reorder
259
+ - resize does not trigger sort
260
+ - resize handle click does not bubble into sort click
261
+ - reorder does not alter width
262
+ - clicking the sortable label still sorts
263
+ - reorder still works through the dedicated `cdkDragHandle`
264
+ - keyboard reorder still works after moving intent to the handle
265
+ - sort keyboard interaction still works after sort leaves the `th`
266
+
267
+ Add persistence tests:
268
+
269
+ - width updates `config.columns`
270
+ - width persists through the shared column mutation coordinator
271
+ - width survives rerender/reload flow
272
+ - width persistence respects `behavior.resizing.persistWidths`
273
+ - resize persists only on commit, not on every pointer move
274
+ - concurrent resize/reorder persistence does not leave stale width/order state
275
+ - out-of-order async persistence callbacks do not overwrite the latest width/order snapshot
276
+
277
+ Add limit tests:
278
+
279
+ - `minWidthPx` is respected
280
+ - `maxWidthPx` is respected
281
+
282
+ Add pointer/cancel coverage:
283
+
284
+ - resize aborts cleanly on `Escape`
285
+ - resize aborts cleanly on `pointercancel`
286
+ - resize completes correctly when pointerup happens outside the immediate handle area
287
+
288
+ Add visual/e2e coverage later:
289
+
290
+ - resize with horizontal scroll enabled
291
+ - resize under `scroll-auto`
292
+ - resize under `scroll-wrap`
293
+ - reorder still works after resize
294
+ - sticky start column resize
295
+ - sticky end column resize
296
+ - sticky columns do not produce overlap/z-index regression while resizing
297
+ - adjacent non-data columns (`_select`, `_expander`, `_actions`) do not regress
298
+ - responsive scroll modes remain aligned between header and body
299
+
300
+ ## Recommended Delivery Sequence
301
+
302
+ 1. Refactor header structure so sort is isolated to a dedicated sortable label/container
303
+ 2. Move reorder affordance, keyboard handling, aria-label, and tooltip to a real drag handle
304
+ 3. Add resize handle with runtime-only pointer support and explicit cancel paths
305
+ 4. Validate sticky and horizontal-scroll behavior in the same slice
306
+ 5. Introduce shared column runtime mutation coordination for reorder + resize persistence
307
+ 6. Persist column width on commit only
308
+ 7. Add focused conflict and persistence tests
309
+ 8. Add keyboard resize and final resize a11y semantics in a follow-up slice
310
+ 9. Enable only through config/feature flag
311
+
312
+ ## Definition of Done
313
+
314
+ - users can reorder only from the drag handle
315
+ - users can resize only from the right-edge handle
316
+ - clicking the sortable label still sorts
317
+ - reorder, resize, and sort do not interfere with each other
318
+ - width and order persist correctly
319
+ - stale async persistence callbacks cannot revert the latest width/order mutation
320
+ - existing horizontal scroll and sticky behavior remain intact after first resize
321
+
322
+ ## Risk Notes
323
+
324
+ - gesture ambiguity is not solved by event suppression alone; sort must be structurally isolated from reorder/resize handles
325
+ - supporting `%` and `auto` widths requires careful first-resize normalization
326
+ - first resize may intentionally convert responsive width behavior into fixed pixel width behavior
327
+ - sticky start/end columns and horizontal scroll are immediate implementation concerns
328
+ - resize and reorder mutations need a shared commit/stale-callback policy, not parallel ad hoc logic
329
+
330
+ ## Suggested Follow-Up
331
+
332
+ After review, break this plan into small implementation tasks by file:
333
+
334
+ - `praxis-table.html`
335
+ - `praxis-table.scss`
336
+ - `praxis-table.ts`
337
+ - relevant core config model files
338
+ - dedicated unit/integration specs
@@ -0,0 +1,34 @@
1
+ # Review Prompt For Another Agent
2
+
3
+ Review the implementation plan in:
4
+
5
+ - `/mnt/d/Developer/praxis-plataform/praxis-ui-angular/projects/praxis-table/docs/column-resize-reorder-implementation-plan.md`
6
+
7
+ Context:
8
+
9
+ - The goal is to support both column resizing and column reordering in `praxis-table`.
10
+ - The current table header already combines `mat-sort-header`, `cdkDrag`, and reorder UI.
11
+ - We want a robust solution where `sort`, `reorder`, and `resize` do not conflict.
12
+ - The plan assumes:
13
+ - sorting stays on the header text/content area
14
+ - reordering moves to a dedicated drag handle
15
+ - resizing uses a dedicated right-edge handle
16
+ - width persistence reuses the existing runtime mutation persistence flow
17
+
18
+ What to review:
19
+
20
+ 1. Validate whether the interaction model is robust and aligned with mature grid implementations.
21
+ 2. Identify technical risks in the proposed architecture, especially around Angular Material, CDK drag-drop, and pointer events.
22
+ 3. Check whether the persistence strategy is coherent with the current `praxis-table` runtime mutation model.
23
+ 4. Evaluate whether the accessibility plan is sufficient or missing critical keyboard/screen-reader behaviors.
24
+ 5. Point out any missing test scenarios, especially conflict cases between resize, reorder, sort, sticky columns, and horizontal scroll.
25
+ 6. Flag any assumptions that are too optimistic or likely to create regressions.
26
+ 7. Suggest simplifications if the plan is over-engineered for the current codebase.
27
+
28
+ Expected output:
29
+
30
+ - Findings first, ordered by severity
31
+ - Then open questions or assumptions
32
+ - Then concrete recommendations
33
+
34
+ Please be critical. Focus on bugs, regressions, architectural risk, and implementation traps rather than rewriting the plan stylistically.
@@ -0,0 +1,207 @@
1
+ ---
2
+ title: "Dynamic Filter Architecture Overview"
3
+ slug: "dynamic-filter-architecture-overview"
4
+ description: "Visao arquitetural do filtro dinamico no ecossistema Praxis, cobrindo runtime Angular, campos inline, payload enviado e normalizacao no metadata-starter."
5
+ doc_type: "concept"
6
+ document_kind: "host-guide"
7
+ component: "table"
8
+ category: "architecture"
9
+ audience:
10
+ - "host"
11
+ - "frontend"
12
+ - "backend"
13
+ - "architect"
14
+ level: "advanced"
15
+ status: "active"
16
+ owner: "praxis-ui"
17
+ tags:
18
+ - "table"
19
+ - "dynamic-filter"
20
+ - "dynamic-fields"
21
+ - "metadata-starter"
22
+ - "range"
23
+ order: 26
24
+ icon: "account_tree"
25
+ toc: true
26
+ sidebar: true
27
+ search_boost: 1.15
28
+ reading_time: 16
29
+ estimated_setup_time: 35
30
+ version: "1.0"
31
+ related_docs:
32
+ - "table-overview"
33
+ - "dynamic-filter-payload-contract"
34
+ - "dynamic-filter-range-filters-guide"
35
+ - "dynamic-fields-inline-components-guide"
36
+ keywords:
37
+ - "praxis-filter"
38
+ - "GenericFilterDTO"
39
+ - "RangePayloadNormalizer"
40
+ - "filter settings"
41
+ source_of_truth:
42
+ - "projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.ts"
43
+ - "projects/praxis-table/src/lib/services/filter-config.service.ts"
44
+ - "projects/praxis-table/src/lib/filter-settings/filter-settings.component.ts"
45
+ - "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/web/FilterRequestBodyAdvice.java"
46
+ - "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/range/RangePayloadNormalizer.java"
47
+ - "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/specification/GenericSpecificationsBuilder.java"
48
+ last_updated: "2026-03-07"
49
+ ---
50
+
51
+ # Dynamic Filter Architecture Overview
52
+
53
+ ## Objetivo
54
+
55
+ Explicar o filtro dinâmico como uma feature transversal do ecossistema Praxis, não como um componente isolado, deixando claro como metadata, UI inline, editor/configuração, payload HTTP e interpretação backend trabalham juntos.
56
+
57
+ ## Pré-requisitos
58
+
59
+ - Conhecimento básico de `@praxisui/table`, `@praxisui/dynamic-fields` e contratos de `FilterDTO`.
60
+ - Host Angular já integrado com `praxis-filter` e endpoints resource-oriented de filtro do backend.
61
+ - Familiaridade com metadados `controlType`, `@Filterable` e `@UISchema`.
62
+
63
+ ## Quando usar
64
+
65
+ Use este documento quando precisar:
66
+
67
+ - entender o fluxo completo do filtro da tabela;
68
+ - planejar documentação funcional para hosts e squads backend;
69
+ - decidir onde documentar variantes inline, settings editor e contratos de range;
70
+ - investigar por que um payload válido na UI vira filtro inválido no backend.
71
+
72
+ ## Visão geral da feature
73
+
74
+ No Praxis, o filtro dinâmico é uma pipeline de seis etapas:
75
+
76
+ 1. O backend publica um `FilterDTO` anotado com `@Filterable` e `@UISchema`.
77
+ 2. O host ou a tabela obtém schema/metadata para montar os campos do filtro.
78
+ 3. O `PraxisFilter` decide o layout visível, a variante inline e o modo avançado.
79
+ 4. O usuário interage com os campos, e o runtime emite `change`, `submit`, `clear`, `tagsChange` e eventos auxiliares.
80
+ 5. O host envia o payload para endpoints como `POST /filter`, `POST /filter/cursor`, `POST /locate` ou `POST /options/filter`.
81
+ 6. O `praxis-metadata-starter` normaliza ranges e constrói as predicates JPA a partir do `GenericFilterDTO`.
82
+
83
+ ## Mapa de responsabilidades
84
+
85
+ ### 1. `praxis-table`: runtime de orquestração
86
+
87
+ `PraxisFilter` é o núcleo do runtime na UI. Ele concentra:
88
+
89
+ - inputs de configuração e layout, como `alwaysVisibleFields`, `selectedFieldIds`, `allowSaveTags`, `changeDebounceMs` e variantes `useInline*Variant`;
90
+ - outputs que materializam o contrato com o host, principalmente `submit` e `change`;
91
+ - persistência local do estado do DTO e das preferências de exibição com chaves `filter-dto:<key>` e `filter-config:<key>`;
92
+ - heurísticas que convertem `controlType` genérico para inline canônico quando a feature pede experiência compacta.
93
+
94
+ Esse acoplamento existe em `projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.ts` e `projects/praxis-table/src/lib/services/filter-config.service.ts`.
95
+
96
+ ### 2. `praxis-dynamic-fields`: superfície dos controles
97
+
98
+ Os componentes `filter-*-inline` pertencem ao catálogo de `dynamic-fields`. Eles não definem sozinhos a jornada do filtro; eles implementam o comportamento visual e semântico de cada campo compacto:
99
+
100
+ - selects inline;
101
+ - async/searchable/entity lookup;
102
+ - intervalos numéricos, monetários, data e hora;
103
+ - controles especializados como rating, radius, pipeline status e score priority.
104
+
105
+ O contrato corporativo desses campos está em `projects/praxis-dynamic-fields/docs/dynamic-fields-inline-components-guide.md` (slug publicado: `dynamic-fields-inline-components-guide`).
106
+
107
+ ### 3. `filter-settings`: governança editorial e operacional
108
+
109
+ `filter-settings.component.ts` é a ponte entre metadata, seleção de campos e overrides de UX. É onde o ecossistema define:
110
+
111
+ - quais campos ficam sempre visíveis;
112
+ - que `controlType` efetivo será usado na toolbar compacta;
113
+ - se a fonte da metadata vem do `filter-dto` ou de `columns`;
114
+ - quais conversões são aceitas para `rangeSlider`, `priceRange`, `dateRange`, `timeRange` e afins.
115
+
116
+ Em termos de documentação, isso significa que o editor do filtro precisa ser tratado como parte da feature, não como “extra”.
117
+
118
+ ### 4. `praxis-metadata-starter`: normalização e interpretação
119
+
120
+ No backend, o payload recebido pelo controller não vai direto para a specification.
121
+
122
+ Antes disso:
123
+
124
+ - `FilterRequestBodyAdvice` intercepta qualquer request body compatível com `GenericFilterDTO`;
125
+ - `RangePayloadNormalizer` converte objetos e aliases em formato canônico de lista;
126
+ - `GenericSpecificationsBuilder` transforma o valor normalizado em predicates como `BETWEEN`, `BETWEEN_EXCLUSIVE`, `NOT_BETWEEN` e `OUTSIDE_RANGE`.
127
+
128
+ Isso é o motivo de a documentação do filtro precisar citar explicitamente o `praxis-metadata-starter`.
129
+
130
+ ## Fluxo ponta a ponta
131
+
132
+ ### Etapa 1. Schema e metadata
133
+
134
+ O backend descreve o filtro com:
135
+
136
+ - `@Filterable` para a operação semântica;
137
+ - `@UISchema` para `label`, `controlType`, `endpoint`, `numericFormat`, ordem e outras pistas visuais.
138
+
139
+ ### Etapa 2. Resolução da UI
140
+
141
+ O `PraxisFilter` e o catálogo `dynamic-fields` decidem:
142
+
143
+ - se o campo vai para a barra compacta ou para a grade;
144
+ - se a variante será inline, dedicada ou avançada;
145
+ - se haverá clear button corporativo, tooltip contextual e presets rápidos.
146
+
147
+ ### Etapa 3. Estado local
148
+
149
+ O runtime persiste:
150
+
151
+ - o DTO de filtro em `filter-dto:<key>`;
152
+ - a configuração de layout do filtro em `filter-config:<key>`.
153
+
154
+ Isso permite restaurar seleção de campos, preferências visuais e estado operacional do filtro.
155
+
156
+ ### Etapa 4. Emissão de payload
157
+
158
+ No modo avançado, `onAdvancedChange(event)` trabalha sobre `event.formData`. Os eventos `change` e `submit` são o contrato que o host normalmente observa para disparar integração com a API.
159
+
160
+ Há uma nuance importante já coberta em teste: objeto de range vazio deve ser limpo antes de poluir o payload emitido.
161
+
162
+ ### Etapa 5. Request HTTP
163
+
164
+ O host envia o DTO para endpoints como:
165
+
166
+ - `POST /filter`
167
+ - `POST /filter/cursor`
168
+ - `POST /locate`
169
+ - `POST /options/filter`
170
+
171
+ Todos eles fazem parte da superfície resource-oriented de filtro do metadata-starter.
172
+
173
+ ### Etapa 6. Normalização backend
174
+
175
+ O backend aceita mais de um shape para intervalos, mas canonicaliza tudo para a lista esperada pelo `FilterDTO` tipado.
176
+
177
+ Exemplos:
178
+
179
+ - `{ minPrice: 10, maxPrice: 20 }` vira `[10, 20]`;
180
+ - `{ startDate: "2026-03-01", endDate: "2026-03-31" }` vira `["2026-03-01", "2026-03-31"]`;
181
+ - `[null, 100]` continua representando “somente teto superior”.
182
+
183
+ ## Regras arquiteturais que a doc precisa preservar
184
+
185
+ 1. O filtro é metadata-driven, mas não schema-only.
186
+ Runtime Angular, overrides do host e normalização backend influenciam o comportamento final.
187
+
188
+ 2. O payload não é só responsabilidade do frontend.
189
+ O backend canonicaliza ranges aceitos pela plataforma e rejeita payloads inválidos.
190
+
191
+ 3. O editor/configuração faz parte do contrato da feature.
192
+ Sempre documente `alwaysVisibleFields`, metadata overrides e variantes inline quando o componente depender deles.
193
+
194
+ 4. A documentação deve separar duas perspectivas:
195
+ - experiência de uso do filtro no contexto da tabela;
196
+ - contrato técnico dos campos inline e do payload.
197
+
198
+ ## Escopo da trilha documental recomendada
199
+
200
+ Para documentação extensa e didática, a feature deve ser desdobrada em pelo menos quatro documentos:
201
+
202
+ - arquitetura geral;
203
+ - contrato de payload;
204
+ - ranges e normalização;
205
+ - catálogo de campos inline e settings editor.
206
+
207
+ Os três primeiros já passam a existir nesta trilha. O quarto permanece ancorado no guia canônico de inline em `dynamic-fields`.