@rozie-ui/data-table-lit 0.7.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozie-ui/data-table-lit",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
package/src/Column.ts CHANGED
@@ -54,6 +54,14 @@ export default class Column extends SignalWatcher(LitElement) {
54
54
  * The table-core aggregation for this column inside a group-header cell. Either a built-in name string — `'sum'` | `'min'` | `'max'` | `'extent'` | `'mean'` | `'median'` | `'unique'` | `'uniqueCount'` | `'count'` — or a custom function `(columnId, leafRows, childRows) => any` (defensively wrapped by the parent so a throw cannot crash grouping). Null → no aggregation (the group-header cell renders as a placeholder).
55
55
  */
56
56
  @property({ type: String, attribute: 'aggregation-fn' }) aggregationFn: string | (((...args: any[]) => any) | null) = null;
57
+ /**
58
+ * The table-core filter rule for this column. Either a built-in name string (e.g. `'arrIncludesSome'`, `'equalsString'`) or a custom function `(value, filterValue, row) => boolean`, where `row` is the original data object. Null → table-core's auto rule for the column's value type.
59
+ */
60
+ @property({ type: String, attribute: 'filter-fn' }) filterFn: string | (((...args: any[]) => any) | null) = null;
61
+ /**
62
+ * The table-core sort rule for this column. Either a built-in name string (e.g. `'alphanumeric'`, `'basic'`) or a custom ascending comparator `(a, b, rowA, rowB) => number` over the cell values, with the original data objects as `rowA` / `rowB`. Null → table-core's auto rule for the column's value type.
63
+ */
64
+ @property({ type: String, attribute: 'sorting-fn' }) sortingFn: string | (((...args: any[]) => any) | null) = null;
57
65
  /**
58
66
  * Whether this column's cells are editable (opt-in). Default `false` → the column is read-only and the display↔editor branch never mounts an editor. Bind `:editable="true"` (a bare attr only coerces on Vue+Lit).
59
67
  */
@@ -96,7 +104,9 @@ private get registry() { return this.__rozieCtxConsumer_data_table_columns.value
96
104
  }
97
105
 
98
106
  updated(changedProperties: Map<string, unknown>): void {
99
- if (this.__rozieFirstUpdateDone && (changedProperties.has('id') || changedProperties.has('field') || changedProperties.has('header') || changedProperties.has('sortable') || changedProperties.has('filterable') || changedProperties.has('pinned') || changedProperties.has('width') || changedProperties.has('expandable') || changedProperties.has('groupable') || changedProperties.has('aggregationFn') || changedProperties.has('editable') || changedProperties.has('editor') || changedProperties.has('editorOptions') || changedProperties.has('validate'))) { const __watchVal = (() => [this.id, this.field, this.header, this.sortable, this.filterable, this.pinned, this.width, this.expandable, this.groupable, this.aggregationFn, this.editable, this.editor, this.editorOptions, this.validate])(); (() => {
107
+ if (this.__rozieFirstUpdateDone && (changedProperties.has('id') || changedProperties.has('field') || changedProperties.has('header') || changedProperties.has('sortable') || changedProperties.has('filterable') || changedProperties.has('pinned') || changedProperties.has('width') || changedProperties.has('expandable') || changedProperties.has('groupable') || changedProperties.has('aggregationFn') || changedProperties.has('filterFn') || changedProperties.has('sortingFn') || changedProperties.has('editable') || changedProperties.has('editor') || changedProperties.has('editorOptions') || changedProperties.has('validate'))) { const __watchVal = (() => [this.id, this.field, this.header, this.sortable, this.filterable, this.pinned, this.width, this.expandable, this.groupable, this.aggregationFn, this.filterFn, this.sortingFn, this.editable, this.editor, this.editorOptions, this.validate])(); (() => {
108
+ this.latestFilterFn = this.filterFn;
109
+ this.latestSortingFn = this.sortingFn;
100
110
  if (this.reg) this.reg.registerColumn(this.colId(), this.buildSpec());
101
111
  })(); }
102
112
  this.__rozieFirstUpdateDone = true;
@@ -141,31 +151,57 @@ private get registry() { return this.__rozieCtxConsumer_data_table_columns.value
141
151
  // REQ-30) and the $onMount registration never double-register the column.
142
152
  registered = false;
143
153
 
154
+ // Stable trampolines for custom filterFn/sortingFn. An inline arrow is a new identity every
155
+ // consumer render; registering it directly would defeat the registerColumn equivalence guard
156
+ // (a function compares by source, but table-core would hold a stale closure). The spec holds
157
+ // ONE trampoline per column that always calls the latest function, refreshed in the re-register
158
+ // watch below BEFORE the equivalence check. A mutable top-level `let` (a ref on React).
159
+ // table-core reads `autoRemove` / `resolveFilterValue` OFF the filter function itself, so the
160
+ // filter trampoline carries stable forwarders that read the CURRENT latestFilterFn (absent on
161
+ // the consumer's fn => table-core's own defaults: never auto-remove, value passes through).
162
+ latestFilterFn: any = null;
163
+
164
+ latestSortingFn: any = null;
165
+
166
+ filterTrampoline: any = Object.assign((...args: any[]) => typeof this.latestFilterFn === 'function' ? this.latestFilterFn(...args) : true, {
167
+ autoRemove: (value: any, column: any) => typeof this.latestFilterFn === 'function' && typeof this.latestFilterFn.autoRemove === 'function' ? this.latestFilterFn.autoRemove(value, column) : false,
168
+ resolveFilterValue: (value: any) => typeof this.latestFilterFn === 'function' && typeof this.latestFilterFn.resolveFilterValue === 'function' ? this.latestFilterFn.resolveFilterValue(value) : value
169
+ });
170
+
171
+ sortingTrampoline = (...args: any[]) => typeof this.latestSortingFn === 'function' ? this.latestSortingFn(...args) : 0;
172
+
144
173
  // the column SPEC builder — shared by the $onMount register and the late-context
145
174
  // $onUpdate below. Carries METADATA ONLY (no cell/header render callbacks — D-A moved
146
175
  // per-cell rendering to the parent's #cell/#header scoped slot, dispatched by columnId).
147
176
  colId = () => this.id !== '' ? this.id : this.field;
148
177
 
149
- buildSpec = () => ({
150
- id: this.colId(),
151
- field: this.field !== '' ? this.field : this.colId(),
152
- header: this.header,
153
- sortable: this.sortable,
154
- filterable: this.filterable,
155
- pinned: this.pinned,
156
- width: this.width,
157
- // Expandable-rows reserved metadata (phase 50, D-04) — carried via the parent registry.
158
- expandable: this.expandable,
159
- // Grouping + aggregation metadata (phase 50, reqs 4-7, D-05) — carried via the parent
160
- // registry; the parent resolves aggregationFn onto the ColumnDef (defensive-wrapping a
161
- // custom fn) and filters groupableColumns by `groupable`.
162
- groupable: this.groupable,
163
- aggregationFn: this.aggregationFn,
164
- // Editable-cell config (Phase 51) — carried into ColumnDef.meta via the parent
165
- // registry (the existing per-column metadata path; NO parallel registry).
166
- editable: this.editable,
167
- editor: this.editor,
168
- editorOptions: this.editorOptions,
169
- validate: this.validate
170
- });
178
+ buildSpec = () => {
179
+ // Seed the trampolines' targets at first registration (Lit props may not exist earlier).
180
+ this.latestFilterFn = this.filterFn;
181
+ this.latestSortingFn = this.sortingFn;
182
+ return {
183
+ id: this.colId(),
184
+ field: this.field !== '' ? this.field : this.colId(),
185
+ header: this.header,
186
+ sortable: this.sortable,
187
+ filterable: this.filterable,
188
+ pinned: this.pinned,
189
+ width: this.width,
190
+ // Expandable-rows reserved metadata (phase 50, D-04) — carried via the parent registry.
191
+ expandable: this.expandable,
192
+ // Grouping + aggregation metadata (phase 50, reqs 4-7, D-05) — carried via the parent
193
+ // registry; the parent resolves aggregationFn onto the ColumnDef (defensive-wrapping a
194
+ // custom fn) and filters groupableColumns by `groupable`.
195
+ groupable: this.groupable,
196
+ aggregationFn: this.aggregationFn,
197
+ filterFn: typeof this.filterFn === 'function' ? this.filterTrampoline : this.filterFn,
198
+ sortingFn: typeof this.sortingFn === 'function' ? this.sortingTrampoline : this.sortingFn,
199
+ // Editable-cell config (Phase 51) — carried into ColumnDef.meta via the parent
200
+ // registry (the existing per-column metadata path; NO parallel registry).
201
+ editable: this.editable,
202
+ editor: this.editor,
203
+ editorOptions: this.editorOptions,
204
+ validate: this.validate
205
+ };
206
+ };
171
207
  }