runeforge 0.0.57 → 0.0.59

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.
Files changed (69) hide show
  1. package/README.md +51 -0
  2. package/dist/components/crud/EmbeddedField.svelte +33 -5
  3. package/dist/components/crud/EmbeddedField.svelte.d.ts +6 -1
  4. package/dist/components/crud/Field.svelte +76 -423
  5. package/dist/components/crud/Field.svelte.d.ts +5 -1
  6. package/dist/components/crud/GenericCRUD.svelte +4 -4
  7. package/dist/components/crud/GenericCRUD.svelte.d.ts +4 -1
  8. package/dist/components/crud/SearchInput.svelte +1 -1
  9. package/dist/components/crud/SearchInput.svelte.d.ts +1 -1
  10. package/dist/components/crud/fields/BooleanField.svelte +38 -0
  11. package/dist/components/crud/fields/BooleanField.svelte.d.ts +33 -0
  12. package/dist/components/crud/fields/DatetimeField.svelte +250 -0
  13. package/dist/components/crud/fields/DatetimeField.svelte.d.ts +34 -0
  14. package/dist/components/crud/fields/DefaultField.svelte +67 -0
  15. package/dist/components/crud/fields/DefaultField.svelte.d.ts +34 -0
  16. package/dist/components/crud/fields/FileField.svelte +70 -0
  17. package/dist/components/crud/fields/FileField.svelte.d.ts +34 -0
  18. package/dist/components/crud/fields/MultiSelectField.svelte +70 -0
  19. package/dist/components/crud/fields/MultiSelectField.svelte.d.ts +34 -0
  20. package/dist/components/crud/fields/SelectField.svelte +67 -0
  21. package/dist/components/crud/fields/SelectField.svelte.d.ts +34 -0
  22. package/dist/components/crud/fields/TextareaField.svelte +55 -0
  23. package/dist/components/crud/fields/TextareaField.svelte.d.ts +34 -0
  24. package/dist/components/crud/fields/TreeField.svelte +63 -0
  25. package/dist/components/crud/fields/TreeField.svelte.d.ts +33 -0
  26. package/dist/components/crud/utils/embedded.d.ts +1 -1
  27. package/dist/components/crud/utils/grouping.d.ts +1 -1
  28. package/dist/components/crud/utils/resolution.d.ts +1 -1
  29. package/dist/components/crud/utils/resolution.js +6 -2
  30. package/dist/components/crud/utils/validation.d.ts +1 -1
  31. package/dist/components/crud/utils/validation.js +10 -0
  32. package/dist/components/crud/views/Create.svelte +37 -30
  33. package/dist/components/crud/views/Create.svelte.d.ts +2 -1
  34. package/dist/components/crud/views/Read.svelte +19 -12
  35. package/dist/components/crud/views/Read.svelte.d.ts +2 -1
  36. package/dist/components/crud/views/Update.svelte +28 -21
  37. package/dist/components/crud/views/Update.svelte.d.ts +2 -1
  38. package/dist/components/crud/views/list/List.svelte +3 -3
  39. package/dist/components/crud/views/list/List.svelte.d.ts +3 -1
  40. package/dist/components/crud/views/list/Modals.svelte +1 -1
  41. package/dist/components/crud/views/list/Modals.svelte.d.ts +1 -1
  42. package/dist/components/crud/views/list/Table.svelte +3 -6
  43. package/dist/components/crud/views/list/Table.svelte.d.ts +3 -1
  44. package/dist/components/crud/views/list/Toolbar.svelte +3 -4
  45. package/dist/components/crud/views/list/Toolbar.svelte.d.ts +3 -1
  46. package/dist/components/table/ColumnFilter.svelte +1 -1
  47. package/dist/components/table/ColumnFilter.svelte.d.ts +1 -1
  48. package/dist/components/table/PaginatedTable.svelte +1 -1
  49. package/dist/components/table/PaginatedTable.svelte.d.ts +1 -1
  50. package/dist/components/table/TableBody.svelte +1 -1
  51. package/dist/components/table/TableBody.svelte.d.ts +1 -1
  52. package/dist/components/table/TableHeader.svelte +1 -1
  53. package/dist/components/table/TableHeader.svelte.d.ts +1 -1
  54. package/dist/components/table/export.d.ts +1 -1
  55. package/dist/components/table/state.svelte.d.ts +1 -1
  56. package/dist/components/table/utils.d.ts +1 -1
  57. package/dist/index.d.ts +5 -1
  58. package/dist/types/attribute.d.ts +56 -4
  59. package/dist/types/{crud.d.ts → crud/actions.d.ts} +0 -123
  60. package/dist/types/crud/columns.d.ts +23 -0
  61. package/dist/types/crud/columns.js +1 -0
  62. package/dist/types/crud/fields.d.ts +48 -0
  63. package/dist/types/crud/fields.js +1 -0
  64. package/dist/types/crud/list-config.d.ts +23 -0
  65. package/dist/types/crud/list-config.js +1 -0
  66. package/dist/types/crud/reorder.d.ts +45 -0
  67. package/dist/types/crud/reorder.js +1 -0
  68. package/package.json +1 -1
  69. /package/dist/types/{crud.js → crud/actions.js} +0 -0
