runeforge 0.0.17 → 0.0.19

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.
@@ -1,344 +1,379 @@
1
1
  <script lang="ts" generics="T extends object = Record<string, unknown>">
2
- import { page } from '$app/state';
3
- import { goto } from '$app/navigation';
4
- import List from './views/List.svelte';
5
- import Read from './views/Read.svelte';
6
- import Create from './views/Create.svelte';
7
- import Update from './views/Update.svelte';
8
- import { AUTO_EXCLUDED } from './utils/constants.js';
9
- import { resolveOptions, resolveFormatter, inferType } from './utils/resolution.js';
10
- import { isFilterable } from '../table/utils.js';
11
- import type { AttributeMetadata } from '../../types/attribute.js';
12
- import type {
13
- ActionConfiguration,
14
- ColumnDefinition,
15
- CustomAction,
16
- FieldDefinition,
17
- } from '../../types/crud.js';
18
- import type {
19
- FilterSnapshot,
20
- PaginatedEnvelope,
21
- ServerPagination,
22
- SortDirection,
23
- TableQuery,
24
- } from '../../types/table.js';
2
+ import { page } from '$app/state';
3
+ import { goto } from '$app/navigation';
4
+ import List from './views/List.svelte';
5
+ import Read from './views/Read.svelte';
6
+ import Create from './views/Create.svelte';
7
+ import Update from './views/Update.svelte';
8
+ import { AUTO_EXCLUDED } from './utils/constants.js';
9
+ import {
10
+ resolveOptions,
11
+ resolveFormatter,
12
+ inferType
13
+ } from './utils/resolution.js';
14
+ import { isFilterable } from '../table/utils.js';
15
+ import type { XlsxModule } from '../table/export.js';
16
+ import type { AttributeMetadata } from '../../types/attribute.js';
17
+ import type {
18
+ ActionConfiguration,
19
+ ColumnDefinition,
20
+ CustomAction,
21
+ CustomBulkAction,
22
+ FieldDefinition,
23
+ SearchConfiguration
24
+ } from '../../types/crud.js';
25
+ import type {
26
+ FilterSnapshot,
27
+ PaginatedEnvelope,
28
+ ServerPagination,
29
+ SortDirection,
30
+ TableQuery
31
+ } from '../../types/table.js';
25
32
 
