runeforge 0.0.37 → 0.0.39
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/common/Header.svelte +1 -1
- package/dist/components/crud/GenericCRUD.svelte +6 -4
- package/dist/components/crud/SearchInput.svelte +1 -1
- package/dist/components/crud/views/List.svelte +230 -73
- package/dist/components/crud/views/List.svelte.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/types/crud.d.ts +32 -12
- package/package.json +1 -1
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
CustomAction,
|
|
22
22
|
CustomBulkAction,
|
|
23
23
|
FieldDefinition,
|
|
24
|
-
SearchConfiguration
|
|
24
|
+
SearchConfiguration,
|
|
25
|
+
ViewBasedCustomBulkAction
|
|
25
26
|
} from '../../types/crud.js';
|
|
26
27
|
import type {
|
|
27
28
|
FilterSnapshot,
|
|
@@ -121,7 +122,9 @@
|
|
|
121
122
|
);
|
|
122
123
|
|
|
123
124
|
let activeAction = $state<{ action: CustomAction<T>; item: T } | null>(null);
|
|
124
|
-
let activeBulkAction = $state<{ action:
|
|
125
|
+
let activeBulkAction = $state<{ action: ViewBasedCustomBulkAction<T>; items: T[] } | null>(
|
|
126
|
+
null
|
|
127
|
+
);
|
|
125
128
|
|
|
126
129
|
async function runAction(action: CustomAction<T>, item: T) {
|
|
127
130
|
if (!(action.condition?.(item) ?? true)) return;
|
|
@@ -132,7 +135,7 @@
|
|
|
132
135
|
activeAction = { action, item };
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
function runBulkAction(action:
|
|
138
|
+
function runBulkAction(action: ViewBasedCustomBulkAction<T>, items: T[]) {
|
|
136
139
|
activeBulkAction = { action, items };
|
|
137
140
|
}
|
|
138
141
|
|
|
@@ -387,7 +390,6 @@
|
|
|
387
390
|
<BulkActionView
|
|
388
391
|
items={activeBulkAction.items}
|
|
389
392
|
label={activeBulkAction.action.label}
|
|
390
|
-
endpoint={activeBulkAction.action.endpoint}
|
|
391
393
|
{serverError}
|
|
392
394
|
onCancel={() => (activeBulkAction = null)}
|
|
393
395
|
onSuccess={() => (activeBulkAction = null)}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
CustomAction,
|
|
16
16
|
CustomBulkAction,
|
|
17
17
|
RowAction,
|
|
18
|
-
SearchConfiguration
|
|
18
|
+
SearchConfiguration,
|
|
19
|
+
ViewBasedCustomBulkAction
|
|
19
20
|
} from '../../../types/crud.js';
|
|
20
21
|
import type {
|
|
21
22
|
FilterSnapshot,
|
|
@@ -27,6 +28,12 @@
|
|
|
27
28
|
|
|
28
29
|
const strings = getStrings();
|
|
29
30
|
|
|
31
|
+
function isViewBasedBulkAction(
|
|
32
|
+
action: CustomBulkAction<T>
|
|
33
|
+
): action is ViewBasedCustomBulkAction<T> {
|
|
34
|
+
return action.kind === 'view';
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
let {
|
|
31
38
|
data = [] as T[],
|
|
32
39
|
labelOne = '',
|
|
@@ -86,7 +93,7 @@
|
|
|
86
93
|
onEdit?: (item: T) => void;
|
|
87
94
|
onView?: (item: T) => void;
|
|
88
95
|
onAction?: (action: CustomAction<T>, item: T) => void;
|
|
89
|
-
onBulkAction?: (action:
|
|
96
|
+
onBulkAction?: (action: ViewBasedCustomBulkAction<T>, items: T[]) => void;
|
|
90
97
|
} = $props();
|
|
91
98
|
|
|
92
99
|
const icons = $derived(getIconSet() ?? defaultIconSet);
|
|
@@ -99,7 +106,9 @@
|
|
|
99
106
|
const updateLabel = $derived(update.label ?? strings.edit);
|
|
100
107
|
const readLabel = $derived(read.label ?? strings.view);
|
|
101
108
|
const showRowActions = $derived(allowRead || allowUpdate || allowDelete || actions.length > 0);
|
|
102
|
-
const allowSelection = $derived(
|
|
109
|
+
const allowSelection = $derived(
|
|
110
|
+
allowDelete || customBulkActions.some((action) => !isViewBasedBulkAction(action))
|
|
111
|
+
);
|
|
103
112
|
|
|
104
113
|
let selected = new SvelteSet<number>();
|
|
105
114
|
let pendingDeletion = $state<T[] | null>(null);
|
|
@@ -109,7 +118,33 @@
|
|
|
109
118
|
let visibleRows = $state<T[]>([]);
|
|
110
119
|
let exportQuery = $state<TableQuery | undefined>(undefined);
|
|
111
120
|
let exportPopoverEl: HTMLElement | undefined = $state();
|
|
121
|
+
let actionsPopoverEl: HTMLElement | undefined = $state();
|
|
112
122
|
const exportId = $props.id();
|
|
123
|
+
const actionsMenuId = `${exportId}-actions`;
|
|
124
|
+
|
|
125
|
+
// Collapses export/bulk-actions/delete into a single "Acciones" dropdown
|
|
126
|
+
// once the toolbar no longer fits its available width (small monitor, a
|
|
127
|
+
// browser window not maximized, many customBulkActions, ...) — measured
|
|
128
|
+
// against an identical but off-flow, always-uncollapsed clone rather than
|
|
129
|
+
// the visible row itself, since the visible row's own width changes
|
|
130
|
+
// depending on whether it's currently collapsed.
|
|
131
|
+
let toolbarEl: HTMLElement | undefined = $state();
|
|
132
|
+
let toolbarMeasureEl: HTMLElement | undefined = $state();
|
|
133
|
+
let toolbarCollapsed = $state(false);
|
|
134
|
+
|
|
135
|
+
$effect(() => {
|
|
136
|
+
if (!toolbarEl || !toolbarMeasureEl) return;
|
|
137
|
+
const el = toolbarEl;
|
|
138
|
+
const measureEl = toolbarMeasureEl;
|
|
139
|
+
const check = () => {
|
|
140
|
+
toolbarCollapsed = measureEl.scrollWidth > el.clientWidth + 1;
|
|
141
|
+
};
|
|
142
|
+
const observer = new ResizeObserver(check);
|
|
143
|
+
observer.observe(el);
|
|
144
|
+
observer.observe(measureEl);
|
|
145
|
+
check();
|
|
146
|
+
return () => observer.disconnect();
|
|
147
|
+
});
|
|
113
148
|
|
|
114
149
|
async function resolveExportRows(): Promise<T[]> {
|
|
115
150
|
if (!pagination) return visibleRows;
|
|
@@ -181,12 +216,12 @@
|
|
|
181
216
|
}
|
|
182
217
|
|
|
183
218
|
async function runBulkAction(action: CustomBulkAction<T>, items: T[]) {
|
|
184
|
-
if (
|
|
219
|
+
if (isViewBasedBulkAction(action)) return;
|
|
185
220
|
await runEndpointAction(action.endpoint, items);
|
|
186
221
|
}
|
|
187
222
|
|
|
188
223
|
function requestBulkAction(action: CustomBulkAction<T>, items: T[]) {
|
|
189
|
-
if (action.confirm) {
|
|
224
|
+
if (!isViewBasedBulkAction(action) && action.confirm) {
|
|
190
225
|
pendingBulkAction = { action, items };
|
|
191
226
|
} else {
|
|
192
227
|
runBulkAction(action, items);
|
|
@@ -195,7 +230,7 @@
|
|
|
195
230
|
|
|
196
231
|
function handleBulkAction(action: CustomBulkAction<T>) {
|
|
197
232
|
const items = selectedItems;
|
|
198
|
-
if (action
|
|
233
|
+
if (isViewBasedBulkAction(action)) {
|
|
199
234
|
onBulkAction?.(action, items);
|
|
200
235
|
return;
|
|
201
236
|
}
|
|
@@ -238,6 +273,97 @@
|
|
|
238
273
|
]);
|
|
239
274
|
</script>
|
|
240
275
|
|
|
276
|
+
{#snippet toolbarButtons(measuring: boolean)}
|
|
277
|
+
{#if search}
|
|
278
|
+
<SearchInput config={search} />
|
|
279
|
+
{/if}
|
|
280
|
+
|
|
281
|
+
{#if enableExport}
|
|
282
|
+
{@const ExportIcon = icons.download}
|
|
283
|
+
<Button
|
|
284
|
+
variant="ghost"
|
|
285
|
+
class="btn-square"
|
|
286
|
+
popovertarget={measuring ? undefined : `export-menu-${exportId}`}
|
|
287
|
+
style={measuring ? undefined : `anchor-name:--export-anchor-${exportId}`}
|
|
288
|
+
aria-label={strings.export}
|
|
289
|
+
title={strings.export}
|
|
290
|
+
>
|
|
291
|
+
<ExportIcon class="size-4" />
|
|
292
|
+
</Button>
|
|
293
|
+
{#if !measuring}
|
|
294
|
+
<div
|
|
295
|
+
popover="auto"
|
|
296
|
+
id="export-menu-{exportId}"
|
|
297
|
+
style="position-anchor:--export-anchor-{exportId}"
|
|
298
|
+
class="dropdown dropdown-end w-40 rounded-box border border-base-content/10 bg-base-100 p-1 shadow-lg"
|
|
299
|
+
bind:this={exportPopoverEl}
|
|
300
|
+
>
|
|
301
|
+
<Button
|
|
302
|
+
variant="ghost"
|
|
303
|
+
class="btn-sm w-full justify-start"
|
|
304
|
+
onclick={() => handleExport('csv')}
|
|
305
|
+
>
|
|
306
|
+
{strings.exportCsv}
|
|
307
|
+
</Button>
|
|
308
|
+
{#if xlsx}
|
|
309
|
+
<Button
|
|
310
|
+
variant="ghost"
|
|
311
|
+
class="btn-sm w-full justify-start"
|
|
312
|
+
onclick={() => handleExport('xlsx')}
|
|
313
|
+
>
|
|
314
|
+
{strings.exportExcel}
|
|
315
|
+
</Button>
|
|
316
|
+
{/if}
|
|
317
|
+
</div>
|
|
318
|
+
{/if}
|
|
319
|
+
{/if}
|
|
320
|
+
|
|
321
|
+
{#each customBulkActions as bulkAction, i (i)}
|
|
322
|
+
{#if bulkAction.condition?.(selectedItems) ?? true}
|
|
323
|
+
{@const BulkIcon = bulkAction.icon}
|
|
324
|
+
{@const isView = isViewBasedBulkAction(bulkAction)}
|
|
325
|
+
<Button
|
|
326
|
+
variant={bulkAction.variant ?? 'ghost'}
|
|
327
|
+
class="btn-outline"
|
|
328
|
+
disabled={!isView && selected.size === 0}
|
|
329
|
+
title={bulkAction.tooltip ?? bulkAction.label}
|
|
330
|
+
aria-label={bulkAction.tooltip ?? bulkAction.label}
|
|
331
|
+
onclick={measuring ? undefined : () => handleBulkAction(bulkAction)}
|
|
332
|
+
>
|
|
333
|
+
<BulkIcon class="size-4" />
|
|
334
|
+
{#if bulkAction.label}
|
|
335
|
+
{bulkAction.label}{#if !isView}
|
|
336
|
+
({selected.size})
|
|
337
|
+
{/if}
|
|
338
|
+
{/if}
|
|
339
|
+
</Button>
|
|
340
|
+
{/if}
|
|
341
|
+
{/each}
|
|
342
|
+
|
|
343
|
+
{#if allowDelete}
|
|
344
|
+
{@const DeleteIcon = icons.delete}
|
|
345
|
+
<Button
|
|
346
|
+
variant="error"
|
|
347
|
+
class="btn-outline"
|
|
348
|
+
disabled={selected.size === 0}
|
|
349
|
+
onclick={measuring ? undefined : handleDelete}
|
|
350
|
+
>
|
|
351
|
+
<DeleteIcon class="size-4" />
|
|
352
|
+
{deleteLabel} ({selected.size})
|
|
353
|
+
</Button>
|
|
354
|
+
{/if}
|
|
355
|
+
|
|
356
|
+
{@const CreateIcon = icons.create}
|
|
357
|
+
<Button variant="primary" onclick={measuring ? undefined : handleCreate}>
|
|
358
|
+
<CreateIcon class="size-5" />
|
|
359
|
+
{#if creation.label}
|
|
360
|
+
<span>{creation.label}</span>
|
|
361
|
+
{:else}
|
|
362
|
+
<span>{strings.create}<span class="hidden sm:inline"> {labelOne}</span></span>
|
|
363
|
+
{/if}
|
|
364
|
+
</Button>
|
|
365
|
+
{/snippet}
|
|
366
|
+
|
|
241
367
|
{#snippet actionsCell(item: T)}
|
|
242
368
|
<div class="flex justify-end items-center gap-1">
|
|
243
369
|
{#each rowActions as action (action.label)}
|
|
@@ -283,87 +409,116 @@
|
|
|
283
409
|
breadcrumbs={[{ label: labelMany, icon: entityIcon, link: { href: '#' }, prominent: true }]}
|
|
284
410
|
>
|
|
285
411
|
{#snippet buttons()}
|
|
286
|
-
{
|
|
287
|
-
|
|
288
|
-
|
|
412
|
+
<div bind:this={toolbarEl} class="flex min-w-0 flex-wrap items-center gap-2 overflow-hidden">
|
|
413
|
+
{#if toolbarCollapsed}
|
|
414
|
+
{#if search}
|
|
415
|
+
<SearchInput config={search} />
|
|
416
|
+
{/if}
|
|
289
417
|
|
|
290
|
-
|
|
291
|
-
{@const ExportIcon = icons.download}
|
|
292
|
-
<Button
|
|
293
|
-
variant="ghost"
|
|
294
|
-
class="btn-square"
|
|
295
|
-
popovertarget="export-menu-{exportId}"
|
|
296
|
-
style="anchor-name:--export-anchor-{exportId}"
|
|
297
|
-
aria-label={strings.export}
|
|
298
|
-
title={strings.export}
|
|
299
|
-
>
|
|
300
|
-
<ExportIcon class="size-4" />
|
|
301
|
-
</Button>
|
|
302
|
-
<div
|
|
303
|
-
popover="auto"
|
|
304
|
-
id="export-menu-{exportId}"
|
|
305
|
-
style="position-anchor:--export-anchor-{exportId}"
|
|
306
|
-
class="dropdown dropdown-end w-40 rounded-box border border-base-content/10 bg-base-100 p-1 shadow-lg"
|
|
307
|
-
bind:this={exportPopoverEl}
|
|
308
|
-
>
|
|
418
|
+
{@const CreateIcon = icons.create}
|
|
309
419
|
<Button
|
|
310
420
|
variant="ghost"
|
|
311
|
-
class="btn-
|
|
312
|
-
|
|
421
|
+
class="btn-square"
|
|
422
|
+
popovertarget="actions-menu-{actionsMenuId}"
|
|
423
|
+
style="anchor-name:--actions-anchor-{actionsMenuId}"
|
|
424
|
+
aria-label={strings.actions}
|
|
425
|
+
title={strings.actions}
|
|
313
426
|
>
|
|
314
|
-
|
|
427
|
+
<span class="text-lg leading-none" aria-hidden="true">⋯</span>
|
|
315
428
|
</Button>
|
|
316
|
-
|
|
429
|
+
<div
|
|
430
|
+
popover="auto"
|
|
431
|
+
id="actions-menu-{actionsMenuId}"
|
|
432
|
+
style="position-anchor:--actions-anchor-{actionsMenuId}"
|
|
433
|
+
class="dropdown dropdown-end w-56 rounded-box border border-base-content/10 bg-base-100 p-1 shadow-lg"
|
|
434
|
+
bind:this={actionsPopoverEl}
|
|
435
|
+
>
|
|
317
436
|
<Button
|
|
318
437
|
variant="ghost"
|
|
319
438
|
class="btn-sm w-full justify-start"
|
|
320
|
-
onclick={() =>
|
|
439
|
+
onclick={() => {
|
|
440
|
+
actionsPopoverEl?.hidePopover();
|
|
441
|
+
handleCreate();
|
|
442
|
+
}}
|
|
321
443
|
>
|
|
322
|
-
|
|
444
|
+
<CreateIcon class="size-4" />
|
|
445
|
+
{creation.label || `${strings.create} ${labelOne}`}
|
|
323
446
|
</Button>
|
|
324
|
-
{/if}
|
|
325
|
-
</div>
|
|
326
|
-
{/if}
|
|
327
447
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
448
|
+
{#if allowDelete}
|
|
449
|
+
<Button
|
|
450
|
+
variant="ghost"
|
|
451
|
+
class="btn-sm w-full justify-start text-error"
|
|
452
|
+
disabled={selected.size === 0}
|
|
453
|
+
onclick={() => {
|
|
454
|
+
actionsPopoverEl?.hidePopover();
|
|
455
|
+
handleDelete();
|
|
456
|
+
}}
|
|
457
|
+
>
|
|
458
|
+
{deleteLabel} ({selected.size})
|
|
459
|
+
</Button>
|
|
340
460
|
{/if}
|
|
341
|
-
</Button>
|
|
342
|
-
{/if}
|
|
343
|
-
{/each}
|
|
344
461
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
462
|
+
{#if enableExport}
|
|
463
|
+
<Button
|
|
464
|
+
variant="ghost"
|
|
465
|
+
class="btn-sm w-full justify-start"
|
|
466
|
+
onclick={() => {
|
|
467
|
+
actionsPopoverEl?.hidePopover();
|
|
468
|
+
handleExport('csv');
|
|
469
|
+
}}
|
|
470
|
+
>
|
|
471
|
+
{strings.exportCsv}
|
|
472
|
+
</Button>
|
|
473
|
+
{#if xlsx}
|
|
474
|
+
<Button
|
|
475
|
+
variant="ghost"
|
|
476
|
+
class="btn-sm w-full justify-start"
|
|
477
|
+
onclick={() => {
|
|
478
|
+
actionsPopoverEl?.hidePopover();
|
|
479
|
+
handleExport('xlsx');
|
|
480
|
+
}}
|
|
481
|
+
>
|
|
482
|
+
{strings.exportExcel}
|
|
483
|
+
</Button>
|
|
484
|
+
{/if}
|
|
485
|
+
{/if}
|
|
357
486
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
487
|
+
{#each customBulkActions as bulkAction, i (i)}
|
|
488
|
+
{#if bulkAction.condition?.(selectedItems) ?? true}
|
|
489
|
+
{@const BulkIcon = bulkAction.icon}
|
|
490
|
+
{@const isView = isViewBasedBulkAction(bulkAction)}
|
|
491
|
+
<Button
|
|
492
|
+
variant="ghost"
|
|
493
|
+
class="btn-sm w-full justify-start"
|
|
494
|
+
disabled={!isView && selected.size === 0}
|
|
495
|
+
title={bulkAction.tooltip}
|
|
496
|
+
onclick={() => {
|
|
497
|
+
actionsPopoverEl?.hidePopover();
|
|
498
|
+
handleBulkAction(bulkAction);
|
|
499
|
+
}}
|
|
500
|
+
>
|
|
501
|
+
<BulkIcon class="size-4" />
|
|
502
|
+
{bulkAction.label ?? bulkAction.tooltip}{#if !isView}
|
|
503
|
+
({selected.size})
|
|
504
|
+
{/if}
|
|
505
|
+
</Button>
|
|
506
|
+
{/if}
|
|
507
|
+
{/each}
|
|
508
|
+
</div>
|
|
363
509
|
{:else}
|
|
364
|
-
|
|
510
|
+
{@render toolbarButtons(false)}
|
|
365
511
|
{/if}
|
|
366
|
-
</
|
|
512
|
+
</div>
|
|
513
|
+
|
|
514
|
+
<div
|
|
515
|
+
bind:this={toolbarMeasureEl}
|
|
516
|
+
class="pointer-events-none invisible fixed flex items-center gap-2 whitespace-nowrap"
|
|
517
|
+
style="left:-9999px; top:-9999px; width:max-content;"
|
|
518
|
+
aria-hidden="true"
|
|
519
|
+
>
|
|
520
|
+
{@render toolbarButtons(true)}
|
|
521
|
+
</div>
|
|
367
522
|
{/snippet}
|
|
368
523
|
</Header>
|
|
369
524
|
|
|
@@ -400,8 +555,10 @@
|
|
|
400
555
|
{/if}
|
|
401
556
|
|
|
402
557
|
{#if pendingBulkAction !== null}
|
|
403
|
-
|
|
404
|
-
|
|
558
|
+
{@const pendingActionLabel =
|
|
559
|
+
pendingBulkAction.action.label ?? pendingBulkAction.action.tooltip ?? strings.actions}
|
|
560
|
+
<Modal title={pendingActionLabel} onClose={() => (pendingBulkAction = null)}>
|
|
561
|
+
<p>{strings.deleteConfirm(pendingBulkAction.items.length, pendingActionLabel)}</p>
|
|
405
562
|
<div class="flex justify-end gap-2 mt-4">
|
|
406
563
|
<Button variant="ghost" onclick={() => (pendingBulkAction = null)}>
|
|
407
564
|
{strings.cancel}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type XlsxModule } from '../../table/export.js';
|
|
2
|
-
import type { ActionConfiguration, ColumnDefinition, CustomAction, CustomBulkAction, SearchConfiguration } from '../../../types/crud.js';
|
|
2
|
+
import type { ActionConfiguration, ColumnDefinition, CustomAction, CustomBulkAction, SearchConfiguration, ViewBasedCustomBulkAction } from '../../../types/crud.js';
|
|
3
3
|
import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../../types/table.js';
|
|
4
4
|
declare function $$render<T extends object = Record<string, unknown>>(): {
|
|
5
5
|
props: {
|
|
@@ -36,7 +36,7 @@ declare function $$render<T extends object = Record<string, unknown>>(): {
|
|
|
36
36
|
onEdit?: (item: T) => void;
|
|
37
37
|
onView?: (item: T) => void;
|
|
38
38
|
onAction?: (action: CustomAction<T>, item: T) => void;
|
|
39
|
-
onBulkAction?: (action:
|
|
39
|
+
onBulkAction?: (action: ViewBasedCustomBulkAction<T>, items: T[]) => void;
|
|
40
40
|
};
|
|
41
41
|
exports: {};
|
|
42
42
|
bindings: "";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type { BreadcrumbItem } from './types/breadcrumb.js';
|
|
|
2
2
|
export { AttributeType } from './types/attribute.js';
|
|
3
3
|
export type { AttributeMetadata, InterfaceMetadata, SelectOption, OptionsResolver, FormatterResolver, EmbeddedItemLabelResolver } from './types/attribute.js';
|
|
4
4
|
export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry, PaginatedEnvelope, ServerPagination, FilterSnapshot, TableQuery } from './types/table.js';
|
|
5
|
-
export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, CustomBulkAction, RowAction, SearchConfiguration } from './types/crud.js';
|
|
5
|
+
export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, BaseCustomBulkAction, ViewBasedCustomBulkAction, EndpointBasedCustomBulkAction, CustomBulkAction, RowAction, SearchConfiguration } from './types/crud.js';
|
|
6
6
|
export type { RuneforgeConfig } from './config/context.js';
|
|
7
7
|
export { setConfig, getConfig } from './config/context.js';
|
|
8
8
|
export type { CRUDIconSet, IconComponent } from './icons/types.js';
|
package/dist/types/crud.d.ts
CHANGED
|
@@ -99,24 +99,44 @@ export interface RowAction<T extends object = Record<string, unknown>> {
|
|
|
99
99
|
condition?: (item: T) => boolean;
|
|
100
100
|
run: (item: T) => void;
|
|
101
101
|
}
|
|
102
|
-
export interface
|
|
103
|
-
|
|
102
|
+
export interface BaseCustomBulkAction<T extends object = Record<string, unknown>> {
|
|
103
|
+
/** Visible text on the button. Omit for an icon-only button — pass
|
|
104
|
+
* `tooltip` in that case so the action still has an accessible name and a
|
|
105
|
+
* hover hint, since `label` is what a collapsed "Acciones" menu item and
|
|
106
|
+
* the button's default `title`/`aria-label` fall back to otherwise. */
|
|
107
|
+
label?: string;
|
|
104
108
|
icon: any;
|
|
105
|
-
/**
|
|
106
|
-
|
|
109
|
+
/** Hover title shown on the button. Defaults to `label`. Set this
|
|
110
|
+
* explicitly when `label` is omitted (icon-only button) so there's still
|
|
111
|
+
* a hint on hover and an accessible name. */
|
|
112
|
+
tooltip?: string;
|
|
107
113
|
variant?: string;
|
|
108
|
-
confirm?: boolean;
|
|
109
114
|
condition?: (items: T[]) => boolean;
|
|
115
|
+
}
|
|
116
|
+
export interface ViewBasedCustomBulkAction<T extends object = Record<string, unknown>> extends BaseCustomBulkAction<T> {
|
|
117
|
+
kind: 'view';
|
|
110
118
|
/** Renders as a modal-like panel when the action runs, instead of POSTing
|
|
111
|
-
* to
|
|
112
|
-
* `endpoint` — provide exactly one of the two, mirroring `CustomAction.view`.
|
|
119
|
+
* to an endpoint once per selected item, mirroring `CustomAction.view`.
|
|
113
120
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
* A `view` action never requires a pre-existing selection: the button is
|
|
122
|
+
* enabled even with nothing selected. The component receives the current
|
|
123
|
+
* selection as `items` (possibly empty). */
|
|
124
|
+
view: Component<any>;
|
|
125
|
+
}
|
|
126
|
+
export interface EndpointBasedCustomBulkAction<T extends object = Record<string, unknown>> extends BaseCustomBulkAction<T> {
|
|
127
|
+
kind: 'endpoint';
|
|
128
|
+
/** POSTed once per selected item (`id` set in the FormData body), then
|
|
129
|
+
* `invalidateAll()`s. */
|
|
130
|
+
endpoint: string;
|
|
131
|
+
confirm?: boolean;
|
|
119
132
|
}
|
|
133
|
+
/** A toolbar action operating on the current selection — either a `view`
|
|
134
|
+
* (opens a modal-like panel, e.g. an import wizard) or an `endpoint`
|
|
135
|
+
* (POSTed once per selected item). The required `kind` discriminant picks
|
|
136
|
+
* which one: `'view'` brings `view` and forbids `endpoint`/`confirm`;
|
|
137
|
+
* `'endpoint'` brings `endpoint`/`confirm` and forbids `view` — see
|
|
138
|
+
* `ViewBasedCustomBulkAction` / `EndpointBasedCustomBulkAction`. */
|
|
139
|
+
export type CustomBulkAction<T extends object = Record<string, unknown>> = ViewBasedCustomBulkAction<T> | EndpointBasedCustomBulkAction<T>;
|
|
120
140
|
export interface SearchConfiguration {
|
|
121
141
|
param?: string;
|
|
122
142
|
placeholder?: string;
|