@@ -0,0 +1,34 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
3
+ declare function $$render<T extends object = Record<string, unknown>>(): {
4
+ props: {
5
+ field: FieldDefinition<T>;
6
+ record?: Record<string, unknown>;
7
+ parentRecord?: Record<string, unknown>;
8
+ error?: string;
9
+ readonly?: boolean;
10
+ labelRow: Snippet;
11
+ errorRow: Snippet;
12
+ };
13
+ exports: {};
14
+ bindings: "record";
15
+ slots: {};
16
+ events: {};
17
+ };
18
+ declare class __sveltets_Render<T extends object = Record<string, unknown>> {
19
+ props(): ReturnType<typeof $$render<T>>['props'];
20
+ events(): ReturnType<typeof $$render<T>>['events'];
21
+ slots(): ReturnType<typeof $$render<T>>['slots'];
22
+ bindings(): "record";
23
+ exports(): {};
24
+ }
25
+ interface $$IsomorphicComponent {
26
+ new <T extends object = Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
27
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
28
+ } & ReturnType<__sveltets_Render<T>['exports']>;
29
+ <T extends object = Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
30
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
31
+ }
32
+ declare const SelectField: $$IsomorphicComponent;
33
+ type SelectField<T extends object = Record<string, unknown>> = InstanceType<typeof SelectField<T>>;
34
+ export default SelectField;
@@ -0,0 +1,55 @@
1
+ <script lang="ts" generics="T extends object = Record<string, unknown>">
2
+ import type { Snippet } from 'svelte';
3
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
4
+
5
+ let {
6
+ field,
7
+ record = $bindable({} as Record<string, unknown>),
8
+ parentRecord = undefined,
9
+ error = '',
10
+ readonly = false,
11
+ labelRow,
12
+ errorRow
13
+ }: {
14
+ field: FieldDefinition<T>;
15
+ record?: Record<string, unknown>;
16
+ parentRecord?: Record<string, unknown>;
17
+ error?: string;
18
+ readonly?: boolean;
19
+ labelRow: Snippet;
20
+ errorRow: Snippet;
21
+ } = $props();
22
+
23
+ const name = $derived(readonly ? undefined : field.attribute);
24
+ const saved = $derived(record[field.attribute]);
25
+ const displayValue = $derived(saved == null ? '' : String(saved));
26
+ const formattedValue = $derived(
27
+ readonly && field.formatter ? field.formatter(saved, record) : displayValue
28
+ );
29
+ const fieldDisabled = $derived(field.disabled ? field.disabled(record, parentRecord) : false);
30
+ </script>
31
+
32
+ {@render labelRow()}
33
+
34
+ {#if readonly}
35
+ <textarea
36
+ id={field.attribute}
37
+ rows={field.rows}
38
+ class="textarea textarea-bordered bg-base-100 w-full"
39
+ value={formattedValue}
40
+ disabled
41
+ ></textarea>
42
+ {:else}
43
+ <textarea
44
+ id={field.attribute}
45
+ {name}
46
+ rows={field.rows}
47
+ placeholder={field.placeholder ?? ''}
48
+ bind:value={record[field.attribute]}
49
+ class="textarea textarea-bordered bg-base-100 w-full"
50
+ class:textarea-error={!!error}
51
+ disabled={fieldDisabled}
52
+ ></textarea>
53
+ {/if}
54
+
55
+ {@render errorRow()}
@@ -0,0 +1,34 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
3
+ declare function $$render<T extends object = Record<string, unknown>>(): {
4
+ props: {
5
+ field: FieldDefinition<T>;
6
+ record?: Record<string, unknown>;
7
+ parentRecord?: Record<string, unknown>;
8
+ error?: string;
9
+ readonly?: boolean;
10
+ labelRow: Snippet;
11
+ errorRow: Snippet;
12
+ };
13
+ exports: {};
14
+ bindings: "record";
15
+ slots: {};
16
+ events: {};
17
+ };
18
+ declare class __sveltets_Render<T extends object = Record<string, unknown>> {
19
+ props(): ReturnType<typeof $$render<T>>['props'];
20
+ events(): ReturnType<typeof $$render<T>>['events'];
21
+ slots(): ReturnType<typeof $$render<T>>['slots'];
22
+ bindings(): "record";
23
+ exports(): {};
24
+ }
25
+ interface $$IsomorphicComponent {
26
+ new <T extends object = Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
27
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
28
+ } & ReturnType<__sveltets_Render<T>['exports']>;
29
+ <T extends object = Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
30
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
31
+ }
32
+ declare const TextareaField: $$IsomorphicComponent;
33
+ type TextareaField<T extends object = Record<string, unknown>> = InstanceType<typeof TextareaField<T>>;
34
+ export default TextareaField;
@@ -0,0 +1,63 @@
1
+ <script lang="ts" generics="T extends object = Record<string, unknown>">
2
+ import type { Snippet } from 'svelte';
3
+ import Tree from '../../form/Tree.svelte';
4
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
5
+
6
+ let {
7
+ field,
8
+ record = $bindable({} as Record<string, unknown>),
9
+ parentRecord = undefined,
10
+ readonly = false,
11
+ labelRow,
12
+ errorRow
13
+ }: {
14
+ field: FieldDefinition<T>;
15
+ record?: Record<string, unknown>;
16
+ parentRecord?: Record<string, unknown>;
17
+ readonly?: boolean;
18
+ labelRow: Snippet;
19
+ errorRow: Snippet;
20
+ } = $props();
21
+
22
+ const saved = $derived(record[field.attribute]);
23
+ const selectOptions = $derived(
24
+ field.dependentOptions ? field.dependentOptions(record, parentRecord) : (field.options ?? [])
25
+ );
26
+ const fieldDisabled = $derived(field.disabled ? field.disabled(record, parentRecord) : false);
27
+
28
+ // See SelectField's equivalent effect.
29
+ $effect(() => {
30
+ if (!field.dependentOptions) return;
31
+ const current = record[field.attribute];
32
+ if (!Array.isArray(current)) return;
33
+ const validValues = new Set(selectOptions.map((o) => o.value));
34
+ const pruned = current.filter((v) => validValues.has(String(v)));
35
+ if (pruned.length !== current.length) {
36
+ record[field.attribute] = pruned;
37
+ }
38
+ });
39
+ </script>
40
+
41
+ {@render labelRow()}
42
+
43
+ {#if readonly}
44
+ <input
45
+ type="text"
46
+ id={field.attribute}
47
+ class="input input-bordered w-full"
48
+ value={(Array.isArray(saved) ? saved : [])
49
+ .map((v) => selectOptions.find((o) => o.value === String(v))?.label ?? String(v))
50
+ .join(', ')}
51
+ disabled
52
+ />
53
+ {:else}
54
+ <Tree
55
+ name={field.attribute}
56
+ bind:value={record[field.attribute] as string[]}
57
+ options={selectOptions}
58
+ disabled={fieldDisabled}
59
+ defaultExpanded={field.defaultExpanded ?? true}
60
+ />
61
+ {/if}
62
+
63
+ {@render errorRow()}
@@ -0,0 +1,33 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
3
+ declare function $$render<T extends object = Record<string, unknown>>(): {
4
+ props: {
5
+ field: FieldDefinition<T>;
6
+ record?: Record<string, unknown>;
7
+ parentRecord?: Record<string, unknown>;
8
+ readonly?: boolean;
9
+ labelRow: Snippet;
10
+ errorRow: Snippet;
11
+ };
12
+ exports: {};
13
+ bindings: "record";
14
+ slots: {};
15
+ events: {};
16
+ };
17
+ declare class __sveltets_Render<T extends object = Record<string, unknown>> {
18
+ props(): ReturnType<typeof $$render<T>>['props'];
19
+ events(): ReturnType<typeof $$render<T>>['events'];
20
+ slots(): ReturnType<typeof $$render<T>>['slots'];
21
+ bindings(): "record";
22
+ exports(): {};
23
+ }
24
+ interface $$IsomorphicComponent {
25
+ new <T extends object = Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
26
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
27
+ } & ReturnType<__sveltets_Render<T>['exports']>;
28
+ <T extends object = Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
29
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
30
+ }
31
+ declare const TreeField: $$IsomorphicComponent;
32
+ type TreeField<T extends object = Record<string, unknown>> = InstanceType<typeof TreeField<T>>;
33
+ export default TreeField;
@@ -1,4 +1,4 @@
1
- import type { FieldDefinition } from '../../../types/crud.js';
1
+ import type { FieldDefinition } from '../../../types/crud/fields.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>;
@@ -1,4 +1,4 @@
1
- import type { FieldDefinition } from '../../../types/crud.js';
1
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
2
2
  export type FieldRow<T extends object = Record<string, unknown>> = FieldDefinition<T>[];
