runeforge 0.0.53 → 0.0.54
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/README.md +128 -26
- package/dist/components/crud/GenericCRUD.svelte +12 -22
- package/dist/components/crud/GenericCRUD.svelte.d.ts +6 -9
- package/dist/components/crud/utils/reorder.d.ts +4 -0
- package/dist/components/crud/utils/reorder.js +13 -0
- package/dist/components/crud/views/list/List.svelte +291 -0
- package/dist/components/crud/views/{List.svelte.d.ts → list/List.svelte.d.ts} +4 -14
- package/dist/components/crud/views/list/Modals.svelte +56 -0
- package/dist/components/crud/views/list/Modals.svelte.d.ts +36 -0
- package/dist/components/crud/views/list/Table.svelte +176 -0
- package/dist/components/crud/views/list/Table.svelte.d.ts +59 -0
- package/dist/components/crud/views/list/Toolbar.svelte +341 -0
- package/dist/components/crud/views/list/Toolbar.svelte.d.ts +46 -0
- package/dist/components/table/PaginatedTable.svelte +361 -16
- package/dist/components/table/PaginatedTable.svelte.d.ts +11 -2
- package/dist/components/table/TableBody.svelte +83 -3
- package/dist/components/table/TableBody.svelte.d.ts +14 -1
- package/dist/components/table/TableHeader.svelte +9 -4
- package/dist/components/table/TableHeader.svelte.d.ts +1 -0
- package/dist/components/table/sortable.d.ts +27 -0
- package/dist/components/table/sortable.js +55 -0
- package/dist/components/table/utils.d.ts +19 -1
- package/dist/components/table/utils.js +40 -0
- package/dist/i18n/en.js +2 -0
- package/dist/i18n/es.js +2 -0
- package/dist/i18n/types.d.ts +2 -0
- package/dist/icons/defaults/Grip.svelte +7 -0
- package/dist/icons/defaults/Grip.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 +3 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/types/crud.d.ts +70 -1
- package/dist/types/table.d.ts +56 -0
- package/package.json +3 -1
- package/dist/components/crud/views/List.svelte +0 -584
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
import type { SortableModule } from '../../types/table.js';
|
|
3
|
+
/** The indices SortableJS reports a drag settled at — `oldIndex`/`newIndex`
|
|
4
|
+
* for a single-row drag, `oldIndicies`/`newIndicies` (one entry per row)
|
|
5
|
+
* instead when `multiDrag` moved a whole selection together. */
|
|
6
|
+
export interface SortEndIndices {
|
|
7
|
+
oldIndex?: number;
|
|
8
|
+
newIndex?: number;
|
|
9
|
+
oldIndicies: number[];
|
|
10
|
+
newIndicies: number[];
|
|
11
|
+
}
|
|
12
|
+
export interface SortableRowsParams {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** The resolved `sortablejs` module — required whenever `enabled` is true.
|
|
15
|
+
* Runeforge never imports `sortablejs` itself; the caller supplies it. */
|
|
16
|
+
sortable?: SortableModule;
|
|
17
|
+
multiDrag?: boolean;
|
|
18
|
+
onStart?: () => void;
|
|
19
|
+
onEnd?: (indices: SortEndIndices) => void;
|
|
20
|
+
}
|
|
21
|
+
/** Mounts SortableJS on a `<tbody>` to drive drag-to-reorder, restricting
|
|
22
|
+
* drag initiation to elements carrying `data-reorder-handle`. The settled
|
|
23
|
+
* order is computed from SortableJS's own before/after indices (see
|
|
24
|
+
* TableBody) rather than read back from the DOM — `multiDrag` in particular
|
|
25
|
+
* doesn't reliably leave the DOM in the dropped state, only its event data
|
|
26
|
+
* reflects it correctly. */
|
|
27
|
+
export declare const sortableRows: Action<HTMLElement, SortableRowsParams>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// A real Tailwind utility (not a hand-rolled class) so it's picked up by the
|
|
2
|
+
// consuming project's own Tailwind build — see the README's "Tailwind source
|
|
3
|
+
// scanning" note about `@source`-ing this package's `dist` output.
|
|
4
|
+
const MULTI_DRAG_SELECTED_CLASS = 'bg-primary/10';
|
|
5
|
+
/** Mounts SortableJS on a `<tbody>` to drive drag-to-reorder, restricting
|
|
6
|
+
* drag initiation to elements carrying `data-reorder-handle`. The settled
|
|
7
|
+
* order is computed from SortableJS's own before/after indices (see
|
|
8
|
+
* TableBody) rather than read back from the DOM — `multiDrag` in particular
|
|
9
|
+
* doesn't reliably leave the DOM in the dropped state, only its event data
|
|
10
|
+
* reflects it correctly. */
|
|
11
|
+
export const sortableRows = (node, params) => {
|
|
12
|
+
let instance;
|
|
13
|
+
function setup(p) {
|
|
14
|
+
instance?.destroy();
|
|
15
|
+
instance = undefined;
|
|
16
|
+
if (!p.enabled || !p.sortable)
|
|
17
|
+
return;
|
|
18
|
+
instance = p.sortable.create(node, {
|
|
19
|
+
handle: '[data-reorder-handle]',
|
|
20
|
+
animation: 150,
|
|
21
|
+
// Mouse/touch-simulated dragging instead of native HTML5 DnD: avoids
|
|
22
|
+
// native drag-ghost/Firefox quirks and (usefully) makes drags
|
|
23
|
+
// reproducible with plain mouse events in end-to-end tests. Not used
|
|
24
|
+
// together with `multiDrag`, though — SortableJS's MultiDrag plugin
|
|
25
|
+
// relies on native HTML5 DnD to track a multi-item drag correctly;
|
|
26
|
+
// paired with `forceFallback` it reports the drop as a no-op.
|
|
27
|
+
forceFallback: !p.multiDrag,
|
|
28
|
+
// Keeps the floating drag clone out of the `<tbody>` — otherwise it's
|
|
29
|
+
// an extra element carrying the same `data-row-key` as the row it was
|
|
30
|
+
// cloned from, which could confuse the multi-drag selection sync query.
|
|
31
|
+
fallbackOnBody: true,
|
|
32
|
+
multiDrag: p.multiDrag || undefined,
|
|
33
|
+
selectedClass: p.multiDrag ? MULTI_DRAG_SELECTED_CLASS : undefined,
|
|
34
|
+
onStart: () => p.onStart?.(),
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
onEnd: (evt) => {
|
|
37
|
+
p.onEnd?.({
|
|
38
|
+
oldIndex: evt.oldIndex,
|
|
39
|
+
newIndex: evt.newIndex,
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
oldIndicies: (evt.oldIndicies ?? []).map((e) => e.index),
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
newIndicies: (evt.newIndicies ?? []).map((e) => e.index),
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
setup(params);
|
|
49
|
+
return {
|
|
50
|
+
update: setup,
|
|
51
|
+
destroy() {
|
|
52
|
+
instance?.destroy();
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
};
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import type { ColumnDefinition } from '../../types/crud.js';
|
|
2
|
-
import type { DistinctEntry } from '../../types/table.js';
|
|
2
|
+
import type { DistinctEntry, IndexedRow } from '../../types/table.js';
|
|
3
3
|
export declare function cellRenderedText<T extends object>(row: T, col: ColumnDefinition<T>): string;
|
|
4
4
|
export declare function isSortable<T extends object>(col: ColumnDefinition<T>): boolean;
|
|
5
5
|
export declare function isFilterable<T extends object>(col: ColumnDefinition<T>): boolean;
|
|
6
6
|
export declare function compare(a: unknown, b: unknown): number;
|
|
7
7
|
export declare function distinctEntries<T extends object>(data: T[], columns: ColumnDefinition<T>[]): Record<string, DistinctEntry<T>[]>;
|
|
8
|
+
/** Returns a copy of `rows` with the item at `from` moved to `to` — pure
|
|
9
|
+
* array surgery, handy for building custom reorder UIs on `PaginatedTable`.
|
|
10
|
+
* Out-of-range indices are a no-op. */
|
|
11
|
+
export declare function moveIndexedRow<T extends object>(rows: IndexedRow<T>[], from: number, to: number): IndexedRow<T>[];
|
|
12
|
+
/** Generalizes `moveIndexedRow` to moving a *set* of items (SortableJS's
|
|
13
|
+
* `multiDrag`) to `toIndex` in one go, preserving their relative order.
|
|
14
|
+
* `toIndex` is a position in the *result* array, same convention as
|
|
15
|
+
* `moveIndexedRow`'s `to` (and SortableJS's own `newIndex`) — not an index
|
|
16
|
+
* into the pre-move array. Duplicate/out-of-range `fromIndices` are ignored;
|
|
17
|
+
* an empty valid set is a no-op. */
|
|
18
|
+
export declare function moveIndexedRows<T extends object>(rows: IndexedRow<T>[], fromIndices: number[], toIndex: number): IndexedRow<T>[];
|
|
19
|
+
/** Resolves the comparator establishing reorder mode's row order: `compare`
|
|
20
|
+
* when given (composite orders — a related record's order first, then this
|
|
21
|
+
* row's own `attribute`), otherwise plain ascending by `attribute`. */
|
|
22
|
+
export declare function resolveReorderComparator<T extends object>(reorder: {
|
|
23
|
+
attribute: keyof T & string;
|
|
24
|
+
compare?: (a: T, b: T) => number;
|
|
25
|
+
}): (a: T, b: T) => number;
|
|
@@ -42,3 +42,43 @@ export function distinctEntries(data, columns) {
|
|
|
42
42
|
}
|
|
43
43
|
return result;
|
|
44
44
|
}
|
|
45
|
+
/** Returns a copy of `rows` with the item at `from` moved to `to` — pure
|
|
46
|
+
* array surgery, handy for building custom reorder UIs on `PaginatedTable`.
|
|
47
|
+
* Out-of-range indices are a no-op. */
|
|
48
|
+
export function moveIndexedRow(rows, from, to) {
|
|
49
|
+
if (from === to || from < 0 || to < 0 || from >= rows.length || to >= rows.length)
|
|
50
|
+
return rows;
|
|
51
|
+
const next = [...rows];
|
|
52
|
+
const [moved] = next.splice(from, 1);
|
|
53
|
+
next.splice(to, 0, moved);
|
|
54
|
+
return next;
|
|
55
|
+
}
|
|
56
|
+
/** Generalizes `moveIndexedRow` to moving a *set* of items (SortableJS's
|
|
57
|
+
* `multiDrag`) to `toIndex` in one go, preserving their relative order.
|
|
58
|
+
* `toIndex` is a position in the *result* array, same convention as
|
|
59
|
+
* `moveIndexedRow`'s `to` (and SortableJS's own `newIndex`) — not an index
|
|
60
|
+
* into the pre-move array. Duplicate/out-of-range `fromIndices` are ignored;
|
|
61
|
+
* an empty valid set is a no-op. */
|
|
62
|
+
export function moveIndexedRows(rows, fromIndices, toIndex) {
|
|
63
|
+
const validFrom = [...new Set(fromIndices)]
|
|
64
|
+
.filter((i) => i >= 0 && i < rows.length)
|
|
65
|
+
.sort((a, b) => a - b);
|
|
66
|
+
if (validFrom.length === 0)
|
|
67
|
+
return rows;
|
|
68
|
+
const fromSet = new Set(validFrom);
|
|
69
|
+
const moved = validFrom.map((i) => rows[i]);
|
|
70
|
+
const remaining = rows.filter((_, i) => !fromSet.has(i));
|
|
71
|
+
const insertAt = Math.min(remaining.length, Math.max(0, toIndex));
|
|
72
|
+
const next = [...remaining];
|
|
73
|
+
next.splice(insertAt, 0, ...moved);
|
|
74
|
+
return next;
|
|
75
|
+
}
|
|
76
|
+
/** Resolves the comparator establishing reorder mode's row order: `compare`
|
|
77
|
+
* when given (composite orders — a related record's order first, then this
|
|
78
|
+
* row's own `attribute`), otherwise plain ascending by `attribute`. */
|
|
79
|
+
export function resolveReorderComparator(reorder) {
|
|
80
|
+
if (reorder.compare)
|
|
81
|
+
return reorder.compare;
|
|
82
|
+
const { attribute } = reorder;
|
|
83
|
+
return (a, b) => compare(a[attribute], b[attribute]);
|
|
84
|
+
}
|
package/dist/i18n/en.js
CHANGED
package/dist/i18n/es.js
CHANGED
|
@@ -20,6 +20,8 @@ export const es = {
|
|
|
20
20
|
delete: 'Eliminar',
|
|
21
21
|
create: 'Crear',
|
|
22
22
|
searchPlaceholder: 'Buscar...',
|
|
23
|
+
reorder: 'Arrastrar para reordenar',
|
|
24
|
+
reorderPage: (page) => `Pág ${page}`,
|
|
23
25
|
export: 'Exportar',
|
|
24
26
|
exportCsv: 'Exportar a CSV',
|
|
25
27
|
exportExcel: 'Exportar a Excel',
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { size = '1em', class: cls = '' }: { size?: string | number; class?: string } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16" fill="currentColor" class={cls}>
|
|
6
|
+
<path d="M7 2a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0M7 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m-3 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0m3 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
|
|
7
|
+
</svg>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* setIconSet(bootstrapIconSet);
|
|
11
11
|
*/
|
|
12
12
|
import * as Icons from 'svelte-bootstrap-icons';
|
|
13
|
-
const { ChevronExpand, CaretUpFill, CaretDownFill, Funnel, FunnelFill, Plus, Eye, PencilSquare, Trash3, HouseDoor, Folder, EyeSlash, X, Download, } = Icons;
|
|
13
|
+
const { ChevronExpand, CaretUpFill, CaretDownFill, Funnel, FunnelFill, Plus, Eye, PencilSquare, Trash3, HouseDoor, Folder, EyeSlash, X, Download, GripVertical, } = Icons;
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
15
|
function asIcon(c) { return c; }
|
|
16
16
|
export const bootstrapIconSet = {
|
|
@@ -29,6 +29,7 @@ export const bootstrapIconSet = {
|
|
|
29
29
|
passwordShow: asIcon(Eye),
|
|
30
30
|
passwordHide: asIcon(EyeSlash),
|
|
31
31
|
download: asIcon(Download),
|
|
32
|
+
grip: asIcon(GripVertical),
|
|
32
33
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
34
|
getByName: (name) => asIcon(Icons[name]) ?? null,
|
|
34
35
|
};
|
|
@@ -13,6 +13,7 @@ import Folder from '../defaults/Folder.svelte';
|
|
|
13
13
|
import PasswordShow from '../defaults/PasswordShow.svelte';
|
|
14
14
|
import PasswordHide from '../defaults/PasswordHide.svelte';
|
|
15
15
|
import Download from '../defaults/Download.svelte';
|
|
16
|
+
import Grip from '../defaults/Grip.svelte';
|
|
16
17
|
export const defaultIconSet = {
|
|
17
18
|
sortNone: SortNone,
|
|
18
19
|
sortAsc: SortAsc,
|
|
@@ -29,4 +30,5 @@ export const defaultIconSet = {
|
|
|
29
30
|
passwordShow: PasswordShow,
|
|
30
31
|
passwordHide: PasswordHide,
|
|
31
32
|
download: Download,
|
|
33
|
+
grip: Grip,
|
|
32
34
|
};
|
package/dist/icons/types.d.ts
CHANGED
|
@@ -19,5 +19,8 @@ export interface CRUDIconSet {
|
|
|
19
19
|
passwordShow: IconComponent;
|
|
20
20
|
passwordHide: IconComponent;
|
|
21
21
|
download: IconComponent;
|
|
22
|
+
/** Drag handle shown at the start of each row when list reordering is
|
|
23
|
+
* enabled. Optional so existing custom icon sets keep compiling. */
|
|
24
|
+
grip?: IconComponent;
|
|
22
25
|
getByName?: (name: string) => IconComponent | null;
|
|
23
26
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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, EmbeddedItemLabelResolver } from './types/attribute.js';
|
|
4
|
-
export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry, PaginatedEnvelope, ServerPagination, FilterSnapshot, TableQuery } from './types/table.js';
|
|
5
|
-
export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, BaseCustomBulkAction, ViewBasedCustomBulkAction, EndpointBasedCustomBulkAction, CustomBulkAction, RowAction, SearchConfiguration } from './types/crud.js';
|
|
4
|
+
export type { CellProps, CellComponent, CellFormatter, SortDirection, IndexedRow, DistinctEntry, PaginatedEnvelope, ServerPagination, FilterSnapshot, TableQuery, ReorderOptions } from './types/table.js';
|
|
5
|
+
export type { ColumnDefinition, FieldDefinition, ActionConfiguration, CustomAction, BaseCustomBulkAction, ViewBasedCustomBulkAction, EndpointBasedCustomBulkAction, CustomBulkAction, RowAction, SearchConfiguration, ExportConfiguration, ReorderConfiguration, ListActions, ListConfig } 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';
|
|
@@ -31,14 +31,14 @@ export { default as SortHeader } from './components/table/SortHeader.svelte';
|
|
|
31
31
|
export { default as Paginator } from './components/table/Paginator.svelte';
|
|
32
32
|
export { default as ColumnFilter } from './components/table/ColumnFilter.svelte';
|
|
33
33
|
export { SortState, FilterState, snapshotFilter } from './components/table/state.svelte.js';
|
|
34
|
-
export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries } from './components/table/utils.js';
|
|
34
|
+
export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries, moveIndexedRow } from './components/table/utils.js';
|
|
35
35
|
export { default as GenericCRUD } from './components/crud/GenericCRUD.svelte';
|
|
36
36
|
export { default as Field } from './components/crud/Field.svelte';
|
|
37
37
|
export { default as EmbeddedField } from './components/crud/EmbeddedField.svelte';
|
|
38
38
|
export { default as Header } from './components/common/Header.svelte';
|
|
39
39
|
export { default as AvatarCell } from './components/crud/columns/Avatar.svelte';
|
|
40
40
|
export { default as IconCell } from './components/crud/columns/Icon.svelte';
|
|
41
|
-
export { default as CRUDList } from './components/crud/views/List.svelte';
|
|
41
|
+
export { default as CRUDList } from './components/crud/views/list/List.svelte';
|
|
42
42
|
export { default as CRUDCreate } from './components/crud/views/Create.svelte';
|
|
43
43
|
export { default as CRUDRead } from './components/crud/views/Read.svelte';
|
|
44
44
|
export { default as CRUDUpdate } from './components/crud/views/Update.svelte';
|
|
@@ -50,5 +50,6 @@ export { formatBoolean, formatDatetime, formatInstance } from './components/crud
|
|
|
50
50
|
export { groupFields } from './components/crud/utils/grouping.js';
|
|
51
51
|
export type { FieldGroup } from './components/crud/utils/grouping.js';
|
|
52
52
|
export { validateAll } from './components/crud/utils/validation.js';
|
|
53
|
+
export { computeReorderChanges } from './components/crud/utils/reorder.js';
|
|
53
54
|
export { emptyRecord, defaultItemLabel } from './components/crud/utils/embedded.js';
|
|
54
55
|
export { buildChildrenByParent, collectDescendantIds } from './components/crud/utils/tree.js';
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ export { default as SortHeader } from './components/table/SortHeader.svelte';
|
|
|
27
27
|
export { default as Paginator } from './components/table/Paginator.svelte';
|
|
28
28
|
export { default as ColumnFilter } from './components/table/ColumnFilter.svelte';
|
|
29
29
|
export { SortState, FilterState, snapshotFilter } from './components/table/state.svelte.js';
|
|
30
|
-
export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries } from './components/table/utils.js';
|
|
30
|
+
export { cellRenderedText, isSortable, isFilterable, compare, distinctEntries, moveIndexedRow } from './components/table/utils.js';
|
|
31
31
|
// CRUD components
|
|
32
32
|
export { default as GenericCRUD } from './components/crud/GenericCRUD.svelte';
|
|
33
33
|
export { default as Field } from './components/crud/Field.svelte';
|
|
@@ -35,7 +35,7 @@ export { default as EmbeddedField } from './components/crud/EmbeddedField.svelte
|
|
|
35
35
|
export { default as Header } from './components/common/Header.svelte';
|
|
36
36
|
export { default as AvatarCell } from './components/crud/columns/Avatar.svelte';
|
|
37
37
|
export { default as IconCell } from './components/crud/columns/Icon.svelte';
|
|
38
|
-
export { default as CRUDList } from './components/crud/views/List.svelte';
|
|
38
|
+
export { default as CRUDList } from './components/crud/views/list/List.svelte';
|
|
39
39
|
export { default as CRUDCreate } from './components/crud/views/Create.svelte';
|
|
40
40
|
export { default as CRUDRead } from './components/crud/views/Read.svelte';
|
|
41
41
|
export { default as CRUDUpdate } from './components/crud/views/Update.svelte';
|
|
@@ -47,5 +47,6 @@ export { fieldLabel, initials } from './components/crud/utils/misc.js';
|
|
|
47
47
|
export { formatBoolean, formatDatetime, formatInstance } from './components/crud/utils/formatters.js';
|
|
48
48
|
export { groupFields } from './components/crud/utils/grouping.js';
|
|
49
49
|
export { validateAll } from './components/crud/utils/validation.js';
|
|
50
|
+
export { computeReorderChanges } from './components/crud/utils/reorder.js';
|
|
50
51
|
export { emptyRecord, defaultItemLabel } from './components/crud/utils/embedded.js';
|
|
51
52
|
export { buildChildrenByParent, collectDescendantIds } from './components/crud/utils/tree.js';
|
package/dist/types/crud.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
2
|
import type { FullAutoFill } from 'svelte/elements';
|
|
3
3
|
import type { AttributeType, SearchResolver, RequiredResolver, SelectOption } from './attribute.js';
|
|
4
|
-
import type { CellComponent, CellFormatter } from './table.js';
|
|
4
|
+
import type { CellComponent, CellFormatter, SortableModule, TableQuery } from './table.js';
|
|
5
|
+
import type { XlsxModule } from '../components/table/export.js';
|
|
5
6
|
export type ColumnDefinition<T extends object = Record<string, unknown>> = {
|
|
6
7
|
[K in keyof T & string]: {
|
|
7
8
|
attribute: K;
|
|
@@ -182,3 +183,71 @@ export interface SearchConfiguration {
|
|
|
182
183
|
placeholder?: string;
|
|
183
184
|
debounceMs?: number;
|
|
184
185
|
}
|
|
186
|
+
export interface ExportConfiguration<T extends object = Record<string, unknown>> {
|
|
187
|
+
callback?: (query: TableQuery) => Promise<T[]>;
|
|
188
|
+
xlsx?: XlsxModule;
|
|
189
|
+
}
|
|
190
|
+
/** Turns on drag-to-reorder rows in the list view — off by default, enabled
|
|
191
|
+
* by providing this object (`enabled: false` keeps the configuration in
|
|
192
|
+
* place but disables dragging, e.g. temporarily). Not supported alongside
|
|
193
|
+
* [server-side pagination](#server-side-pagination-sorting--filtering): the
|
|
194
|
+
* whole row set needs to be reachable client-side for indices to stay
|
|
195
|
+
* meaningful, so `reorder` is ignored whenever a `PaginatedEnvelope` is used. */
|
|
196
|
+
export interface ReorderConfiguration<T extends object = Record<string, unknown>> {
|
|
197
|
+
enabled?: boolean;
|
|
198
|
+
/** The attribute that stores each row's order index. Establishes the
|
|
199
|
+
* default ascending order (when `compare` is absent) and is written to
|
|
200
|
+
* its new sequential 0-based value on every row whose position changes
|
|
201
|
+
* after a drag. */
|
|
202
|
+
attribute: keyof T & string;
|
|
203
|
+
/** Resolved `sortablejs` default export, e.g. `import Sortable from
|
|
204
|
+
* 'sortablejs'`. Runeforge never bundles `sortablejs` itself — install it
|
|
205
|
+
* separately and pass it in, so the dependency stays fully optional. */
|
|
206
|
+
sortable: SortableModule;
|
|
207
|
+
/** Overrides the plain `attribute`-ascending order for composite orders
|
|
208
|
+
* — e.g. a row's true resting position depends first on a related
|
|
209
|
+
* record's own order, and only then on this row's `attribute`. Takes
|
|
210
|
+
* full ownership of row order while reorder is active (column-header
|
|
211
|
+
* sorting is unavailable, same as the plain `attribute` case). */
|
|
212
|
+
compare?: (a: T, b: T) => number;
|
|
213
|
+
/** POSTed once per drag that settles with at least one changed row — a
|
|
214
|
+
* single request, not one per row — then the list is refreshed. FormData
|
|
215
|
+
* field `changes` carries a JSON-encoded array of `{ id, value }` pairs,
|
|
216
|
+
* one per row whose `attribute` changed (`value` being its new
|
|
217
|
+
* sequential position). Provide this or `callback`. */
|
|
218
|
+
endpoint?: string;
|
|
219
|
+
/** Alternative to `endpoint`: receives the rows whose `attribute` value
|
|
220
|
+
* changed, already updated to their new index. */
|
|
221
|
+
callback?: (items: T[]) => void | Promise<void>;
|
|
222
|
+
/** Drag-handle icon shown at the start of each row. Falls back to the
|
|
223
|
+
* active icon set's `grip` icon. */
|
|
224
|
+
icon?: any;
|
|
225
|
+
/** Lets dragging one row of the current checkbox selection move the
|
|
226
|
+
* whole selection together, via SortableJS's `MultiDrag` plugin (must be
|
|
227
|
+
* mounted on the `sortable` module you pass in). Off by default. */
|
|
228
|
+
multiDrag?: boolean;
|
|
229
|
+
/** While dragging, hovering over the edge zone for this long flips a
|
|
230
|
+
* page instead of requiring one long drag across the whole list.
|
|
231
|
+
* Default `2000`. */
|
|
232
|
+
pageFlipThresholdMs?: number;
|
|
233
|
+
}
|
|
234
|
+
/** Groups the list view's row-level and selection-level custom actions —
|
|
235
|
+
* `custom` replaces the old top-level `actions` prop, `bulk` replaces
|
|
236
|
+
* `customBulkActions`. */
|
|
237
|
+
export interface ListActions<T extends object = Record<string, unknown>> {
|
|
238
|
+
/** Extra per-row actions — see Custom row actions. */
|
|
239
|
+
custom?: CustomAction<T>[];
|
|
240
|
+
/** Extra actions on the current selection — see Custom bulk actions. */
|
|
241
|
+
bulk?: CustomBulkAction<T>[];
|
|
242
|
+
}
|
|
243
|
+
/** Groups the list view's opt-in behaviors. Replaces the old top-level
|
|
244
|
+
* `search`, `enableExport`/`onExport`/`xlsx` props. */
|
|
245
|
+
export interface ListConfig<T extends object = Record<string, unknown>> {
|
|
246
|
+
/** Free-text search box — see Free-text search. */
|
|
247
|
+
search?: SearchConfiguration;
|
|
248
|
+
/** CSV/Excel export. Presence of this object enables the export button —
|
|
249
|
+
* even as an empty `{}` — replacing the old `enableExport` boolean. */
|
|
250
|
+
export?: ExportConfiguration<T>;
|
|
251
|
+
/** Drag-to-reorder rows — see Reordering rows. */
|
|
252
|
+
reorder?: ReorderConfiguration<T>;
|
|
253
|
+
}
|
package/dist/types/table.d.ts
CHANGED
|
@@ -45,3 +45,59 @@ export interface TableQuery {
|
|
|
45
45
|
ordering: string | null;
|
|
46
46
|
filters: FilterSnapshot;
|
|
47
47
|
}
|
|
48
|
+
/** An instance created by `SortableModule.create()` — the only bit of it
|
|
49
|
+
* runeforge ever touches directly. */
|
|
50
|
+
export interface SortableInstance {
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
/** Programmatic selection API SortableJS's `MultiDrag` plugin exposes as
|
|
54
|
+
* `Sortable.utils` — used to mirror this table's own row-selection
|
|
55
|
+
* (checkboxes) into the plugin's selection state, so `multiDrag` drags
|
|
56
|
+
* whatever's currently checked instead of relying on its own click/modifier
|
|
57
|
+
* based selection UX. */
|
|
58
|
+
export interface SortableUtils {
|
|
59
|
+
select(element: HTMLElement): void;
|
|
60
|
+
deselect(element: HTMLElement): void;
|
|
61
|
+
}
|
|
62
|
+
/** Minimal duck-typed subset of the `sortablejs` module API used to drive
|
|
63
|
+
* drag-to-reorder. Runeforge never imports `sortablejs` itself — install it
|
|
64
|
+
* separately and pass the resolved default export in via `reorder.sortable`
|
|
65
|
+
* (or `ReorderConfiguration.sortable` on `GenericCRUD`), so the dependency
|
|
66
|
+
* stays fully optional, mirroring `xlsx` for export. Pass `MultiDrag`
|
|
67
|
+
* pre-mounted (`Sortable.mount(new MultiDrag())`) on the module you hand in
|
|
68
|
+
* if you're using `multiDrag`. */
|
|
69
|
+
export interface SortableModule {
|
|
70
|
+
create(element: HTMLElement, options?: Record<string, any>): SortableInstance;
|
|
71
|
+
utils?: SortableUtils;
|
|
72
|
+
}
|
|
73
|
+
/** Enables the drag-to-reorder row layer on `PaginatedTable`/`TableBody`.
|
|
74
|
+
* Presence of this object is what turns dragging on — per-column filtering
|
|
75
|
+
* is suppressed while it's active (dragging needs the full, unfiltered row
|
|
76
|
+
* set reachable), and row order is fully owned by `compare`/`attribute`
|
|
77
|
+
* rather than column-header sorting. Pagination stays on; crossing a page
|
|
78
|
+
* boundary mid-drag is done via the edge hover zones (see
|
|
79
|
+
* `pageFlipThresholdMs`), not by disabling pagination. */
|
|
80
|
+
export interface ReorderOptions<T extends object = Record<string, unknown>> {
|
|
81
|
+
/** The attribute establishing the default ascending order, used when
|
|
82
|
+
* `compare` is not provided. */
|
|
83
|
+
attribute: keyof T & string;
|
|
84
|
+
/** Resolved `sortablejs` module — see the interface doc above. */
|
|
85
|
+
sortable: SortableModule;
|
|
86
|
+
/** Overrides the plain `attribute`-ascending order for cases where the
|
|
87
|
+
* true resting order depends on more than the row's own attribute (e.g.
|
|
88
|
+
* a parent's order first, then this row's own order within it). Takes
|
|
89
|
+
* full ownership of row order while reorder is active. */
|
|
90
|
+
compare?: (a: T, b: T) => number;
|
|
91
|
+
/** Drag-handle icon shown at the start of each row. Falls back to the
|
|
92
|
+
* active icon set's `grip` icon. */
|
|
93
|
+
icon?: any;
|
|
94
|
+
/** Lets dragging one row of the current checkbox selection move the
|
|
95
|
+
* whole selection together, via SortableJS's `MultiDrag` plugin (must be
|
|
96
|
+
* mounted on the `sortable` module you pass in). Off by default. */
|
|
97
|
+
multiDrag?: boolean;
|
|
98
|
+
/** While dragging, hovering the pointer over the narrow zone at either
|
|
99
|
+
* edge of the table for this long flips to the previous/next page —
|
|
100
|
+
* lets a row travel across pages without an unwieldy long drag. Repeated
|
|
101
|
+
* hovering (without leaving the zone) keeps flipping. Default `2000`. */
|
|
102
|
+
pageFlipThresholdMs?: number;
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runeforge",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.54",
|
|
4
4
|
"description": "SvelteKit toolkit for building metadata-driven CRUD interfaces with tables, forms, and actions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ezequiel Puerta",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
50
50
|
"@tailwindcss/vite": "^4.3.1",
|
|
51
51
|
"@types/node": "^22",
|
|
52
|
+
"@types/sortablejs": "^1.15.9",
|
|
52
53
|
"daisyui": "^5.5.23",
|
|
53
54
|
"eslint": "^10.4.1",
|
|
54
55
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"prettier": "^3.8.3",
|
|
58
59
|
"prettier-plugin-svelte": "^4.1.0",
|
|
59
60
|
"publint": "^0.3.21",
|
|
61
|
+
"sortablejs": "^1.15.7",
|
|
60
62
|
"svelte": "^5.56.1",
|
|
61
63
|
"svelte-bootstrap-icons": "^3.3.0",
|
|
62
64
|
"svelte-check": "^4.6.0",
|