@rozie-ui/data-table-solid 0.7.3 → 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-solid",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  ],
49
49
  "dependencies": {
50
50
  "@solid-primitives/keyed": "^1.5.3",
51
- "@rozie/runtime-solid": "^0.9.1"
51
+ "@rozie/runtime-solid": "^0.10.1"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "solid-js": "^1.8",
package/src/Column.tsx CHANGED
@@ -45,6 +45,14 @@ interface ColumnProps {
45
45
  * 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).
46
46
  */
47
47
  aggregationFn?: (string | ((...args: any[]) => any)) | null;
48
+ /**
49
+ * 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.
50
+ */
51
+ filterFn?: (string | ((...args: any[]) => any)) | null;
52
+ /**
53
+ * 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.
54
+ */
55
+ sortingFn?: (string | ((...args: any[]) => any)) | null;
48
56
  /**
49
57
  * 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).
50
58
  */
@@ -64,10 +72,15 @@ interface ColumnProps {
64
72
  }
65
73
 
66
74
  export default function Column(_props: ColumnProps): JSX.Element {
67
- const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, editable: false, editor: 'text', editorOptions: (() => [])() as any[], validate: null }, _props);
68
- const [local, attrs] = splitProps(_merged, ['id', 'field', 'header', 'sortable', 'filterable', 'pinned', 'width', 'expandable', 'groupable', 'aggregationFn', 'editable', 'editor', 'editorOptions', 'validate']);
75
+ const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, filterFn: null, sortingFn: null, editable: false, editor: 'text', editorOptions: (() => [])() as any[], validate: null }, _props);
76
+ const [local, attrs] = splitProps(_merged, ['id', 'field', 'header', 'sortable', 'filterable', 'pinned', 'width', 'expandable', 'groupable', 'aggregationFn', 'filterFn', 'sortingFn', 'editable', 'editor', 'editorOptions', 'validate']);
69
77
 
70
78
  const registry = useContext(rozieContext("data-table:columns"));
79
+ createEffect(on(() => (() => [local.id, local.field, local.header, local.sortable, local.filterable, local.pinned, local.width, local.expandable, local.groupable, local.aggregationFn, local.filterFn, local.sortingFn, local.editable, local.editor, local.editorOptions, local.validate])(), (v) => untrack(() => (() => {
80
+ latestFilterFn = local.filterFn;
81
+ latestSortingFn = local.sortingFn;
82
+ if (reg) reg.registerColumn(colId(), buildSpec());
83
+ })()), { defer: true }));
71
84
  onMount(() => {
72
85
  const _cleanup = (() => {
73
86
  // register this column's spec. On Lit the injected registry may still be undefined
@@ -91,9 +104,6 @@ export default function Column(_props: ColumnProps): JSX.Element {
91
104
  registered = true;
92
105
  reg.registerColumn(colId(), buildSpec());
93
106
  });
94
- createEffect(on(() => (() => [local.id, local.field, local.header, local.sortable, local.filterable, local.pinned, local.width, local.expandable, local.groupable, local.aggregationFn, local.editable, local.editor, local.editorOptions, local.validate])(), (v) => untrack(() => (() => {
95
- if (reg) reg.registerColumn(colId(), buildSpec());
96
- })()), { defer: true }));
97
107
 
98
108
  // $inject is typed `unknown` (Phase 36 D-4: no rich type synthesis yet), which the
99
109
  // STRICT BUNDLED-LEAF tsc rejects on `.registerColumn(...)` (TS2339). The .rozie-native
@@ -109,6 +119,24 @@ export default function Column(_props: ColumnProps): JSX.Element {
109
119
  // REQ-30) and the $onMount registration never double-register the column.
110
120
  let registered = false;
111
121
 
122
+ // Stable trampolines for custom filterFn/sortingFn. An inline arrow is a new identity every
123
+ // consumer render; registering it directly would defeat the registerColumn equivalence guard
124
+ // (a function compares by source, but table-core would hold a stale closure). The spec holds
125
+ // ONE trampoline per column that always calls the latest function, refreshed in the re-register
126
+ // watch below BEFORE the equivalence check. A mutable top-level `let` (a ref on React).
127
+ // table-core reads `autoRemove` / `resolveFilterValue` OFF the filter function itself, so the
128
+ // filter trampoline carries stable forwarders that read the CURRENT latestFilterFn (absent on
129
+ // the consumer's fn => table-core's own defaults: never auto-remove, value passes through).
130
+ let latestFilterFn: any = null;
131
+ let latestSortingFn: any = null;
132
+ const filterTrampoline: any = Object.assign((...args: any[]) => typeof latestFilterFn === 'function' ? latestFilterFn(...args) : true, {
133
+ autoRemove: (value: any, column: any) => typeof latestFilterFn === 'function' && typeof latestFilterFn.autoRemove === 'function' ? latestFilterFn.autoRemove(value, column) : false,
134
+ resolveFilterValue: (value: any) => typeof latestFilterFn === 'function' && typeof latestFilterFn.resolveFilterValue === 'function' ? latestFilterFn.resolveFilterValue(value) : value
135
+ });
136
+ function sortingTrampoline(...args: any[]) {
137
+ return typeof latestSortingFn === 'function' ? latestSortingFn(...args) : 0;
138
+ }
139
+
112
140
  // the column SPEC builder — shared by the $onMount register and the late-context
113
141
  // $onUpdate below. Carries METADATA ONLY (no cell/header render callbacks — D-A moved
114
142
  // per-cell rendering to the parent's #cell/#header scoped slot, dispatched by columnId).
@@ -116,6 +144,9 @@ export default function Column(_props: ColumnProps): JSX.Element {
116
144
  return local.id !== '' ? local.id : local.field;
117
145
  }
118
146
  function buildSpec() {
147
+ // Seed the trampolines' targets at first registration (Lit props may not exist earlier).
148
+ latestFilterFn = local.filterFn;
149
+ latestSortingFn = local.sortingFn;
119
150
  return {
120
151
  id: colId(),
121
152
  field: local.field !== '' ? local.field : colId(),
@@ -131,6 +162,8 @@ export default function Column(_props: ColumnProps): JSX.Element {
131
162
  // custom fn) and filters groupableColumns by `groupable`.
132
163
  groupable: local.groupable,
133
164
  aggregationFn: local.aggregationFn,
165
+ filterFn: typeof local.filterFn === 'function' ? filterTrampoline : local.filterFn,
166
+ sortingFn: typeof local.sortingFn === 'function' ? sortingTrampoline : local.sortingFn,
134
167
  // Editable-cell config (Phase 51) — carried into ColumnDef.meta via the parent
135
168
  // registry (the existing per-column metadata path; NO parallel registry).
136
169
  editable: local.editable,