runeforge 0.0.12 → 0.0.13

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.
@@ -7,6 +7,7 @@
7
7
  import Update from './views/Update.svelte';
8
8
  import { AUTO_EXCLUDED } from './utils/constants.js';
9
9
  import { resolveOptions, resolveFormatter, inferType } from './utils/resolution.js';
10
+ import { isFilterable } from '../table/utils.js';
10
11
  import type { AttributeMetadata } from '../../types/attribute.js';
11
12
  import type {
12
13
  ActionConfiguration,
@@ -14,6 +15,13 @@
14
15
  CustomAction,
15
16
  FieldDefinition,
16
17
  } from '../../types/crud.js';
18
+ import type {
19
+ FilterSnapshot,
20
+ PaginatedEnvelope,
21
+ ServerPagination,
22
+ SortDirection,
23
+ TableQuery,
24
+ } from '../../types/table.js';
17
25
 
18
26
  let {
19
27
  data = undefined as Record<string, unknown> | undefined,
@@ -52,8 +60,31 @@
52
60
  form?: { error?: string } | null;
53
61
  } = $props();
54
62
 
63
+ function isEnvelope(value: unknown): value is PaginatedEnvelope<T> {
64
+ return (
65
+ !!value &&
66
+ typeof value === 'object' &&
67
+ !Array.isArray(value) &&
68
+ Array.isArray((value as Record<string, unknown>).results) &&
69
+ typeof (value as Record<string, unknown>).count === 'number'
70
+ );
71
+ }
72
+
73
+ const rawSlice = $derived(data && dataKey ? data[dataKey] : undefined);
74
+ const envelope = $derived(isEnvelope(rawSlice) ? rawSlice : undefined);
55
75
  const entityData = $derived<T[]>(
56
- data && dataKey ? (data[dataKey] as T[] ?? []) : []
76
+ envelope ? envelope.results : (Array.isArray(rawSlice) ? (rawSlice as T[]) : [])
77
+ );
78
+
79
+ const serverPagination = $derived<ServerPagination | undefined>(
80
+ envelope
81
+ ? {
82
+ page: envelope.page,
83
+ pageSize: envelope.pageSize,
84
+ totalPages: Math.max(1, Math.ceil(envelope.count / envelope.pageSize)),
85
+ total: envelope.count,
86
+ }
87
+ : undefined
57
88
  );
58
89
 
59
90
  const viewParam = $derived(page.url.searchParams.get('view'));
@@ -102,6 +133,72 @@
102
133
  : [])
103
134
  );
104
135
 