3
3
  export type FieldGroup<T extends object = Record<string, unknown>> = {
4
4
  title?: string;
@@ -1,5 +1,5 @@
1
1
  import type { AttributeMetadata, AttributeType, SelectOption } from '../../../types/attribute.js';
2
- import type { FieldDefinition } from '../../../types/crud.js';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.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;
@@ -54,7 +54,7 @@ export function buildFieldDefinitions(meta, data, excludedFlag, excluded) {
54
54
  default: resolveDefault(m, data),
55
55
  options: resolveOptions(m, data),
56
56
  dependentOptions: m.dependentOptions
57
- ? (record) => m.dependentOptions(data, record)
57
+ ? (record, parent) => m.dependentOptions(data, record, parent)
58
58
  : undefined,
59
59
  search: m.search,
60
60
  disabled: m.disabled,
@@ -68,10 +68,14 @@ export function buildFieldDefinitions(meta, data, excludedFlag, excluded) {
68
68
  minLength: m.minLength,
69
69
  maxLength: m.maxLength,
70
70
  pattern: m.pattern,
71
+ validate: m.validate,
71
72
  rows: m.rows,
72
73
  fields: m.fields ? buildFieldDefinitions(m.fields, data, excludedFlag, new Set()) : undefined,
73
74
  itemLabel: m.itemLabel,
75
+ revalidate: m.revalidate,
76
+ dependsOn: m.dependsOn,
74
77
  defaultExpanded: m.defaultExpanded,
75
- row: m.row
78
+ row: m.row,
79
+ actions: m.actions
76
80
  }));
