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