26
- let {
27
- data = undefined as Record<string, unknown> | undefined,
28
- dataKey = undefined as string | undefined,
29
- idKey = '_id',
30
- labelOne = '',
31
- labelMany = '',
32
- icon,
33
- pageSize = 10,
34
- creation = {} as ActionConfiguration<T>,
35
- update = {} as ActionConfiguration<T>,
36
- read = {} as ActionConfiguration<T>,
37
- deletion = {} as ActionConfiguration<T>,
38
- actions = [] as CustomAction<T>[],
39
- columns = undefined as ColumnDefinition<T>[] | undefined,
40
- fields = undefined as FieldDefinition<T>[] | undefined,
41
- meta = undefined as Partial<Record<string, AttributeMetadata>> | undefined,
42
- form = null as { error?: string } | null,
43
- }: {
44
- data?: Record<string, unknown>;
45
- dataKey?: string;
46
- idKey?: string;
47
- labelOne?: string;
48
- labelMany?: string;
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- icon?: any;
51
- pageSize?: number;
52
- creation?: ActionConfiguration<T>;
53
- update?: ActionConfiguration<T>;
54
- read?: ActionConfiguration<T>;
55
- deletion?: ActionConfiguration<T>;
56
- actions?: CustomAction<T>[];
57
- columns?: ColumnDefinition<T>[];
58
- fields?: FieldDefinition<T>[];
59
- meta?: Partial<Record<string, AttributeMetadata>>;
60
- form?: { error?: string } | null;
61
- } = $props();
33
+ let {
34
+ data = undefined as Record<string, unknown> | undefined,
35
+ dataKey = undefined as string | undefined,
36
+ idKey = '_id',
37
+ labelOne = '',
38
+ labelMany = '',
39
+ icon,
40
+ pageSize = 10,
41
+ creation = {} as ActionConfiguration<T>,
42
+ update = {} as ActionConfiguration<T>,
43
+ read = {} as ActionConfiguration<T>,
44
+ deletion = {} as ActionConfiguration<T>,
45
+ actions = [] as CustomAction<T>[],
46
+ customBulkActions = [] as CustomBulkAction<T>[],
47
+ search = undefined as SearchConfiguration | undefined,
48
+ columns = undefined as ColumnDefinition<T>[] | undefined,
49
+ fields = undefined as FieldDefinition<T>[] | undefined,
50
+ meta = undefined as Partial<Record<string, AttributeMetadata>> | undefined,
51
+ form = null as { error?: string } | null,
52
+ enableExport = false,
53
+ onExport = undefined as ((query: TableQuery) => Promise<T[]>) | undefined,
54
+ xlsx = undefined as XlsxModule | undefined
55
+ }: {
56
+ data?: Record<string, unknown>;
57
+ dataKey?: string;
58
+ idKey?: string;
59
+ labelOne?: string;
60
+ labelMany?: string;
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ icon?: any;
63
+ pageSize?: number;
64
+ creation?: ActionConfiguration<T>;
65
+ update?: ActionConfiguration<T>;
66
+ read?: ActionConfiguration<T>;
67
+ deletion?: ActionConfiguration<T>;
68
+ actions?: CustomAction<T>[];
69
+ customBulkActions?: CustomBulkAction<T>[];
70
+ search?: SearchConfiguration;
71
+ columns?: ColumnDefinition<T>[];
72
+ fields?: FieldDefinition<T>[];
73
+ meta?: Partial<Record<string, AttributeMetadata>>;
74
+ form?: { error?: string } | null;
75
+ /** Shows an icon-only export button (CSV, and Excel if `xlsx` is provided). */
76
+ enableExport?: boolean;
77
+ /** Server-pagination mode only: fetch all rows matching the current query
78
+ * (unpaginated) for export. Without it, export falls back to the loaded page. */
79
+ onExport?: (query: TableQuery) => Promise<T[]>;
80
+ /** Resolved `xlsx` (SheetJS) module, e.g. `import * as xlsx from 'xlsx'`.
81
+ * Enables the "Export as Excel" option; omit to only offer CSV. */
82
+ xlsx?: XlsxModule;
83
+ } = $props();
62
84
 
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
- }
85
+ function isEnvelope(value: unknown): value is PaginatedEnvelope<T> {
86
+ return (
87
+ !!value &&
88
+ typeof value === 'object' &&
89
+ !Array.isArray(value) &&
90
+ Array.isArray((value as Record<string, unknown>).results) &&
91
+ typeof (value as Record<string, unknown>).count === 'number'
92
+ );
93
+ }
72
94
 
73
- const rawSlice = $derived(data && dataKey ? data[dataKey] : undefined);
74
- const envelope = $derived(isEnvelope(rawSlice) ? rawSlice : undefined);
75
- const entityData = $derived<T[]>(
76
- envelope ? envelope.results : (Array.isArray(rawSlice) ? (rawSlice as T[]) : [])
77
- );
95
+ const rawSlice = $derived(data && dataKey ? data[dataKey] : undefined);
96
+ const envelope = $derived(isEnvelope(rawSlice) ? rawSlice : undefined);
97
+ const entityData = $derived<T[]>(
98
+ envelope ? envelope.results : Array.isArray(rawSlice) ? (rawSlice as T[]) : []
99
+ );
78
100
 
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
88
- );
101
+ const serverPagination = $derived<ServerPagination | undefined>(
102
+ envelope
103
+ ? {
104
+ page: envelope.page,
105
+ pageSize: envelope.pageSize,
106
+ totalPages: Math.max(1, Math.ceil(envelope.count / envelope.pageSize)),
107
+ total: envelope.count
108
+ }
109
+ : undefined
110
+ );
89
111
 
90
- const viewParam = $derived(page.url.searchParams.get('view'));
91
- const idParam = $derived(page.url.searchParams.get('id'));
112
+ const viewParam = $derived(page.url.searchParams.get('view'));
113
+ const idParam = $derived(page.url.searchParams.get('id'));
92
114
 
93
- const creating = $derived(viewParam === 'create');
94
- const reading = $derived(idParam !== null && viewParam === null);
95
- const editing = $derived(idParam !== null && viewParam === 'edit');
115
+ const creating = $derived(viewParam === 'create');
116
+ const reading = $derived(idParam !== null && viewParam === null);
117
+ const editing = $derived(idParam !== null && viewParam === 'edit');
96
118
 