77
81
  }
@@ -1,3 +1,3 @@
1
- import type { FieldDefinition } from '../../../types/crud.js';
1
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
2
2
  import type { RuneforgeStrings } from '../../../i18n/types.js';
3
3
  export declare function validateAll<T extends object = Record<string, unknown>>(fields: FieldDefinition<T>[], formData: FormData, strings: RuneforgeStrings): Record<string, string>;
@@ -22,6 +22,11 @@ export function validateAll(fields, formData, strings) {
22
22
  if (required && (!Array.isArray(items) || items.length === 0)) {
23
23
  errors[field.attribute] = strings.required(fieldLabel(field));
24
24
  }
25
+ if (!errors[field.attribute] && field.validate) {
26
+ const message = field.validate(items, record);
27
+ if (message)
28
+ errors[field.attribute] = message;
29
+ }
25
30
  continue;
26
31
  }
27
32
  const val = String(formData.get(field.attribute) ?? '').trim();
@@ -57,6 +62,11 @@ export function validateAll(fields, formData, strings) {
57
62
  errors[field.attribute] = strings.pattern(fieldLabel(field));
58
63
  }
59
64
  }
65
+ if (!errors[field.attribute] && field.validate) {
66
+ const message = field.validate(val, record);
67
+ if (message)
68
+ errors[field.attribute] = message;
69
+ }
60
70
  }
