@svadmin/ui 0.34.7 → 0.34.9

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.
@@ -10,6 +10,7 @@
10
10
  type ColumnVisibilityState,
11
11
  type ExpandedState,
12
12
  } from '@tanstack/svelte-table';
13
+ import type { Updater } from '@tanstack/table-core';
13
14
  import {
14
15
  column_getCanSort,
15
16
  column_getIsSorted,
@@ -28,7 +29,7 @@
28
29
  row_getIsExpanded,
29
30
  row_toggleExpanded,
30
31
  cell_getValue,
31
- } from '@tanstack/svelte-table/static-functions';
32
+ } from '@tanstack/table-core/static-functions';
32
33
 
33
34
  import { useList, useDelete, useDeleteMany, getResource, notify } from '@svadmin/core';
34
35
  import type { Pagination as PaginationState, Sort, Filter, FieldDefinition } from '@svadmin/core';
@@ -107,7 +108,7 @@
107
108
  const initPageSize = untrack(() => resource.pageSize ?? (isNaN(storedPageSize) ? 10 : storedPageSize));
108
109
  const initDefaultSort = untrack(() => resource.defaultSort);
109
110
 
110
- let pagination = $state<{ current: number; pageSize: number }>(untrack(() => externalPagination ?? {
111
+ let pagination = $state<PaginationState>(untrack(() => externalPagination ?? {
111
112
  current: urlState.page ?? 1,
112
113
  pageSize: urlState.pageSize ?? initPageSize,
113
114
  }));
@@ -173,6 +174,41 @@
173
174
  return result;
174
175
  });
175
176
 
177
+ function clonePlainValue(value: unknown): unknown {
178
+ if (Array.isArray(value)) return value.map(clonePlainValue);
179
+ if (value && typeof value === 'object') {
180
+ return Object.fromEntries(
181
+ Object.entries(value as Record<string, unknown>).map(([key, entry]) => [key, clonePlainValue(entry)])
182
+ );
183
+ }
184
+ return value;
185
+ }
186
+
187
+ function cloneFilter(filter: Filter): Filter {
188
+ if ('field' in filter) {
189
+ return {
190
+ field: filter.field,
191
+ operator: filter.operator,
192
+ value: clonePlainValue(filter.value),
193
+ };
194
+ }
195
+ return {
196
+ operator: filter.operator,
197
+ value: filter.value.map(cloneFilter),
198
+ };
199
+ }
200
+
201
+ const queryPagination = $derived<PaginationState>({
202
+ current: pagination.current,
203
+ pageSize: pagination.pageSize,
204
+ mode: pagination.mode,
205
+ });
206
+ const querySorters = $derived<Sort[]>(sorters.map(sorter => ({
207
+ field: sorter.field,
208
+ order: sorter.order,
209
+ })));
210
+ const queryFilters = $derived<Filter[]>(activeFilters.map(cloneFilter));
211
+
176
212
  $effect(() => {
177
213
  if (Object.values(filterValues).some(v => v.trim())) {
178
214
  pagination = { ...pagination, current: 1 };
@@ -182,9 +218,9 @@
182
218
  // ─── Data fetching ────────────────────────────────────────────
183
219
  const listResult = useList({
184
220
  get resource() { return resourceName; },
185
- get pagination() { return pagination; },
186
- get sorters() { return sorters; },
187
- get filters() { return activeFilters; },
221
+ get pagination() { return queryPagination; },
222
+ get sorters() { return querySorters; },
223
+ get filters() { return queryFilters; },
188
224
  });
189
225
  const query = listResult;
190
226
  const deleteResult = useDelete({ get resource() { return resourceName; } });
@@ -227,6 +263,68 @@
227
263
  let expanded = $state.raw<ExpandedState>({});
228
264
  let columnOrder = $state<string[]>([]);
229
265
 
266
+ function applyTableUpdater<T>(current: T, updater: Updater<T>): T {
267
+ return typeof updater === 'function' ? (updater as (old: T) => T)(current) : updater;
268
+ }
269
+
270
+ const tableSorting = $derived<SortingState>(sorting.map(sorter => ({
271
+ id: sorter.id,
272
+ desc: sorter.desc,
273
+ })));
274
+ const tableColumnVisibility = $derived<ColumnVisibilityState>({ ...columnVisibility });
275
+ const tableRowSelection = $derived<RowSelectionState>({ ...rowSelection });
276
+ const tableExpanded = $derived<ExpandedState>(expanded === true ? true : { ...expanded });
277
+ const tableColumnOrder = $derived<string[]>([...columnOrder]);
278
+
279
+ const sortingAtom = {
280
+ get: () => tableSorting,
281
+ set: (updater: Updater<SortingState>) => {
282
+ const nextSorting = applyTableUpdater(tableSorting, updater);
283
+ sorting = nextSorting;
284
+
285
+ const nextSorters: Sort[] = nextSorting.map((s) => ({
286
+ field: s.id,
287
+ order: s.desc ? 'desc' as const : 'asc' as const,
288
+ }));
289
+
290
+ if (JSON.stringify(nextSorters) !== JSON.stringify(sorters)) {
291
+ sorters = nextSorters;
292
+ }
293
+
294
+ if ((pagination.current ?? 1) !== 1) {
295
+ pagination = { ...pagination, current: 1 };
296
+ }
297
+ },
298
+ };
299
+
300
+ const columnVisibilityAtom = {
301
+ get: () => tableColumnVisibility,
302
+ set: (updater: Updater<ColumnVisibilityState>) => {
303
+ columnVisibility = applyTableUpdater(tableColumnVisibility, updater);
304
+ },
305
+ };
306
+
307
+ const rowSelectionAtom = {
308
+ get: () => tableRowSelection,
309
+ set: (updater: Updater<RowSelectionState>) => {
310
+ rowSelection = applyTableUpdater(tableRowSelection, updater);
311
+ },
312
+ };
313
+
314
+ const expandedAtom = {
315
+ get: () => tableExpanded,
316
+ set: (updater: Updater<ExpandedState>) => {
317
+ expanded = applyTableUpdater(tableExpanded, updater);
318
+ },
319
+ };
320
+
321
+ const columnOrderAtom = {
322
+ get: () => tableColumnOrder,
323
+ set: (updater: Updater<string[]>) => {
324
+ columnOrder = applyTableUpdater(tableColumnOrder, updater);
325
+ },
326
+ };
327
+
230
328
  // Persist column visibility to localStorage
231
329
  $effect(() => {
232
330
  const storageKey = `svadmin-columns-${resourceName}`;
@@ -237,25 +335,19 @@
237
335
  }
238
336
  });
239
337
 
240
- // Sync TanStack sorting → server sorters
338
+ // Sync external sorters ? local sorting state (controlled mode only)
241
339
  $effect(() => {
242
- const newSorters: Sort[] = sorting.map(s => ({
243
- field: s.id,
244
- order: s.desc ? 'desc' as const : 'asc' as const,
245
- }));
246
- if (JSON.stringify(newSorters) !== JSON.stringify(sorters)) {
247
- sorters = newSorters;
340
+ if (!externalSorters) return;
341
+ if (JSON.stringify(externalSorters) !== JSON.stringify(sorters)) {
342
+ sorters = externalSorters;
248
343
  }
249
- });
250
-
251
- // Sync external sorters → TanStack sorting state
252
- $effect(() => {
253
- const newSorting = sorters.map(s => ({ id: s.field, desc: s.order === 'desc' }));
254
- if (JSON.stringify(newSorting) !== JSON.stringify(sorting)) {
255
- sorting = newSorting;
344
+ const nextSorting = externalSorters.map(s => ({ id: s.field, desc: s.order === 'desc' }));
345
+ if (JSON.stringify(nextSorting) !== JSON.stringify(sorting)) {
346
+ sorting = nextSorting;
256
347
  }
257
348
  });
258
349
 
350
+
259
351
  // ─── Auto-generate columns from resource fields ──────────────
260
352
  const visibleFields = $derived(
261
353
  resource.fields.filter(f => f.showInList !== false)
@@ -303,27 +395,12 @@
303
395
  manualPagination: true,
304
396
  manualSorting: true,
305
397
  getRowId: (row: any) => String(row[primaryKey]),
306
- state: {
307
- get sorting() { return sorting; },
308
- get columnVisibility() { return columnVisibility; },
309
- get rowSelection() { return rowSelection; },
310
- get expanded() { return expanded; },
311
- get columnOrder() { return columnOrder; },
312
- },
313
- onSortingChange: (updater: any) => {
314
- sorting = typeof updater === 'function' ? updater(sorting) : updater;
315
- },
316
- onColumnVisibilityChange: (updater: any) => {
317
- columnVisibility = typeof updater === 'function' ? updater(columnVisibility) : updater;
318
- },
319
- onRowSelectionChange: (updater: any) => {
320
- rowSelection = typeof updater === 'function' ? updater(rowSelection) : updater;
321
- },
322
- onExpandedChange: (updater: any) => {
323
- expanded = typeof updater === 'function' ? updater(expanded) : updater;
324
- },
325
- onColumnOrderChange: (updater: any) => {
326
- columnOrder = typeof updater === 'function' ? updater(columnOrder) : updater;
398
+ atoms: {
399
+ sorting: sortingAtom,
400
+ columnVisibility: columnVisibilityAtom,
401
+ rowSelection: rowSelectionAtom,
402
+ expanded: expandedAtom,
403
+ columnOrder: columnOrderAtom,
327
404
  },
328
405
  get enableRowSelection() { return selectable && (canDelete || batchActions); },
329
406
  get enableExpanding() { return !!expandedRowRender; },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.34.7",
3
+ "version": "0.34.9",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -33,7 +33,7 @@
33
33
  "./components/*": "./dist/components/*"
34
34
  },
35
35
  "peerDependencies": {
36
- "@svadmin/core": "^0.25.4",
36
+ "@svadmin/core": "^0.25.5",
37
37
  "@tanstack/svelte-query": "^6.1.29",
38
38
  "highlight.js": "^11.11.1",
39
39
  "isomorphic-dompurify": "^3.7.1",