97
- const excluded = $derived(new Set([...AUTO_EXCLUDED, idKey]));
119
+ const excluded = $derived(new Set([...AUTO_EXCLUDED, idKey]));
98
120
 
99
- const singleInstance = $derived<T | undefined>(
100
- (Object.values(page.data as Record<string, unknown>).find(
101
- (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && idKey in (v as object)
102
- ) as T | undefined)
103
- );
121
+ const singleInstance = $derived<T | undefined>(
122
+ Object.values(page.data as Record<string, unknown>).find(
123
+ (v) => v !== null && typeof v === 'object' && !Array.isArray(v) && idKey in (v as object)
124
+ ) as T | undefined
125
+ );
104
126
 
105
- let activeAction = $state<{ action: CustomAction<T>; item: T } | null>(null);
127
+ let activeAction = $state<{ action: CustomAction<T>; item: T } | null>(null);
106
128
 
107
- async function navList() { await goto('?'); }
108
- async function navCreate() { await goto('?view=create'); }
109
- async function navRead(item: T) {
110
- await goto(`?id=${encodeURIComponent(String((item as Record<string, unknown>)[idKey] ?? ''))}`);
111
- }
112
- async function navEdit(item: T) {
113
- await goto(`?id=${encodeURIComponent(String((item as Record<string, unknown>)[idKey] ?? ''))}&view=edit`);
114
- }
129
+ async function navList() {
130
+ await goto('?');
131
+ }
132
+ async function navCreate() {
133
+ await goto('?view=create');
134
+ }
135
+ async function navRead(item: T) {
136
+ await goto(`?id=${encodeURIComponent(String((item as Record<string, unknown>)[idKey] ?? ''))}`);
137
+ }
138
+ async function navEdit(item: T) {
139
+ await goto(
140
+ `?id=${encodeURIComponent(String((item as Record<string, unknown>)[idKey] ?? ''))}&view=edit`
141
+ );
142
+ }
115
143
 
116
- const resolvedColumns: ColumnDefinition<T>[] = $derived(
117
- columns ?? (meta
118
- ? (Object.entries(meta) as [string, AttributeMetadata][])
119
- .filter(([, m]) => !m.excludedFromList)
120
- .map(([k, m]) => ({
121
- attribute: k as keyof T & string,
122
- title: m.label ?? k,
123
- type: m.type,
124
- formatter: resolveFormatter(m, data),
125
- component: m.component,
126
- sortable: m.sortable,
127
- filterable: m.filterable,
128
- }))
129
- : entityData.length > 0
130
- ? (Object.keys(entityData[0]) as (keyof T & string)[])
131
- .filter((k) => !excluded.has(k))
132
- .map((k) => ({ attribute: k, title: k }))
133
- : [])
134
- );
144
+ const resolvedColumns: ColumnDefinition<T>[] = $derived(
145
+ columns ??
146
+ (meta
147
+ ? (Object.entries(meta) as [string, AttributeMetadata][])
148
+ .filter(([, m]) => !m.excludedFromList)
149
+ .map(([k, m]) => ({
150
+ attribute: k as keyof T & string,
151
+ title: m.label ?? k,
152
+ type: m.type,
153
+ formatter: resolveFormatter(m, data),
154
+ component: m.component,
155
+ sortable: m.sortable,
156
+ filterable: m.filterable
157
+ }))
158
+ : entityData.length > 0
159
+ ? (Object.keys(entityData[0]) as (keyof T & string)[])
160
+ .filter((k) => !excluded.has(k))
161
+ .map((k) => ({ attribute: k, title: k }))
162
+ : [])
163
+ );
135
164
 
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
- );
165
+ // Server-pagination mode only: GenericCRUD owns `page`/`ordering`/per-column
166
+ // filter query params the same way it already owns `view`/`id`, so pagination
167
+ // state survives reloads/direct links and `+page.svelte` never has to change.
168
+ const orderingParam = $derived(page.url.searchParams.get('ordering'));
169
+ const initialSort = $derived(
170
+ orderingParam
171
+ ? {
172
+ column: orderingParam.startsWith('-') ? orderingParam.slice(1) : orderingParam,
173
+ direction: (orderingParam.startsWith('-') ? 'desc' : 'asc') as SortDirection
174
+ }
175
+ : undefined
176
+ );
148
177
 
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
- });
178
+ const initialFilters = $derived.by<Partial<FilterSnapshot> | undefined>(() => {
179
+ if (!envelope) return undefined;
180
+ const text: Record<string, string> = {};
181
+ const values: Record<string, string[]> = {};
182
+ const dateRanges: Record<string, { from: string; to: string }> = {};
183
+ for (const col of resolvedColumns) {
184
+ if (!isFilterable(col)) continue;
185
+ const raw = page.url.searchParams.get(col.attribute);
186
+ if (raw != null) {
187
+ if (col.type === 'boolean') values[col.attribute] = raw.split(',').filter(Boolean);
188
+ else text[col.attribute] = raw;
189
+ }
190
+ const from = page.url.searchParams.get(`${col.attribute}_from`);
191
+ const to = page.url.searchParams.get(`${col.attribute}_to`);
192
+ if (from || to) dateRanges[col.attribute] = { from: from ?? '', to: to ?? '' };
193
+ }
194
+ return { text, values, dateRanges };
195
+ });
167
196
 
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');
197
+ async function handlePaginationChange(query: TableQuery) {
198
+ // Local, synchronous query-string builder consumed immediately by goto();
199
+ // not rendered/reactive state, so plain URLSearchParams is correct here.
200
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
201
+ const params = new URLSearchParams(page.url.searchParams);
202
+ params.delete('view');
203
+ params.delete('id');
175
204
 
176
- if (query.page > 1) params.set('page', String(query.page));
177
- else params.delete('page');
205
+ if (query.page > 1) params.set('page', String(query.page));
206
+ else params.delete('page');
178
207
 
179
- if (query.ordering) params.set('ordering', query.ordering);
180
- else params.delete('ordering');
208
+ if (query.ordering) params.set('ordering', query.ordering);
209
+ else params.delete('ordering');
181
210
 
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
- }
211
+ for (const col of resolvedColumns) {
212
+ params.delete(col.attribute);
213
+ params.delete(`${col.attribute}_from`);
214
+ params.delete(`${col.attribute}_to`);
215
+ }
216
+ for (const [k, v] of Object.entries(query.filters.text)) {
217
+ if (v) params.set(k, v);
218
+ }
219
+ for (const [k, vs] of Object.entries(query.filters.values)) {
220
+ if (vs.length) params.set(k, vs.join(','));
221
+ }
222
+ for (const [k, r] of Object.entries(query.filters.dateRanges)) {
223
+ if (r.from) params.set(`${k}_from`, r.from);
224
+ if (r.to) params.set(`${k}_to`, r.to);
225
+ }
197
226
 