136
+ // Server-pagination mode only: GenericCRUD owns `page`/`ordering`/per-column
137
+ // filter query params the same way it already owns `view`/`id`, so pagination
138
+ // state survives reloads/direct links and `+page.svelte` never has to change.
139
+ const orderingParam = $derived(page.url.searchParams.get('ordering'));
140
+ const initialSort = $derived(
141
+ orderingParam
142
+ ? {
143
+ column: orderingParam.startsWith('-') ? orderingParam.slice(1) : orderingParam,
144
+ direction: (orderingParam.startsWith('-') ? 'desc' : 'asc') as SortDirection,
145
+ }
146
+ : undefined
147
+ );
148
+
149
+ const initialFilters = $derived.by<Partial<FilterSnapshot> | undefined>(() => {
150
+ if (!envelope) return undefined;
151
+ const text: Record<string, string> = {};
152
+ const values: Record<string, string[]> = {};
153
+ const dateRanges: Record<string, { from: string; to: string }> = {};
154
+ for (const col of resolvedColumns) {
155
+ if (!isFilterable(col)) continue;
156
+ const raw = page.url.searchParams.get(col.attribute);
157
+ if (raw != null) {
158
+ if (col.type === 'boolean') values[col.attribute] = raw.split(',').filter(Boolean);
159
+ else text[col.attribute] = raw;
160
+ }
161
+ const from = page.url.searchParams.get(`${col.attribute}_from`);
162
+ const to = page.url.searchParams.get(`${col.attribute}_to`);
163
+ if (from || to) dateRanges[col.attribute] = { from: from ?? '', to: to ?? '' };
164
+ }
165
+ return { text, values, dateRanges };
166
+ });
167
+
168
+ async function handlePaginationChange(query: TableQuery) {
169
+ // Local, synchronous query-string builder consumed immediately by goto();
170
+ // not rendered/reactive state, so plain URLSearchParams is correct here.
171
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
172
+ const params = new URLSearchParams(page.url.searchParams);
173
+ params.delete('view');
174
+ params.delete('id');
175
+
176
+ if (query.page > 1) params.set('page', String(query.page));
177
+ else params.delete('page');
178
+
179
+ if (query.ordering) params.set('ordering', query.ordering);
180
+ else params.delete('ordering');
181
+
182
+ for (const col of resolvedColumns) {
183
+ params.delete(col.attribute);
184
+ params.delete(`${col.attribute}_from`);
185
+ params.delete(`${col.attribute}_to`);
186
+ }
187
+ for (const [k, v] of Object.entries(query.filters.text)) {
188
+ if (v) params.set(k, v);
189
+ }
190
+ for (const [k, vs] of Object.entries(query.filters.values)) {
191
+ if (vs.length) params.set(k, vs.join(','));
192
+ }
193
+ for (const [k, r] of Object.entries(query.filters.dateRanges)) {
194
+ if (r.from) params.set(`${k}_from`, r.from);
195
+ if (r.to) params.set(`${k}_to`, r.to);
196
+ }
197
+
198
+ const qs = params.toString();
199
+ await goto(qs ? `?${qs}` : '?', { keepFocus: true, noScroll: true });
200
+ }
201
+
105
202
  const resolvedFields: FieldDefinition<T>[] = $derived(
106
203
  fields ?? (meta
107
204
  ? (Object.entries(meta) as [string, AttributeMetadata][])
@@ -221,6 +318,10 @@
221
318
  {deletion}
222
319
  {actions}
223
320
  columns={resolvedColumns}
321
+ pagination={serverPagination}
322
+ {initialSort}
323
+ {initialFilters}
324
+ onPaginationChange={handlePaginationChange}
224
325
  onCreate={navCreate}
225
326
  onEdit={navEdit}
226
327
  onView={navRead}
@@ -13,6 +13,7 @@
13
13
  CustomAction,
14
14
  RowAction
15
15
  } from '../../../types/crud.js';
16
+ import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../../types/table.js';
16
17
  import { getStrings } from '../../../i18n/context.js';
17
18
 
18
19
  const strings = getStrings();
@@ -30,6 +31,10 @@
30
31
  deletion = {} as ActionConfiguration<T>,
31
32
  actions = [] as CustomAction<T>[],
32
33
  columns = [] as ColumnDefinition<T>[],
34
+ pagination = undefined as ServerPagination | undefined,
35
+ initialSort = undefined as { column: string; direction: SortDirection } | undefined,
36
+ initialFilters = undefined as Partial<FilterSnapshot> | undefined,
37
+ onPaginationChange = undefined as ((query: TableQuery) => void) | undefined,
33
38
  onCreate,
34
39
  onEdit,
35
40
  onView,
@@ -48,6 +53,10 @@
48
53
  deletion?: ActionConfiguration<T>;
49
54
  actions?: CustomAction<T>[];
50
55
  columns?: ColumnDefinition<T>[];
56
+ pagination?: ServerPagination;
57
+ initialSort?: { column: string; direction: SortDirection };
58
+ initialFilters?: Partial<FilterSnapshot>;
59
+ onPaginationChange?: (query: TableQuery) => void;
51
60
  onCreate?: () => void;
52
61
  onEdit?: (item: T) => void;
53
62
  onView?: (item: T) => void;
@@ -68,6 +77,17 @@
68
77
  let selected = new SvelteSet<number>();
69
78
  let pendingDeletion = $state<T[] | null>(null);
70
79
 
80
+ // `selected` holds indices into `data`; if `data` is swapped for a different
81
+ // slice (page/sort/filter change in server mode) stale indices could point
82
+ // at unrelated rows, so clear on any data reference change.
83
+ let lastData: T[] | undefined;
84
+ $effect(() => {
85
+ if (data !== lastData) {
86
+ selected.clear();
87
+ lastData = data;
88
+ }
89
+ });
90
+
71
91
  async function runEndpointAction(endpoint: string, items: T[]) {
72
92
  await Promise.all(items.map((item) => {
73
93
  const fd = new FormData();
@@ -185,6 +205,10 @@
185
205
  selectable={allowDelete}
186
206
  {selected}
187
207
  rowActions={showRowActions ? actionsCell : undefined}
208
+ {pagination}
209
+ {initialSort}
210
+ {initialFilters}
211
+ {onPaginationChange}
188
212
  />
189
213
  </div>
190
214
  </div>
@@ -1,4 +1,5 @@
1
1
  import type { ActionConfiguration, ColumnDefinition, CustomAction } from '../../../types/crud.js';
2
+ import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../../types/table.js';
2
3
  declare function $$render<T extends object = Record<string, unknown>>(): {
3
4
  props: {
4
5
  data?: T[];
@@ -13,6 +14,13 @@ declare function $$render<T extends object = Record<string, unknown>>(): {
13
14
  deletion?: ActionConfiguration<T>;
14
15
  actions?: CustomAction<T>[];
15
16
  columns?: ColumnDefinition<T>[];
17
+ pagination?: ServerPagination;
18
+ initialSort?: {
19
+ column: string;
20
+ direction: SortDirection;
21
+ };
22
+ initialFilters?: Partial<FilterSnapshot>;
23
+ onPaginationChange?: (query: TableQuery) => void;
16
24
  onCreate?: () => void;
17
25
  onEdit?: (item: T) => void;
18
26
  onView?: (item: T) => void;
@@ -48,9 +48,11 @@
48
48
  return () => calendarEl?.removeEventListener('change', handler);
49
49
  });
50
50
 
51
+ let debounceTimer: ReturnType<typeof setTimeout>;
51
52
  function setText(value: string) {
52
53
  filter.setText(column.attribute, value);
53
- onchange?.();
54
+ clearTimeout(debounceTimer);
55
+ debounceTimer = setTimeout(() => onchange?.(), 300);
54
56
  }
55
57
  function toggle(value: string) {
56
58
  filter.toggleValue(column.attribute, value);
@@ -3,9 +3,15 @@
3
3
  import TableBody from './TableBody.svelte';
4
4
  import Paginator from './Paginator.svelte';
5
5
  import TableHeader from './TableHeader.svelte';
6
- import { SortState, FilterState } from './state.svelte.js';
7
- import { distinctEntries } from './utils.js';
8
- import type { IndexedRow } from '../../types/table.js';
6
+ import { SortState, FilterState, snapshotFilter } from './state.svelte.js';
7
+ import { distinctEntries, isFilterable } from './utils.js';
8
+ import type {
9
+ FilterSnapshot,
10
+ IndexedRow,
11
+ ServerPagination,
12
+ SortDirection,
13
+ TableQuery,
14
+ } from '../../types/table.js';
9
15
  import type { Snippet } from 'svelte';
10
16
  import type { ColumnDefinition } from '../../types/crud.js';
11
17
  import { getStrings } from '../../i18n/context.js';
@@ -20,6 +26,10 @@
20
26
  selected = $bindable(new SvelteSet<number>()),
21
27
  rowActions = undefined as Snippet<[T]> | undefined,
22
28
  actionsLabel = strings.actions,
29
+ pagination = undefined as ServerPagination | undefined,
30
+ initialSort = undefined as { column: string; direction: SortDirection } | undefined,
31
+ initialFilters = undefined as Partial<FilterSnapshot> | undefined,
32
+ onPaginationChange = undefined as ((query: TableQuery) => void) | undefined,
23
33
  }: {
24
34
  data?: T[];
25
35
  columns?: ColumnDefinition<T>[];
@@ -28,31 +38,88 @@
28
38
  selected?: SvelteSet<number>;
29
39
  rowActions?: Snippet<[T]>;
30
40
  actionsLabel?: string;
41
+ /** When provided, the table trusts `data` is already the requested page and
42
+ * defers pagination/sort/filter to `onPaginationChange` instead of computing
43
+ * them locally. Omit for the original fully-client-side behavior. */
44
+ pagination?: ServerPagination;
45
+ initialSort?: { column: string; direction: SortDirection };
46
+ initialFilters?: Partial<FilterSnapshot>;
47
+ onPaginationChange?: (query: TableQuery) => void;
31
48
  } = $props();
32
49
 
33
- const sort = new SortState();
34
- const filter = new FilterState();
50
+ // Intentional one-time hydration of local state from the initial prop
51
+ // values (not a live binding) — `svelte-check`'s state_referenced_locally
52
+ // warning is a false positive here.
53
+ const sort = new SortState(initialSort ?? null);
54
+ const filter = new FilterState(initialFilters ?? null);
35
55
 
36
- let currentPage = $state(1);
56
+ let currentPage = $state(pagination?.page ?? 1);
57
+ let lastKnownPage = pagination?.page ?? 1;
37
58
 
38
- const distinctValues = $derived(distinctEntries(data, columns));
59
+ const distinctValues = $derived(
60
+ pagination
61
+ ? Object.fromEntries(
62
+ columns
63
+ .filter((c) => isFilterable(c) && c.type === 'boolean')
64
+ .map((c) => [
65
+ c.attribute,
66
+ [
67
+ { key: 'true', row: {} as T },
68
+ { key: 'false', row: {} as T },
69
+ ],
70
+ ]),
71
+ )
72
+ : distinctEntries(data, columns)
73
+ );
39
74
 
40
75
  const indexed = $derived(data.map((row, index): IndexedRow<T> => ({ row, index })));
41
- const filtered = $derived(indexed.filter(({ row }) => filter.matches(row, columns)));
42
- const sorted = $derived(sort.apply(filtered, columns));
76
+ const filtered = $derived(pagination ? indexed : indexed.filter(({ row }) => filter.matches(row, columns)));
77
+ const sorted = $derived(pagination ? filtered : sort.apply(filtered, columns));
43
78
 
44
- const totalPages = $derived(Math.ceil(sorted.length / pageSize));
45
- const pageStart = $derived((currentPage - 1) * pageSize);
46
- const pageData = $derived(sorted.slice(pageStart, pageStart + pageSize));
79
+ const effectivePageSize = $derived(pagination?.pageSize ?? pageSize);
80
+ const totalPages = $derived(pagination?.totalPages ?? Math.ceil(sorted.length / effectivePageSize));
81
+ const displayPage = $derived(pagination?.page ?? currentPage);
82
+ const pageStart = $derived((displayPage - 1) * effectivePageSize);
83
+ const pageData = $derived(pagination ? sorted : sorted.slice(pageStart, pageStart + effectivePageSize));
84
+ const totalCount = $derived(pagination?.total ?? sorted.length);
47
85
  const allChecked = $derived(pageData.length > 0 && pageData.every((e) => selected.has(e.index)));
48
86
  const someChecked = $derived(pageData.some((e) => selected.has(e.index)));
49
87
 
88
+ // Client mode only: server mode's totalPages is externally owned, clamping
89
+ // here would fight with URL-driven navigation while a page reload is pending.
50
90
  $effect(() => {
51
- if (currentPage > totalPages && totalPages > 0) currentPage = totalPages;
91
+ if (!pagination && currentPage > totalPages && totalPages > 0) currentPage = totalPages;
52
92
  });
53
93
 
54
- function resetPage() {
94
+ // Server mode: external (URL/reload) page changes -> sync local state.
95
+ $effect(() => {
96
+ if (pagination && pagination.page !== lastKnownPage) {
97
+ currentPage = pagination.page;
98
+ lastKnownPage = pagination.page;
99
+ }
100
+ });
101
+
102
+ // Server mode: local (Paginator click) page changes -> notify caller.
103
+ $effect(() => {
104
+ if (pagination && currentPage !== lastKnownPage) {
105
+ lastKnownPage = currentPage;
106
+ onPaginationChange?.(currentQuery(currentPage));
107
+ }
108
+ });
109
+
110
+ function currentQuery(page: number): TableQuery {
111
+ return {
112
+ page,
113
+ ordering: sort.column ? (sort.direction === 'asc' ? sort.column : `-${sort.column}`) : null,
114
+ filters: snapshotFilter(filter),
115
+ };
116
+ }
117
+
118
+ function handleHeaderChange() {
55
119
  currentPage = 1;
120
+ if (!pagination) return;
121
+ lastKnownPage = 1;
122
+ onPaginationChange?.(currentQuery(1));
56
123
  }
57
124
 
58
125
  function toggleAll() {
@@ -82,7 +149,7 @@
82
149
  {distinctValues}
83
150
  hasRowActions={!!rowActions}
84
151
  {actionsLabel}
85
- onchange={resetPage}
152
+ onchange={handleHeaderChange}
86
153
  />
87
154
  <TableBody
88
155
  {columns}
@@ -100,7 +167,7 @@
100
167
  bind:page={currentPage}
101
168
  {totalPages}
102
169
  {pageStart}
103
- {pageSize}
104
- total={sorted.length}
170
+ pageSize={effectivePageSize}
171
+ total={totalCount}
105
172
  />
106
173
  </div>
@@ -1,4 +1,5 @@
1
1
  import { SvelteSet } from 'svelte/reactivity';
2
+ import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../types/table.js';
2
3
  import type { Snippet } from 'svelte';
3
4
  import type { ColumnDefinition } from '../../types/crud.js';
4
5
  declare function $$render<T extends object = Record<string, unknown>>(): {
@@ -10,6 +11,16 @@ declare function $$render<T extends object = Record<string, unknown>>(): {
10
11
  selected?: SvelteSet<number>;
11
12
  rowActions?: Snippet<[T]>;
12
13
  actionsLabel?: string;
14
+ /** When provided, the table trusts `data` is already the requested page and
15
+ * defers pagination/sort/filter to `onPaginationChange` instead of computing
16
+ * them locally. Omit for the original fully-client-side behavior. */
17
+ pagination?: ServerPagination;
18
+ initialSort?: {
19
+ column: string;
20
+ direction: SortDirection;
21
+ };
22
+ initialFilters?: Partial<FilterSnapshot>;
23
+ onPaginationChange?: (query: TableQuery) => void;
13
24
  };
14
25
  exports: {};
15
26
  bindings: "selected";
@@ -1,9 +1,13 @@
1
1
  import { SvelteMap, SvelteSet } from 'svelte/reactivity';
2
2
  import type { ColumnDefinition } from '../../types/crud.js';
3
- import type { IndexedRow, SortDirection } from '../../types/table.js';
3
+ import type { FilterSnapshot, IndexedRow, SortDirection } from '../../types/table.js';
4
4
  export declare class SortState {
5
5
  column: string | null;
6
6
  direction: SortDirection | null;
7
+ constructor(initial?: {
8
+ column: string | null;
9
+ direction: SortDirection | null;
10
+ } | null);
7
11
  directionFor(attribute: string): SortDirection | null;
8
12
  cycle(attribute: string): void;
9
13
  apply<T extends object>(rows: IndexedRow<T>[], columns: ColumnDefinition<T>[]): IndexedRow<T>[];
@@ -15,6 +19,7 @@ export declare class FilterState {
15
19
  from: string;
16
20
  to: string;
17
21
  }>;
22
+ constructor(initial?: Partial<FilterSnapshot> | null);
18
23
  textFor(attribute: string): string;
19
24
  dateRangeFor(attribute: string): {
20
25
  from: string;
@@ -28,3 +33,4 @@ export declare class FilterState {
28
33
  clear(attribute: string): void;
29
34
  matches<T extends object>(row: T, columns: ColumnDefinition<T>[]): boolean;
30
35
  }
36
+ export declare function snapshotFilter(filter: FilterState): FilterSnapshot;
@@ -3,6 +3,12 @@ import { cellRenderedText, compare, isFilterable } from './utils.js';
3
3
  export class SortState {
4
4
  column = $state(null);
5
5
  direction = $state(null);
6
+ constructor(initial) {
7
+ if (initial?.column) {
8
+ this.column = initial.column;
9
+ this.direction = initial.direction ?? 'desc';
10
+ }
11
+ }
6
12
  directionFor(attribute) {
7
13
  return this.column === attribute ? this.direction : null;
8
14
  }
@@ -36,6 +42,15 @@ export class FilterState {
36
42
  text = new SvelteMap();
37
43
  values = new SvelteMap();
38
44
  dateRanges = new SvelteMap();
45
+ constructor(initial) {
46
+ for (const [k, v] of Object.entries(initial?.text ?? {}))
47
+ this.text.set(k, v);
48
+ for (const [k, v] of Object.entries(initial?.values ?? {})) {
49
+ this.values.set(k, new SvelteSet(v));
50
+ }
51
+ for (const [k, v] of Object.entries(initial?.dateRanges ?? {}))
52
+ this.dateRanges.set(k, v);
53
+ }
39
54
  textFor(attribute) {
40
55
  return this.text.get(attribute) ?? '';
41
56
  }
@@ -107,3 +122,10 @@ export class FilterState {
107
122
  });
108
123
  }
109
124
  }
125
+ export function snapshotFilter(filter) {
126
+ return {
127
+ text: Object.fromEntries(filter.text),
128
+ values: Object.fromEntries([...filter.values].map(([k, set]) => [k, [...set]])),
129
+ dateRanges: Object.fromEntries(filter.dateRanges),
130
+ };
131
+ }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export type { BreadcrumbItem } from './types/breadcrumb.js';
2
2
  export { AttributeType } from './types/attribute.js';
3
3
  export type { AttributeMetadata, InterfaceMetadata, SelectOption, OptionsResolver, FormatterResolver } from './types/attribute.js';
4
- export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry } from './types/table.js';
4
+ export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry, PaginatedEnvelope, ServerPagination, FilterSnapshot, TableQuery } from './types/table.js';
5
5
  export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, RowAction } from './types/crud.js';
6
6
  export type { RuneforgeConfig } from './config/context.js';
7
7
  export { setConfig, getConfig } from './config/context.js';
@@ -28,7 +28,7 @@ export { default as TableBody } from './components/table/TableBody.svelte';
28
28
  export { default as SortHeader } from './components/table/SortHeader.svelte';
29
29
  export { default as Paginator } from './components/table/Paginator.svelte';
30
30
  export { default as ColumnFilter } from './components/table/ColumnFilter.svelte';
31
- export { SortState, FilterState } from './components/table/state.svelte.js';
31
+ export { SortState, FilterState, snapshotFilter } from './components/table/state.svelte.js';
32
32
  export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries } from './components/table/utils.js';
33
33
  export { default as GenericCRUD } from './components/crud/GenericCRUD.svelte';
34
34
  export { default as Field } from './components/crud/Field.svelte';
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ export { default as TableBody } from './components/table/TableBody.svelte';
24
24
  export { default as SortHeader } from './components/table/SortHeader.svelte';
25
25
  export { default as Paginator } from './components/table/Paginator.svelte';
26
26
  export { default as ColumnFilter } from './components/table/ColumnFilter.svelte';
27
- export { SortState, FilterState } from './components/table/state.svelte.js';
27
+ export { SortState, FilterState, snapshotFilter } from './components/table/state.svelte.js';
28
28
  export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries } from './components/table/utils.js';
29
29
  // CRUD components
30
30
  export { default as GenericCRUD } from './components/crud/GenericCRUD.svelte';
@@ -14,3 +14,28 @@ export interface CellProps<T extends object = Record<string, unknown>, V = unkno
14
14
  }
15
15
  export type CellComponent<T extends object = Record<string, unknown>, V = unknown> = Component<CellProps<T, V>>;
16
16
  export type CellFormatter<T extends object = Record<string, unknown>, V = unknown> = (value: CellProps<T, V>['value'], row: CellProps<T, V>['row']) => string;
17
+ export interface PaginatedEnvelope<T> {
18
+ results: T[];
19
+ count: number;
20
+ page: number;
21
+ pageSize: number;
22
+ }
23
+ export interface ServerPagination {
24
+ page: number;
25
+ totalPages: number;
26
+ total: number;
27
+ pageSize: number;
28
+ }
29
+ export interface FilterSnapshot {
30
+ text: Record<string, string>;
31
+ values: Record<string, string[]>;
32
+ dateRanges: Record<string, {
33
+ from: string;
34
+ to: string;
35
+ }>;
36
+ }
37
+ export interface TableQuery {
38
+ page: number;
39
+ ordering: string | null;
40
+ filters: FilterSnapshot;
41
+ }
package/package.json CHANGED
@@ -1,81 +1,79 @@
1
1
  {
2
- "name": "runeforge",
3
- "version": "0.0.12",
4
- "description": "SvelteKit toolkit for building metadata-driven CRUD interfaces with tables, forms, and actions",
5
- "license": "MIT",
6
- "author": "Ezequiel Puerta",
7
- "keywords": [
8
- "svelte",
9
- "sveltekit",
10
- "crud",
11
- "table",
12
- "form",
13
- "daisyui",
14
- "tailwindcss",
15
- "ui"
16
- ],
17
- "scripts": {
18
- "dev": "vite dev",
19
- "build": "vite build && npm run prepack",
20
- "preview": "vite preview",
21
- "prepare": "svelte-kit sync || echo ''",
22
- "prepack": "svelte-kit sync && svelte-package && publint",
23
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
24
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
25
- "test": "pnpm run test:unit && pnpm run test:e2e",
26
- "test:unit": "vitest run",
27
- "test:unit:watch": "vitest",
28
- "test:e2e": "playwright test",
29
- "lint": "prettier --check . && eslint .",
30
- "format": "prettier --write ."
31
- },
32
- "files": [
33
- "dist",
34
- "!dist/**/*.test.*",
35
- "!dist/**/*.spec.*"
36
- ],
37
- "sideEffects": [
38
- "**/*.css"
39
- ],
40
- "svelte": "./dist/index.js",
41
- "types": "./dist/index.d.ts",
42
- "type": "module",
43
- "exports": {
44
- ".": {
45
- "types": "./dist/index.d.ts",
46
- "svelte": "./dist/index.js"
47
- }
48
- },
49
- "peerDependencies": {
50
- "@sveltejs/kit": "^2.0.0",
51
- "daisyui": "^5.0.0",
52
- "svelte": "^5.0.0",
53
- "tailwindcss": "^4.0.0"
54
- },
55
- "devDependencies": {
56
- "@eslint/js": "^10.0.1",
57
- "@playwright/test": "^1.60.0",
58
- "@sveltejs/adapter-auto": "^7.0.1",
59
- "@sveltejs/kit": "^2.63.0",
60
- "@sveltejs/package": "^2.5.8",
61
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
62
- "@tailwindcss/vite": "^4.3.1",
63
- "@types/node": "^22",
64
- "daisyui": "^5.5.23",
65
- "eslint": "^10.4.1",
66
- "eslint-config-prettier": "^10.1.8",
67
- "eslint-plugin-svelte": "^3.19.0",
68
- "globals": "^17.6.0",
69
- "prettier": "^3.8.3",
70
- "prettier-plugin-svelte": "^4.1.0",
71
- "publint": "^0.3.21",
72
- "svelte": "^5.56.1",
73
- "svelte-bootstrap-icons": "^3.3.0",
74
- "svelte-check": "^4.6.0",
75
- "tailwindcss": "^4.3.1",
76
- "typescript": "^6.0.3",
77
- "typescript-eslint": "^8.60.1",
78
- "vite": "^8.0.16",
79
- "vitest": "^4.1.8"
80
- }
81
- }
2
+ "name": "runeforge",
3
+ "version": "0.0.13",
4
+ "description": "SvelteKit toolkit for building metadata-driven CRUD interfaces with tables, forms, and actions",
5
+ "license": "MIT",
6
+ "author": "Ezequiel Puerta",
7
+ "keywords": [
8
+ "svelte",
9
+ "sveltekit",
10
+ "crud",
11
+ "table",
12
+ "form",
13
+ "daisyui",
14
+ "tailwindcss",
15
+ "ui"
16
+ ],
17
+ "files": [
18
+ "dist",
19
+ "!dist/**/*.test.*",
20
+ "!dist/**/*.spec.*"
21
+ ],
22
+ "sideEffects": [
23
+ "**/*.css"
24
+ ],
25
+ "svelte": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "type": "module",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "svelte": "./dist/index.js"
32
+ }
33
+ },
34
+ "peerDependencies": {
35
+ "@sveltejs/kit": "^2.0.0",
36
+ "daisyui": "^5.0.0",
37
+ "svelte": "^5.0.0",
38
+ "tailwindcss": "^4.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@eslint/js": "^10.0.1",
42
+ "@playwright/test": "^1.60.0",
43
+ "@sveltejs/adapter-auto": "^7.0.1",
44
+ "@sveltejs/kit": "^2.63.0",
45
+ "@sveltejs/package": "^2.5.8",
46
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
47
+ "@tailwindcss/vite": "^4.3.1",
48
+ "@types/node": "^22",
49
+ "daisyui": "^5.5.23",
50
+ "eslint": "^10.4.1",
51
+ "eslint-config-prettier": "^10.1.8",
52
+ "eslint-plugin-svelte": "^3.19.0",
53
+ "globals": "^17.6.0",
54
+ "prettier": "^3.8.3",
55
+ "prettier-plugin-svelte": "^4.1.0",
56
+ "publint": "^0.3.21",
57
+ "svelte": "^5.56.1",
58
+ "svelte-bootstrap-icons": "^3.3.0",
59
+ "svelte-check": "^4.6.0",
60
+ "tailwindcss": "^4.3.1",
61
+ "typescript": "^6.0.3",
62
+ "typescript-eslint": "^8.60.1",
63
+ "vite": "^8.0.16",
64
+ "vitest": "^4.1.8"
65
+ },
66
+ "scripts": {
67
+ "dev": "vite dev",
68
+ "build": "vite build && npm run prepack",
69
+ "preview": "vite preview",
70
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
71
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
72
+ "test": "pnpm run test:unit && pnpm run test:e2e",
73
+ "test:unit": "vitest run",
74
+ "test:unit:watch": "vitest",
75
+ "test:e2e": "playwright test",
76
+ "lint": "prettier --check . && eslint .",
77
+ "format": "prettier --write ."
78
+ }
79
+ }