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.
- package/dist/components/crud/GenericCRUD.svelte +346 -311
- package/dist/components/crud/GenericCRUD.svelte.d.ts +13 -1
- package/dist/components/crud/SearchInput.svelte +53 -0
- package/dist/components/crud/SearchInput.svelte.d.ts +7 -0
- package/dist/components/crud/views/List.svelte +379 -221
- package/dist/components/crud/views/List.svelte.d.ts +12 -1
- package/dist/components/table/PaginatedTable.svelte +16 -0
- package/dist/components/table/PaginatedTable.svelte.d.ts +8 -2
- package/dist/components/table/export.d.ts +17 -0
- package/dist/components/table/export.js +31 -0
- package/dist/i18n/en.js +5 -1
- package/dist/i18n/es.js +5 -1
- package/dist/i18n/types.d.ts +4 -0
- package/dist/icons/defaults/Download.svelte +7 -0
- package/dist/icons/defaults/Download.svelte.d.ts +7 -0
- package/dist/icons/sets/bootstrap.js +2 -1
- package/dist/icons/sets/default.js +2 -0
- package/dist/icons/types.d.ts +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/types/crud.d.ts +13 -0
- package/package.json +1 -1
|
@@ -1,344 +1,379 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends object = Record<string, unknown>">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
112
|
+
const viewParam = $derived(page.url.searchParams.get('view'));
|
|
113
|
+
const idParam = $derived(page.url.searchParams.get('id'));
|
|
92
114
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
119
|
+
const excluded = $derived(new Set([...AUTO_EXCLUDED, idKey]));
|
|
98
120
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
127
|
+
let activeAction = $state<{ action: CustomAction<T>; item: T } | null>(null);
|
|
106
128
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
177
|
-
|
|
205
|
+
if (query.page > 1) params.set('page', String(query.page));
|
|
206
|
+
else params.delete('page');
|
|
178
207
|
|
|
179
|
-
|
|
180
|
-
|
|
208
|
+
if (query.ordering) params.set('ordering', query.ordering);
|
|
209
|
+
else params.delete('ordering');
|
|
181
210
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
227
|
+
const qs = params.toString();
|
|
228
|
+
await goto(qs ? `?${qs}` : '?', { keepFocus: true, noScroll: true });
|
|
229
|
+
}
|
|
201
230
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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}
|