198
- const qs = params.toString();
199
- await goto(qs ? `?${qs}` : '?', { keepFocus: true, noScroll: true });
200
- }
227
+ const qs = params.toString();
228
+ await goto(qs ? `?${qs}` : '?', { keepFocus: true, noScroll: true });
229
+ }
201
230
 
202
- const resolvedFields: FieldDefinition<T>[] = $derived(
203
- fields ?? (meta
204
- ? (Object.entries(meta) as [string, AttributeMetadata][])
205
- .filter(([k, m]) => !excluded.has(k) && !m.excludedFromCreate)
206
- .map(([k, m]) => ({
207
- attribute: k as keyof T & string,
208
- title: m.label,
209
- type: m.type ?? inferType(k, undefined),
210
- required: m.required,
211
- autocomplete: m.autocomplete,
212
- placeholder: m.placeholder,
213
- default: m.default,
214
- options: resolveOptions(m, data),
215
- }))
216
- : entityData.length > 0
217
- ? (Object.entries(entityData[0]) as [string, unknown][])
218
- .filter(([k]) => !excluded.has(k))
219
- .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
220
- : [])
221
- );
231
+ const resolvedFields: FieldDefinition<T>[] = $derived(
232
+ fields ??
233
+ (meta
234
+ ? (Object.entries(meta) as [string, AttributeMetadata][])
235
+ .filter(([k, m]) => !excluded.has(k) && !m.excludedFromCreate)
236
+ .map(([k, m]) => ({
237
+ attribute: k as keyof T & string,
238
+ title: m.label,
239
+ type: m.type ?? inferType(k, undefined),
240
+ required: m.required,
241
+ autocomplete: m.autocomplete,
242
+ placeholder: m.placeholder,
243
+ default: m.default,
244
+ options: resolveOptions(m, data)
245
+ }))
246
+ : entityData.length > 0
247
+ ? (Object.entries(entityData[0]) as [string, unknown][])
248
+ .filter(([k]) => !excluded.has(k))
249
+ .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
250
+ : [])
251
+ );
222
252
 