61
71
  return errors;
62
72
  }
@@ -9,7 +9,8 @@
9
9
  import { validateAll } from '../utils/validation.js';
10
10
  import { groupFields } from '../utils/grouping.js';
11
11
  import { applyDuplicateOmit, emptyRecord, seedRecord } from '../utils/embedded.js';
12
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
12
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
13
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
13
14
  import { getStrings } from '../../../i18n/context.js';
14
15
 
15
16
  const strings = getStrings();
@@ -78,35 +79,37 @@
78
79
 
79
80
  <div class="flex flex-col gap-6">
80
81
 
81
- <Header
82
- title={labelMany}
83
- breadcrumbs={[
84
- { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
85
- { label: creation.label ?? labelOne, icon: icons.create },
86
- ]}
87
- />
88
-
89
- {#if successMessage}
90
- <div role="alert" class="alert alert-success">
91
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
92
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
93
- </svg>
94
- <span class="text-sm">{successMessage}</span>
95
- </div>
96
- {/if}
97
-
98
- {#if errorEntries.length > 0}
99
- <div role="alert" class="alert alert-error">
100
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
101
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
102
- </svg>
103
- <ul class="list-disc list-inside text-sm">
104
- {#each errorEntries as [key, msg] (key)}
105
- <li>{msg}</li>
106
- {/each}
107
- </ul>
108
- </div>
109
- {/if}
82
+ <div class="sticky-header sticky z-10 flex flex-col gap-6 bg-base-100 pb-2">
83
+ <Header
84
+ title={labelMany}
85
+ breadcrumbs={[
86
+ { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
87
+ { label: creation.label ?? labelOne, icon: icons.create },
88
+ ]}
89
+ />
90
+
91
+ {#if successMessage}
92
+ <div role="alert" class="alert alert-success">
93
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
94
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
95
+ </svg>
96
+ <span class="text-sm">{successMessage}</span>
97
+ </div>
98
+ {/if}
99
+
100
+ {#if errorEntries.length > 0}
101
+ <div role="alert" class="alert alert-error">
102
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
103
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
104
+ </svg>
105
+ <ul class="list-disc list-inside text-sm">
106
+ {#each errorEntries as [key, msg] (key)}
107
+ <li>{msg}</li>
108
+ {/each}
109
+ </ul>
110
+ </div>
111
+ {/if}
112
+ </div>
110
113
 
111
114
  <form
112
115
  method="POST"
@@ -229,4 +232,8 @@
229
232
  form {
230
233
  max-width: var(--runeforge-form-max-width, 32rem);
231
234
  }
235
+
236
+ .sticky-header {
237
+ top: var(--runeforge-sticky-header-top, 0);
238
+ }
232
239
  </style>
@@ -1,4 +1,5 @@
1
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
1
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
2
3
  declare function $$render<T extends object = Record<string, unknown>>(): {
3
4
  props: {
4
5
  labelOne?: string;
@@ -7,7 +7,8 @@
7
7
  import { getIconSet } from '../../../icons/context.js';
8
8
  import { defaultIconSet } from '../../../icons/sets/default.js';
9
9
  import { groupFields } from '../utils/grouping.js';
10
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
10
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
11
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
11
12
  import { getStrings } from '../../../i18n/context.js';
12
13
 
13
14
  const strings = getStrings();
@@ -67,13 +68,15 @@
67
68
 
68
69
  <div class="flex flex-col gap-6">
69
70
 
70
- <Header
71
- title={labelMany}
72
- breadcrumbs={[
73
- { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
74
- { label: read.label ?? labelOne, icon: icons.view },
75
- ]}
76
- />
71
+ <div class="sticky-header sticky z-10 bg-base-100 pb-2">
72
+ <Header
73
+ title={labelMany}
74
+ breadcrumbs={[
75
+ { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
76
+ { label: read.label ?? labelOne, icon: icons.view },
77
+ ]}
78
+ />
79
+ </div>
77
80
 
78
81
  <div class="fields-panel mx-auto flex w-full flex-col gap-4 px-4">
79
82
  {#each groups as group, i (group.title ?? `_ungrouped_${i}`)}
@@ -85,11 +88,11 @@
85
88
  {#if row.length > 1}
86
89
  <div class="flex flex-col gap-4 md:flex-row">
87
90
  {#each row as field (field.attribute)}
88
- <Field {field} {record} readonly class="md:min-w-0 md:flex-1" />
91
+ <Field {field} bind:record readonly class="md:min-w-0 md:flex-1" />
89
92
  {/each}
90
93
  </div>
91
94
  {:else}
92
- <Field field={row[0]} {record} readonly />
95
+ <Field field={row[0]} bind:record readonly />
93
96
  {/if}
94
97
  {/each}
95
98
  </div>
@@ -99,11 +102,11 @@
99
102
  {#if row.length > 1}
100
103
  <div class="flex flex-col gap-4 md:flex-row">
101
104
  {#each row as field (field.attribute)}
102
- <Field {field} {record} readonly class="md:min-w-0 md:flex-1" />
105
+ <Field {field} bind:record readonly class="md:min-w-0 md:flex-1" />
103
106
  {/each}
104
107
  </div>
105
108
  {:else}
106
- <Field field={row[0]} {record} readonly />
109
+ <Field field={row[0]} bind:record readonly />
107
110
  {/if}
108
111
  {/each}
109
112
  {/if}
@@ -121,4 +124,8 @@
121
124
  .fields-panel {
122
125
  max-width: var(--runeforge-form-max-width, 32rem);
123
126
  }
127
+
128
+ .sticky-header {
129
+ top: var(--runeforge-sticky-header-top, 0);
130
+ }
124
131
  </style>
@@ -1,4 +1,5 @@
1
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
1
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
2
3
  declare function $$render<T extends object = Record<string, unknown>>(): {
3
4
  props: {
4
5
  labelOne?: string;
@@ -9,7 +9,8 @@
9
9
  import { validateAll } from '../utils/validation.js';
10
10
  import { groupFields } from '../utils/grouping.js';
11
11
  import { applyDuplicateOmit, seedRecord } from '../utils/embedded.js';
12
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
12
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
13
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
13
14
  import { getStrings } from '../../../i18n/context.js';
14
15
 
15
16
  const strings = getStrings();
@@ -80,26 +81,28 @@
80
81
 
81
82
  <div class="flex flex-col gap-6">
82
83
 
83
- <Header
84
- title={labelMany}
85
- breadcrumbs={[
86
- { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
87
- { label: update.label ?? labelOne, icon: icons.edit },
88
- ]}
89
- />
90
-
91
- {#if errorEntries.length > 0}
92
- <div role="alert" class="alert alert-error">
93
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
94
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
95
- </svg>
96
- <ul class="list-disc list-inside text-sm">
97
- {#each errorEntries as [key, msg] (key)}
98
- <li>{msg}</li>
99
- {/each}
100
- </ul>
101
- </div>
102
- {/if}
84
+ <div class="sticky-header sticky z-10 flex flex-col gap-6 bg-base-100 pb-2">
85
+ <Header
86
+ title={labelMany}
87
+ breadcrumbs={[
88
+ { label: labelMany, icon: entityIcon, link: { href: '#', onclick: (e) => { e.preventDefault(); onCancel?.(); } }, prominent: true },
89
+ { label: update.label ?? labelOne, icon: icons.edit },
90
+ ]}
91
+ />
92
+
93
+ {#if errorEntries.length > 0}
94
+ <div role="alert" class="alert alert-error">
95
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
96
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
97
+ </svg>
98
+ <ul class="list-disc list-inside text-sm">
99
+ {#each errorEntries as [key, msg] (key)}
100
+ <li>{msg}</li>
101
+ {/each}
102
+ </ul>
103
+ </div>
104
+ {/if}
105
+ </div>
103
106
 
104
107
  <form
105
108
  method="POST"
@@ -205,4 +208,8 @@
205
208
  form {
206
209
  max-width: var(--runeforge-form-max-width, 32rem);
207
210
  }
211
+
212
+ .sticky-header {
213
+ top: var(--runeforge-sticky-header-top, 0);
214
+ }
208
215
  </style>
@@ -1,4 +1,5 @@
1
- import type { ActionConfiguration, FieldDefinition } from '../../../types/crud.js';
1
+ import type { ActionConfiguration } from '../../../types/crud/actions.js';
2
+ import type { FieldDefinition } from '../../../types/crud/fields.js';
2
3
  declare function $$render<T extends object = Record<string, unknown>>(): {
3
4
  props: {
4
5
  labelOne?: string;
@@ -7,13 +7,13 @@
7
7
  import { computeReorderChanges } from '../../utils/reorder.js';
8
8
  import type {
9
9
  ActionConfiguration,
10
- ColumnDefinition,
11
10
  CustomAction,
12
11
  CustomBulkAction,
13
12
  ListActions,
14
- ListConfig,
15
13
  ViewBasedCustomBulkAction
16
- } from '../../../../types/crud.js';
14
+ } from '../../../../types/crud/actions.js';
15
+ import type { ColumnDefinition } from '../../../../types/crud/columns.js';
16
+ import type { ListConfig } from '../../../../types/crud/list-config.js';
17
17
  import type {
18
18
  FilterSnapshot,
19
19
  ServerPagination,
@@ -1,4 +1,6 @@
1
- import type { ActionConfiguration, ColumnDefinition, CustomAction, ListActions, ListConfig, ViewBasedCustomBulkAction } from '../../../../types/crud.js';
1
+ import type { ActionConfiguration, CustomAction, ListActions, ViewBasedCustomBulkAction } from '../../../../types/crud/actions.js';
2
+ import type { ColumnDefinition } from '../../../../types/crud/columns.js';
3
+ import type { ListConfig } from '../../../../types/crud/list-config.js';
2
4
  import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../../../types/table.js';
3
5
  declare function $$render<T extends object = Record<string, unknown>>(): {
4
6
  props: {
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" generics="T extends object = Record<string, unknown>">
2
2
  import Button from '../../../form/Button.svelte';
3
3
  import Modal from '../../../Modal.svelte';
4
- import type { CustomBulkAction } from '../../../../types/crud.js';
4
+ import type { CustomBulkAction } from '../../../../types/crud/actions.js';
5
5
  import { getStrings } from '../../../../i18n/context.js';
6
6
 
7
7
  const strings = getStrings();
@@ -1,4 +1,4 @@
1
- import type { CustomBulkAction } from '../../../../types/crud.js';
1
+ import type { CustomBulkAction } from '../../../../types/crud/actions.js';
2
2
  declare function $$render<T extends object = Record<string, unknown>>(): {
3
3
  props: {
4
4
  deleteLabel?: string;
@@ -4,12 +4,9 @@
4
4
  import PaginatedTable from '../../../table/PaginatedTable.svelte';
5
5
  import { getIconSet } from '../../../../icons/context.js';
6
6
  import { defaultIconSet } from '../../../../icons/sets/default.js';
7
- import type {
8
- ColumnDefinition,
9
- CustomAction,
10
- ReorderConfiguration,
11
- RowAction
12
- } from '../../../../types/crud.js';
7
+ import type { ColumnDefinition } from '../../../../types/crud/columns.js';
8
+ import type { CustomAction, RowAction } from '../../../../types/crud/actions.js';
9
+ import type { ReorderConfiguration } from '../../../../types/crud/reorder.js';
13
10
  import type {
14
11
  FilterSnapshot,
15
12
  ServerPagination,
@@ -1,5 +1,7 @@
1
1
  import { SvelteSet } from 'svelte/reactivity';
2
- import type { ColumnDefinition, CustomAction, ReorderConfiguration } from '../../../../types/crud.js';
2
+ import type { ColumnDefinition } from '../../../../types/crud/columns.js';
3
+ import type { CustomAction } from '../../../../types/crud/actions.js';
4
+ import type { ReorderConfiguration } from '../../../../types/crud/reorder.js';
3
5
  import type { FilterSnapshot, ServerPagination, SortDirection, TableQuery } from '../../../../types/table.js';
4
6
  declare function $$render<T extends object = Record<string, unknown>>(): {
5
7
  props: {