runeforge 0.0.52 → 0.0.53
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 +6 -1
- package/dist/components/crud/utils/embedded.d.ts +1 -0
- package/dist/components/crud/utils/embedded.js +14 -0
- package/dist/components/crud/utils/formatters.d.ts +0 -1
- package/dist/components/crud/utils/formatters.js +0 -6
- package/dist/components/crud/utils/resolution.d.ts +1 -0
- package/dist/components/crud/utils/resolution.js +11 -0
- package/dist/components/crud/views/Create.svelte +2 -1
- package/dist/components/crud/views/Update.svelte +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/types/attribute.d.ts +8 -0
- package/dist/types/crud.d.ts +9 -1
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { AUTO_EXCLUDED } from './utils/constants.js';
|
|
9
9
|
import {
|
|
10
10
|
resolveFormatter,
|
|
11
|
+
truncateFormatter,
|
|
11
12
|
inferType,
|
|
12
13
|
buildFieldDefinitions
|
|
13
14
|
} from './utils/resolution.js';
|
|
@@ -198,7 +199,7 @@
|
|
|
198
199
|
// embedded column without an explicit formatter falls back to
|
|
199
200
|
// joining each item's label (itemLabel, or the same
|
|
200
201
|
// sub-field-joining summary the embedded form list uses).
|
|
201
|
-
const
|
|
202
|
+
const resolvedFormatter =
|
|
202
203
|
resolveFormatter(m, data) ??
|
|
203
204
|
(embeddedFields
|
|
204
205
|
? (value: unknown) =>
|
|
@@ -212,6 +213,10 @@
|
|
|
212
213
|
.join(', ')
|
|
213
214
|
: ''
|
|
214
215
|
: undefined);
|
|
216
|
+
const formatter =
|
|
217
|
+
m.truncateUpTo != null
|
|
218
|
+
? truncateFormatter(resolvedFormatter, m.truncateUpTo)
|
|
219
|
+
: resolvedFormatter;
|
|
215
220
|
return {
|
|
216
221
|
attribute: k as keyof T & string,
|
|
217
222
|
title: m.label ?? k,
|
|
@@ -2,5 +2,6 @@ import type { FieldDefinition } from '../../../types/crud.js';
|
|
|
2
2
|
export declare function seedField<T extends object = Record<string, unknown>>(f: FieldDefinition<T>, raw: unknown): unknown;
|
|
3
3
|
export declare function emptyRecord<T extends object = Record<string, unknown>>(fields: FieldDefinition<T>[]): Record<string, unknown>;
|
|
4
4
|
export declare function seedRecord<T extends object = Record<string, unknown>>(fields: FieldDefinition<T>[], source: Record<string, unknown>): Record<string, unknown>;
|
|
5
|
+
export declare function applyDuplicateOmit<T extends object = Record<string, unknown>>(fields: FieldDefinition<T>[], record: Record<string, unknown>, omit?: string[]): Record<string, unknown>;
|
|
5
6
|
export declare function formatEmbeddedFieldValue<T extends object = Record<string, unknown>>(f: FieldDefinition<T>, raw: unknown): string;
|
|
6
7
|
export declare function defaultItemLabel<T extends object = Record<string, unknown>>(fields: FieldDefinition<T>[], item: Record<string, unknown>): string;
|
|
@@ -39,6 +39,20 @@ export function seedRecord(fields, source) {
|
|
|
39
39
|
}
|
|
40
40
|
return seeded;
|
|
41
41
|
}
|
|
42
|
+
// Shared by Update.svelte and Create.svelte's "Duplicate" handling: returns a
|
|
43
|
+
// copy of the just-saved record with `omit`-listed attributes reset to their
|
|
44
|
+
// field default, so the new draft doesn't inherit values that only made sense
|
|
45
|
+
// for the original instance (e.g. a publication date).
|
|
46
|
+
export function applyDuplicateOmit(fields, record, omit = []) {
|
|
47
|
+
if (!omit.length)
|
|
48
|
+
return record;
|
|
49
|
+
const result = { ...record };
|
|
50
|
+
for (const f of fields) {
|
|
51
|
+
if (omit.includes(f.attribute))
|
|
52
|
+
result[f.attribute] = seedField(f, f.default);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
42
56
|
// Shared by defaultItemLabel and the CSV/XLSX export column expansion: renders
|
|
43
57
|
// one sub-field's raw stored value as display text, resolving a select's
|
|
44
58
|
// option label instead of its stored value. Booleans are left to the caller
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export declare const formatBoolean: (trueLabel?: string, falseLabel?: string) => () => (value: boolean) => string;
|
|
2
2
|
export declare const formatDatetime: (format?: string, timeZone?: string) => (() => (value: Date) => string);
|
|
3
|
-
export declare function formatTruncateTextUpTo(maxLength: number): () => (value: string) => string;
|
|
4
3
|
export declare function formatInstance<T extends Record<string, unknown>>(attribute: keyof T & string, instances: T[], urlPath: string, idKey?: keyof T & string): (value: unknown) => string;
|
|
@@ -52,12 +52,6 @@ export const formatDatetime = (format = 'dd/mm/YYYY HH:MM', timeZone) => {
|
|
|
52
52
|
};
|
|
53
53
|
return () => fmt;
|
|
54
54
|
};
|
|
55
|
-
export function formatTruncateTextUpTo(maxLength) {
|
|
56
|
-
return () => (value) => {
|
|
57
|
-
const str = String(value ?? '');
|
|
58
|
-
return str.length > maxLength ? str.slice(0, maxLength) + '…' : str;
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
55
|
function escapeHtml(str) {
|
|
62
56
|
return str
|
|
63
57
|
.replace(/&/g, '&')
|
|
@@ -3,5 +3,6 @@ import type { FieldDefinition } from '../../../types/crud.js';
|
|
|
3
3
|
export declare function resolveOptions(m: AttributeMetadata, d: unknown): SelectOption[] | undefined;
|
|
4
4
|
export declare function resolveDefault(m: AttributeMetadata, d: unknown): unknown;
|
|
5
5
|
export declare function resolveFormatter(m: AttributeMetadata, d: unknown): import("../../../index.ts").CellFormatter<any, any> | undefined;
|
|
6
|
+
export declare function truncateFormatter<T extends object = Record<string, unknown>, V = unknown>(formatter: ((value: V, row: T) => string) | undefined, maxLength: number): (value: V, row: T) => string;
|
|
6
7
|
export declare function inferType(key: string, value: unknown): AttributeType;
|
|
7
8
|
export declare function buildFieldDefinitions<T extends object = Record<string, unknown>>(meta: Partial<Record<string, AttributeMetadata>>, data: unknown, excludedFlag: 'excludedFromCreate' | 'excludedFromRead' | 'excludedFromUpdate' | 'excludedFromList', excluded: Set<string>): FieldDefinition<T>[];
|
|
@@ -9,6 +9,17 @@ export function resolveDefault(m, d) {
|
|
|
9
9
|
export function resolveFormatter(m, d) {
|
|
10
10
|
return m.formatter?.(d);
|
|
11
11
|
}
|
|
12
|
+
// List column only (GenericCRUD's resolvedColumns): wraps `formatter` (or,
|
|
13
|
+
// absent one, the same raw String(value) TableBody falls back to) so a column
|
|
14
|
+
// that needs to stay narrow can truncate its cell without that truncation
|
|
15
|
+
// leaking into the Read view or the Create/Update forms, which call
|
|
16
|
+
// `formatter` directly and never go through this wrapper.
|
|
17
|
+
export function truncateFormatter(formatter, maxLength) {
|
|
18
|
+
return (value, row) => {
|
|
19
|
+
const str = formatter ? formatter(value, row) : String(value ?? '');
|
|
20
|
+
return str.length > maxLength ? str.slice(0, maxLength) + '…' : str;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
12
23
|
export function inferType(key, value) {
|
|
13
24
|
if (typeof value === 'boolean')
|
|
14
25
|
return 'boolean';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { defaultIconSet } from '../../../icons/sets/default.js';
|
|
9
9
|
import { validateAll } from '../utils/validation.js';
|
|
10
10
|
import { groupFields } from '../utils/grouping.js';
|
|
11
|
-
import { emptyRecord, seedRecord } from '../utils/embedded.js';
|
|
11
|
+
import { applyDuplicateOmit, emptyRecord, seedRecord } from '../utils/embedded.js';
|
|
12
12
|
import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
|
|
13
13
|
import { getStrings } from '../../../i18n/context.js';
|
|
14
14
|
|
|
@@ -136,6 +136,7 @@
|
|
|
136
136
|
fieldErrors = {};
|
|
137
137
|
internalError = '';
|
|
138
138
|
flashSuccess();
|
|
139
|
+
record = applyDuplicateOmit(fields, record, creation.duplication?.omit);
|
|
139
140
|
} else {
|
|
140
141
|
onSuccess?.();
|
|
141
142
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { defaultIconSet } from '../../../icons/sets/default.js';
|
|
9
9
|
import { validateAll } from '../utils/validation.js';
|
|
10
10
|
import { groupFields } from '../utils/grouping.js';
|
|
11
|
-
import { seedRecord } from '../utils/embedded.js';
|
|
11
|
+
import { applyDuplicateOmit, seedRecord } from '../utils/embedded.js';
|
|
12
12
|
import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
|
|
13
13
|
import { getStrings } from '../../../i18n/context.js';
|
|
14
14
|
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
onContinue?.();
|
|
124
124
|
} else if (duplicating) {
|
|
125
125
|
duplicating = false;
|
|
126
|
-
onDuplicate?.(record);
|
|
126
|
+
onDuplicate?.(applyDuplicateOmit(fields, record, update.duplication?.omit));
|
|
127
127
|
} else {
|
|
128
128
|
onSuccess?.();
|
|
129
129
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -44,9 +44,9 @@ export { default as CRUDRead } from './components/crud/views/Read.svelte';
|
|
|
44
44
|
export { default as CRUDUpdate } from './components/crud/views/Update.svelte';
|
|
45
45
|
export { default as SearchInput } from './components/crud/SearchInput.svelte';
|
|
46
46
|
export { AUTO_EXCLUDED } from './components/crud/utils/constants.js';
|
|
47
|
-
export { resolveOptions, resolveFormatter, inferType, buildFieldDefinitions } from './components/crud/utils/resolution.js';
|
|
47
|
+
export { resolveOptions, resolveFormatter, truncateFormatter, inferType, buildFieldDefinitions } from './components/crud/utils/resolution.js';
|
|
48
48
|
export { fieldLabel, initials } from './components/crud/utils/misc.js';
|
|
49
|
-
export { formatBoolean, formatDatetime,
|
|
49
|
+
export { formatBoolean, formatDatetime, formatInstance } from './components/crud/utils/formatters.js';
|
|
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';
|
package/dist/index.js
CHANGED
|
@@ -42,9 +42,9 @@ export { default as CRUDUpdate } from './components/crud/views/Update.svelte';
|
|
|
42
42
|
export { default as SearchInput } from './components/crud/SearchInput.svelte';
|
|
43
43
|
// CRUD utilities
|
|
44
44
|
export { AUTO_EXCLUDED } from './components/crud/utils/constants.js';
|
|
45
|
-
export { resolveOptions, resolveFormatter, inferType, buildFieldDefinitions } from './components/crud/utils/resolution.js';
|
|
45
|
+
export { resolveOptions, resolveFormatter, truncateFormatter, inferType, buildFieldDefinitions } from './components/crud/utils/resolution.js';
|
|
46
46
|
export { fieldLabel, initials } from './components/crud/utils/misc.js';
|
|
47
|
-
export { formatBoolean, formatDatetime,
|
|
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
50
|
export { emptyRecord, defaultItemLabel } from './components/crud/utils/embedded.js';
|
|
@@ -51,7 +51,15 @@ export type AttributeMetadata = {
|
|
|
51
51
|
hidden?: boolean | HiddenResolver;
|
|
52
52
|
seed?: SeedResolver;
|
|
53
53
|
component?: CellComponent<any, any>;
|
|
54
|
+
/** Formats the raw value everywhere it's displayed read-only: the list
|
|
55
|
+
* column's cell, the Read view's field, and any other readonly Field. */
|
|
54
56
|
formatter?: FormatterResolver;
|
|
57
|
+
/** List column only: truncates the cell's text to this many characters,
|
|
58
|
+
* appending an ellipsis, applied to `formatter`'s output (or, absent one,
|
|
59
|
+
* the raw stringified value) — the Read view and Create/Update forms
|
|
60
|
+
* always show the untruncated value. Handy for a long free-text column
|
|
61
|
+
* that would otherwise blow out the table's width. */
|
|
62
|
+
truncateUpTo?: number;
|
|
55
63
|
/** Pass a function to require a field only in certain conditions, e.g. a
|
|
56
64
|
* quantity that only applies to some of a select's options. */
|
|
57
65
|
required?: boolean | RequiredResolver;
|
package/dist/types/crud.d.ts
CHANGED
|
@@ -65,6 +65,14 @@ export interface CreateFormButtonConfiguration {
|
|
|
65
65
|
label?: string;
|
|
66
66
|
class?: string;
|
|
67
67
|
}
|
|
68
|
+
/** The "Duplicate" button's configuration — a `CreateFormButtonConfiguration`
|
|
69
|
+
* plus which attributes to leave out of the duplicated draft. */
|
|
70
|
+
export interface DuplicationButtonConfiguration extends CreateFormButtonConfiguration {
|
|
71
|
+
/** Attribute names reset to the field's `default` instead of copying the
|
|
72
|
+
* source instance's value — e.g. a publication date that should depend on
|
|
73
|
+
* when the copy is itself published, not on when the original was. */
|
|
74
|
+
omit?: string[];
|
|
75
|
+
}
|
|
68
76
|
export interface ActionConfiguration<T extends object = Record<string, unknown>> {
|
|
69
77
|
enabled?: boolean;
|
|
70
78
|
label?: string;
|
|
@@ -90,7 +98,7 @@ export interface ActionConfiguration<T extends object = Record<string, unknown>>
|
|
|
90
98
|
* create form pre-filled with the just-saved instance's values, so a new
|
|
91
99
|
* record can be started from it without retyping everything.
|
|
92
100
|
* Disabled by default — pass `{ enabled: true }` to show it. */
|
|
93
|
-
duplication?:
|
|
101
|
+
duplication?: DuplicationButtonConfiguration;
|
|
94
102
|
}
|
|
95
103
|
export interface CustomAction<T extends object = Record<string, unknown>> {
|
|
96
104
|
label: string;
|