223
- const resolvedReadFields: FieldDefinition<T>[] = $derived(
224
- fields ?? (meta
225
- ? (Object.entries(meta) as [string, AttributeMetadata][])
226
- .filter(([k, m]) => !excluded.has(k) && !m.excludedFromRead)
227
- .map(([k, m]) => ({
228
- attribute: k as keyof T & string,
229
- title: m.label,
230
- type: m.type ?? inferType(k, undefined),
231
- required: m.required,
232
- autocomplete: m.autocomplete,
233
- placeholder: m.placeholder,
234
- default: m.default,
235
- options: resolveOptions(m, data),
236
- }))
237
- : entityData.length > 0
238
- ? (Object.entries(entityData[0]) as [string, unknown][])
239
- .filter(([k]) => !excluded.has(k))
240
- .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
241
- : [])
242
- );
253
+ const resolvedReadFields: FieldDefinition<T>[] = $derived(
254
+ fields ??
255
+ (meta
256
+ ? (Object.entries(meta) as [string, AttributeMetadata][])
257
+ .filter(([k, m]) => !excluded.has(k) && !m.excludedFromRead)
258
+ .map(([k, m]) => ({
259
+ attribute: k as keyof T & string,
260
+ title: m.label,
261
+ type: m.type ?? inferType(k, undefined),
262
+ required: m.required,
263
+ autocomplete: m.autocomplete,
264
+ placeholder: m.placeholder,
265
+ default: m.default,
266
+ options: resolveOptions(m, data)
267
+ }))
268
+ : entityData.length > 0
269
+ ? (Object.entries(entityData[0]) as [string, unknown][])
270
+ .filter(([k]) => !excluded.has(k))
271
+ .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
272
+ : [])
273
+ );
243
274
 
244
- const resolvedUpdateFields: FieldDefinition<T>[] = $derived(
245
- fields ?? (meta
246
- ? (Object.entries(meta) as [string, AttributeMetadata][])
247
- .filter(([k, m]) => !excluded.has(k) && !m.excludedFromUpdate)
248
- .map(([k, m]) => ({
249
- attribute: k as keyof T & string,
250
- title: m.label,
251
- type: m.type ?? inferType(k, undefined),
252
- required: m.required,
253
- autocomplete: m.autocomplete,
254
- placeholder: m.placeholder,
255
- default: m.default,
256
- options: resolveOptions(m, data),
257
- }))
258
- : entityData.length > 0
259
- ? (Object.entries(entityData[0]) as [string, unknown][])
260
- .filter(([k]) => !excluded.has(k))
261
- .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
262
- : [])
263
- );
275
+ const resolvedUpdateFields: FieldDefinition<T>[] = $derived(
276
+ fields ??
277
+ (meta
278
+ ? (Object.entries(meta) as [string, AttributeMetadata][])
279
+ .filter(([k, m]) => !excluded.has(k) && !m.excludedFromUpdate)
280
+ .map(([k, m]) => ({
281
+ attribute: k as keyof T & string,
282
+ title: m.label,
283
+ type: m.type ?? inferType(k, undefined),
284
+ required: m.required,
285
+ autocomplete: m.autocomplete,
286
+ placeholder: m.placeholder,
287
+ default: m.default,
288
+ options: resolveOptions(m, data)
289
+ }))
290
+ : entityData.length > 0
291
+ ? (Object.entries(entityData[0]) as [string, unknown][])
292
+ .filter(([k]) => !excluded.has(k))
293
+ .map(([k, v]) => ({ attribute: k as keyof T & string, type: inferType(k, v) }))
294
+ : [])
295
+ );
264
296
 
265
- const serverError = $derived(
266
- creating || reading || editing || activeAction !== null
267
- ? (form?.error ?? '')
268
- : ''
269
- );
297
+ const serverError = $derived(
298
+ creating || reading || editing || activeAction !== null ? (form?.error ?? '') : ''
299
+ );
270
300
  </script>
271
301
 
272
302
  {#if creating}
273
- <Create
274
- {labelOne}
275
- {labelMany}
276
- {icon}
277
- fields={resolvedFields}
278
- {creation}
279
- {serverError}
280
- onCancel={navList}
281
- onSuccess={navList}
282
- />
303
+ <Create
304
+ {labelOne}
305
+ {labelMany}
306
+ {icon}
307
+ fields={resolvedFields}
308
+ {creation}
309
+ {serverError}
310
+ onCancel={navList}
311
+ onSuccess={navList}
312
+ />
283
313
  {:else if reading}
284
- <Read
285
- {labelOne}
286
- {labelMany}
287
- {icon}
288
- {idKey}
289
- fields={resolvedReadFields}
290
- instance={singleInstance ?? {} as T}
291
- {read}
292
- onCancel={navList}
293
- />
314
+ <Read
315
+ {labelOne}
316
+ {labelMany}
317
+ {icon}
318
+ {idKey}
319
+ fields={resolvedReadFields}
320
+ instance={singleInstance ?? ({} as T)}
321
+ {read}
322
+ onCancel={navList}
323
+ />
294
324
  {:else if editing}
295
- <Update
296
- {labelOne}
297
- {labelMany}
298
- {icon}
299
- {idKey}
300
- fields={resolvedUpdateFields}
301
- instance={singleInstance ?? {} as T}
302
- {update}
303
- {serverError}
304
- onCancel={navList}
305
- onSuccess={navList}
306
- />
325
+ <Update
326
+ {labelOne}
327
+ {labelMany}
328
+ {icon}
329
+ {idKey}
330
+ fields={resolvedUpdateFields}
331
+ instance={singleInstance ?? ({} as T)}
332
+ {update}
333
+ {serverError}
334
+ onCancel={navList}
335
+ onSuccess={navList}
336
+ />
307
337
  {:else}
308
- <List
309
- data={entityData}
310
- {labelOne}
311
- {labelMany}
312
- {icon}
313
- {pageSize}
314
- {idKey}
315
- {creation}
316
- {update}
317
- {read}
318
- {deletion}
319
- {actions}
320
- columns={resolvedColumns}
321
- pagination={serverPagination}
322
- {initialSort}
323
- {initialFilters}
324
- onPaginationChange={handlePaginationChange}
325
- onCreate={navCreate}
326
- onEdit={navEdit}
327
- onView={navRead}
328
- onAction={(action, item) => {
329
- if (action.condition?.(item) ?? true) activeAction = { action, item };
330
- }}
331
- />
338
+ <List
339
+ data={entityData}
340
+ {labelOne}
341
+ {labelMany}
342
+ {icon}
343
+ {pageSize}
344
+ {idKey}
345
+ {creation}
346
+ {update}
347
+ {read}
348
+ {deletion}
349
+ {actions}
350
+ {customBulkActions}
351
+ {search}
352
+ columns={resolvedColumns}
353
+ pagination={serverPagination}
354
+ {initialSort}
355
+ {initialFilters}
356
+ onPaginationChange={handlePaginationChange}
357
+ {enableExport}
358
+ {onExport}
359
+ {xlsx}
360
+ onCreate={navCreate}
361
+ onEdit={navEdit}
362
+ onView={navRead}
363
+ onAction={(action, item) => {
364
+ if (action.condition?.(item) ?? true) activeAction = { action, item };
365
+ }}
366
+ />
332
367
  {/if}
333
368
 
334
369
  {#if activeAction !== null}
335
- {@const ActionView = activeAction.action.view}
336
- <ActionView
337
- instance={activeAction.item}
338
- label={activeAction.action.label}
339
- endpoint={activeAction.action.endpoint}
340
- {serverError}
341
- onCancel={() => (activeAction = null)}
342
- onSuccess={() => (activeAction = null)}
343
- />
370
+ {@const ActionView = activeAction.action.view}
371
+ <ActionView
372
+ instance={activeAction.item}
373
+ label={activeAction.action.label}
374
+ endpoint={activeAction.action.endpoint}
375
+ {serverError}
376
+ onCancel={() => (activeAction = null)}
377
+ onSuccess={() => (activeAction = null)}
378
+ />
344
379
  {/if}