@lobb-js/studio 0.28.5 → 0.29.0

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 (92) hide show
  1. package/dist/components/Studio.svelte +7 -4
  2. package/dist/components/combobox.svelte +3 -3
  3. package/dist/components/confirmationDialog/confirmationDialog.svelte +1 -1
  4. package/dist/components/dataTable/dataTable.svelte +74 -82
  5. package/dist/components/dataTable/dataTable.svelte.d.ts +3 -19
  6. package/dist/components/dataTable/filter.svelte +1 -1
  7. package/dist/components/dataTable/filterButton.svelte +1 -1
  8. package/dist/components/dataTable/header.svelte +33 -54
  9. package/dist/components/dataTable/header.svelte.d.ts +3 -2
  10. package/dist/components/dataTable/sort.svelte +1 -1
  11. package/dist/components/dataTable/sortButton.svelte +2 -2
  12. package/dist/components/detailView/create/children.svelte +1 -1
  13. package/dist/components/detailView/create/createDetailView.svelte +77 -42
  14. package/dist/components/detailView/create/createDetailView.svelte.d.ts +2 -2
  15. package/dist/components/detailView/create/createDetailViewButton.svelte +2 -2
  16. package/dist/components/detailView/create/createDetailViewButton.svelte.d.ts +1 -1
  17. package/dist/components/detailView/create/createManyView.svelte +10 -6
  18. package/dist/components/detailView/fieldInput.svelte +1 -1
  19. package/dist/components/detailView/fieldInputReplacement.svelte +1 -1
  20. package/dist/components/detailView/update/detailViewChildren.svelte +15 -26
  21. package/dist/components/detailView/update/detailViewChildren.svelte.d.ts +3 -8
  22. package/dist/components/detailView/update/updateDetailView.svelte +85 -27
  23. package/dist/components/detailView/update/updateDetailView.svelte.d.ts +2 -2
  24. package/dist/components/detailView/update/updateDetailViewButton.svelte +3 -2
  25. package/dist/components/detailView/update/updateDetailViewButton.svelte.d.ts +1 -1
  26. package/dist/components/detailView/utils.d.ts +17 -0
  27. package/dist/components/miniSidebar.svelte +4 -4
  28. package/dist/components/rangeCalendarButton.svelte +3 -3
  29. package/dist/components/routes/collections/collection.svelte +3 -3
  30. package/dist/components/routes/collections/collections.svelte +2 -2
  31. package/dist/components/routes/data_model/dataModel.svelte +2 -2
  32. package/dist/components/routes/data_model/syncManager.svelte +4 -4
  33. package/dist/components/routes/extensions/extension.svelte +1 -1
  34. package/dist/components/routes/home.svelte +1 -1
  35. package/dist/components/routes/workflows/workflows.svelte +5 -5
  36. package/dist/components/selectRecord.svelte +2 -21
  37. package/dist/components/setServerPage.svelte +1 -1
  38. package/dist/components/ui/alert-dialog/alert-dialog-action.svelte +1 -1
  39. package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte +1 -1
  40. package/dist/components/ui/command/command-dialog.svelte +1 -1
  41. package/dist/components/ui/range-calendar/range-calendar-day.svelte +1 -1
  42. package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +1 -1
  43. package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +1 -1
  44. package/dist/components/ui/select/select-separator.svelte +1 -1
  45. package/dist/components/workflowEditor.svelte +3 -3
  46. package/dist/store.types.d.ts +1 -1
  47. package/package.json +2 -2
  48. package/src/lib/components/Studio.svelte +7 -4
  49. package/src/lib/components/combobox.svelte +3 -3
  50. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +1 -1
  51. package/src/lib/components/dataTable/dataTable.svelte +74 -82
  52. package/src/lib/components/dataTable/filter.svelte +1 -1
  53. package/src/lib/components/dataTable/filterButton.svelte +1 -1
  54. package/src/lib/components/dataTable/header.svelte +33 -54
  55. package/src/lib/components/dataTable/sort.svelte +1 -1
  56. package/src/lib/components/dataTable/sortButton.svelte +2 -2
  57. package/src/lib/components/detailView/create/children.svelte +1 -1
  58. package/src/lib/components/detailView/create/createDetailView.svelte +77 -42
  59. package/src/lib/components/detailView/create/createDetailViewButton.svelte +2 -2
  60. package/src/lib/components/detailView/create/createManyView.svelte +10 -6
  61. package/src/lib/components/detailView/fieldInput.svelte +1 -1
  62. package/src/lib/components/detailView/fieldInputReplacement.svelte +1 -1
  63. package/src/lib/components/detailView/update/detailViewChildren.svelte +15 -26
  64. package/src/lib/components/detailView/update/updateDetailView.svelte +85 -27
  65. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +3 -2
  66. package/src/lib/components/detailView/utils.ts +13 -0
  67. package/src/lib/components/miniSidebar.svelte +4 -4
  68. package/src/lib/components/rangeCalendarButton.svelte +3 -3
  69. package/src/lib/components/routes/collections/collection.svelte +3 -3
  70. package/src/lib/components/routes/collections/collections.svelte +2 -2
  71. package/src/lib/components/routes/data_model/dataModel.svelte +2 -2
  72. package/src/lib/components/routes/data_model/syncManager.svelte +4 -4
  73. package/src/lib/components/routes/extensions/extension.svelte +1 -1
  74. package/src/lib/components/routes/home.svelte +1 -1
  75. package/src/lib/components/routes/workflows/workflows.svelte +5 -5
  76. package/src/lib/components/selectRecord.svelte +2 -21
  77. package/src/lib/components/setServerPage.svelte +1 -1
  78. package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +1 -1
  79. package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +1 -1
  80. package/src/lib/components/ui/command/command-dialog.svelte +1 -1
  81. package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +1 -1
  82. package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +1 -1
  83. package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +1 -1
  84. package/src/lib/components/ui/select/select-separator.svelte +1 -1
  85. package/src/lib/components/workflowEditor.svelte +3 -3
  86. package/src/lib/store.types.ts +1 -1
  87. package/vite-plugins/index.js +2 -4
  88. package/vite-plugins/lobb-extensions.js +12 -0
  89. package/vite-plugins/utils.js +15 -0
  90. package/vite-plugins/workspace-fs-allow.js +33 -0
  91. package/vite-plugins/contextual-lib-alias.js +0 -67
  92. package/vite-plugins/workspace-optimize.js +0 -106
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getStudioContext } from "../../context";
3
-
4
- const { lobb, ctx } = getStudioContext();
3
+ import type { Changes, ChildrenChanges } from "../detailView/utils";
4
+ import type { ParentContext } from "./dataTable.svelte";
5
5
  import { Download, ListRestart, Plus, Trash, Link } from "lucide-svelte";
6
6
  import * as Tooltip from "../ui/tooltip";
7
7
  import LlmButton from "../LlmButton.svelte";
@@ -15,14 +15,15 @@
15
15
  import ExtensionsComponents from "../extensionsComponents.svelte";
16
16
  import { getExtensionUtils } from "../../extensions/extensionUtils";
17
17
  import type { Snippet } from "svelte";
18
- import type { ParentContext, RecordOperation } from "./dataTable.svelte";
18
+
19
+ const { lobb, ctx } = getStudioContext();
19
20
 
20
21
  interface Props {
21
22
  collectionName: string;
22
23
  params: any;
23
24
  selectedRecords: string[];
24
25
  parentContext?: ParentContext;
25
- onOperation?: (op: RecordOperation) => void;
26
+ changes?: ChildrenChanges;
26
27
  showImport?: boolean;
27
28
  left?: Snippet<[]>;
28
29
  }
@@ -32,37 +33,27 @@
32
33
  params = $bindable(),
33
34
  selectedRecords = $bindable(),
34
35
  parentContext,
35
- onOperation,
36
+ changes,
36
37
  showImport = true,
37
38
  left
38
39
  }: Props = $props();
39
40
 
40
- async function handleLink(selected: any) {
41
- if (!parentContext) return;
42
- if (onOperation) {
43
- onOperation({ type: "link", record: selected });
44
- } else {
45
- await lobb.updateOne(
46
- parentContext.collectionName,
47
- String(parentContext.recordId),
48
- {},
49
- { [collectionName]: { link: [selected.id] } },
50
- );
41
+ // Local changes object for the create form (recording mode only)
42
+ let createChanges = $state<Changes>({ data: {}, children: {} });
43
+
44
+ function handleLink(record: any) {
45
+ if (changes) {
46
+ changes.linked.push(record);
47
+ } else if (parentContext) {
48
+ lobb.updateOne(parentContext.collectionName, String(parentContext.recordId), {}, { [collectionName]: { link: [record.id] } });
51
49
  resetTable();
52
50
  }
53
51
  }
54
52
 
55
- async function handleChildCreate(formData: any) {
56
- if (!parentContext) return;
57
- if (onOperation) {
58
- onOperation({ type: "create", record: formData });
53
+ async function handleCreateSuccess(snap: any) {
54
+ if (changes) {
55
+ changes.created.push({ data: snap.data });
59
56
  } else {
60
- await lobb.updateOne(
61
- parentContext.collectionName,
62
- String(parentContext.recordId),
63
- {},
64
- { [collectionName]: { create: [formData] } },
65
- );
66
57
  resetTable();
67
58
  }
68
59
  }
@@ -199,37 +190,25 @@
199
190
  refresh={() => { params = { ...params }; }}
200
191
  />
201
192
  {#if parentContext}
202
- {#if parentContext}
203
- <SelectRecord
204
- {collectionName}
205
- variant="outline"
206
- class="h-7 px-3 text-xs font-normal"
207
- Icon={Link}
208
- onSelect={handleLink}
209
- >
210
- {headerIsSmall ? "" : "Link"}
211
- </SelectRecord>
212
- {/if}
213
- <CreateDetailViewButton
193
+ <SelectRecord
214
194
  {collectionName}
215
- variant="default"
216
- class="h-7 px-3 text-xs font-normal"
217
- Icon={Plus}
218
- rollback={true}
219
- onSuccessfullSave={handleChildCreate}
220
- >
221
- {headerIsSmall ? "" : "Create"}
222
- </CreateDetailViewButton>
223
- {:else}
224
- <CreateDetailViewButton
225
- {collectionName}
226
- variant="default"
195
+ variant="outline"
227
196
  class="h-7 px-3 text-xs font-normal"
228
- Icon={Plus}
229
- onSuccessfullSave={() => (params = { ...params })}
197
+ Icon={Link}
198
+ onSelect={handleLink}
230
199
  >
231
- {headerIsSmall ? "" : "Create"}
232
- </CreateDetailViewButton>
200
+ {headerIsSmall ? "" : "Link"}
201
+ </SelectRecord>
233
202
  {/if}
203
+ <CreateDetailViewButton
204
+ {collectionName}
205
+ variant="default"
206
+ class="h-7 px-3 text-xs font-normal"
207
+ Icon={Plus}
208
+ changes={changes ? createChanges : undefined}
209
+ onSuccessfullSave={handleCreateSuccess}
210
+ >
211
+ {headerIsSmall ? "" : "Create"}
212
+ </CreateDetailViewButton>
234
213
  </div>
235
214
  </div>
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import * as _ from "lodash-es";
3
3
 
4
- import * as Popover from "../../components/ui/popover/index.js";
4
+ import * as Popover from "../ui/popover/index.js";
5
5
  import { ArrowDown, ArrowUp, GripVertical, Plus, X } from "lucide-svelte";
6
6
  import Button, { buttonVariants } from "../ui/button/button.svelte";
7
7
  import Label from "../ui/label/label.svelte";
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
- import * as Popover from "../../components/ui/popover/index.js";
2
+ import * as Popover from "../ui/popover/index.js";
3
3
  import { ArrowDownWideNarrow } from "lucide-svelte";
4
- import { buttonVariants } from "../../components/ui/button";
4
+ import { buttonVariants } from "../ui/button";
5
5
  import Sort from "./sort.svelte";
6
6
 
7
7
  interface Props {
@@ -2,7 +2,7 @@
2
2
  import { getStudioContext } from "../../../context";
3
3
  import { Link } from "lucide-svelte";
4
4
  import CreateManyView from "./createManyView.svelte";
5
- import ExtensionsComponents from "../../../components/extensionsComponents.svelte";
5
+ import ExtensionsComponents from "../../extensionsComponents.svelte";
6
6
  import { getExtensionUtils } from "../../../extensions/extensionUtils";
7
7
 
8
8
  const { ctx, lobb } = getStudioContext();
@@ -8,93 +8,128 @@
8
8
  collectionName: string;
9
9
  values?: Record<string, any>;
10
10
  showRelatedRecords?: boolean;
11
- rollback?: boolean;
12
11
  submitButton?: SubmitButton;
13
12
  title?: Snippet<[string]>;
14
13
  onSuccessfullSave?: (entry: any) => Promise<void>;
15
14
  onCancel?: () => Promise<void>;
15
+ changes?: import("../utils").Changes | undefined;
16
16
  }
17
17
  </script>
18
18
 
19
19
  <script lang="ts">
20
20
  import { ArrowLeft, Plus, X } from "lucide-svelte";
21
- import Button from "../../../components/ui/button/button.svelte";
21
+ import Button from "../../ui/button/button.svelte";
22
22
  import { getStudioContext } from "../../../context";
23
23
  import { toast } from "svelte-sonner";
24
+ import { untrack } from "svelte";
24
25
 
25
26
  const { lobb, ctx } = getStudioContext();
26
27
  import ExtensionsComponents from "../../extensionsComponents.svelte";
27
28
  import { getExtensionUtils } from "../../../extensions/extensionUtils";
28
29
  import { getField, getFieldIcon } from "../../dataTable/utils";
29
30
  import Children from "./children.svelte";
30
- import {
31
- buildChildren,
32
- getDefaultEntry,
33
- } from "../utils";
31
+ import { buildChildren, getDefaultEntry } from "../utils";
32
+ import type { Changes } from "../utils";
34
33
  import { getChangedProperties } from "../../../utils";
35
34
  import type { Snippet } from "svelte";
36
35
  import FieldInput from "../fieldInput.svelte";
37
- import { emitEvent } from "../../../eventSystem";
38
- import Drawer from "../../../components/drawer.svelte";
36
+ import Drawer from "../../drawer.svelte";
39
37
 
40
38
  let {
41
39
  collectionName,
42
40
  values = {},
43
41
  showRelatedRecords = true,
44
- rollback = false,
45
42
  onCancel,
46
43
  onSuccessfullSave,
47
44
  title,
48
45
  submitButton,
46
+ changes = $bindable<Changes | undefined>(undefined),
49
47
  }: CreateDetailViewProp = $props();
50
48
 
49
+ let _changes = $state<Changes>({ data: {}, children: {} });
50
+ const isRecordingMode = $derived(changes !== undefined);
51
+
51
52
  const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
52
53
  let entry: Record<string, any> = $state(
53
54
  getDefaultEntry(ctx, fieldNames, collectionName, values),
54
55
  );
55
56
  const initialEntry = $state.snapshot(entry);
56
57
  let fieldsErrors: Record<string, any> = $state({});
57
- const subCollections = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName).map((relation) => relation.from.collection);
58
+
59
+ const childCollections = ctx.meta.relations
60
+ .filter((r) => r.to.collection === collectionName)
61
+ .map((r) => (r as any).from.collection);
62
+
63
+ const subCollections = childCollections;
58
64
  const subCollectionsValues: Record<string, any> = {};
59
- for (let index = 0; index < subCollections.length; index++) {
60
- const subCollection = subCollections[index];
61
- if (values[subCollection]) {
62
- subCollectionsValues[subCollection] = values[subCollection];
63
- }
65
+ for (const col of subCollections) {
66
+ if (values[col]) subCollectionsValues[col] = values[col];
64
67
  }
65
68
 
66
- async function handleSave() {
67
- let localEntry = getChangedProperties(initialEntry, $state.snapshot(entry));
69
+ $effect(() => {
70
+ const snap = $state.snapshot(entry);
68
71
 
69
- await emitEvent({ lobb, ctx }, "studio.collections.preCreate", {
70
- collectionName,
71
- entry: localEntry,
72
- });
72
+ untrack(() => {
73
+ const target = changes ?? _changes;
74
+ const data: Record<string, any> = {};
75
+ const children: Record<string, any> = {};
73
76
 
74
- // remove empty children records data
75
- for (const [key, value] of Object.entries(localEntry)) {
76
- if (Array.isArray(value) && !value.length) {
77
- delete localEntry[key];
77
+ for (const [key, value] of Object.entries(snap)) {
78
+ if (childCollections.includes(key) && Array.isArray(value)) {
79
+ children[key] = {
80
+ created: (value as any[]).filter((r) => !r.id).map((r) => ({ data: r })),
81
+ updated: [],
82
+ deleted: [],
83
+ linked: (value as any[]).filter((r) => r.id).map((r) => r.id),
84
+ unlinked: [],
85
+ };
86
+ } else if (value !== null && value !== undefined && value !== '') {
87
+ data[key] = value;
88
+ }
78
89
  }
79
- }
80
90
 
81
- if (rollback) {
82
- if (onSuccessfullSave) await onSuccessfullSave(localEntry);
83
- onCancel?.();
84
- return;
91
+ target.data = data;
92
+ target.children = children;
93
+
94
+ if (!isRecordingMode) {
95
+ console.log(`[${collectionName}] changes:`, $state.snapshot(target));
96
+ }
97
+ });
98
+ });
99
+
100
+ function handleCancel() {
101
+ if (changes !== undefined) {
102
+ changes.data = {};
103
+ changes.children = {};
85
104
  }
105
+ onCancel?.();
106
+ }
107
+
108
+ async function handleSave() {
109
+ const target = changes ?? _changes;
110
+ const snap = $state.snapshot(target);
86
111
 
87
- const children = buildChildren(ctx, collectionName, localEntry);
88
- const response = await lobb.createOne(collectionName, localEntry, children);
112
+ const children = buildChildren(ctx, collectionName, { ...snap.data, ...Object.fromEntries(
113
+ Object.entries(snap.children).map(([col, ops]) => [
114
+ col,
115
+ [
116
+ ...(ops.created.map((c) => c.data)),
117
+ ...(ops.linked.map((id) => ({ id }))),
118
+ ]
119
+ ])
120
+ )});
89
121
 
90
- await emitEvent({ lobb, ctx }, "studio.collections.create", {
91
- collectionName,
92
- entry: localEntry,
93
- response: response,
94
- });
122
+ const response = await lobb.createOne(collectionName, snap.data, children, undefined, isRecordingMode);
123
+
124
+ if (response.status === 204) {
125
+ if (onSuccessfullSave) await onSuccessfullSave(snap);
126
+ toast.success(`The record was successfully created`);
127
+ handleCancel();
128
+ return;
129
+ }
95
130
 
96
131
  if (!response.bodyUsed) {
97
- let result = await response.json();
132
+ const result = await response.json();
98
133
  if (response.status >= 400) {
99
134
  if (result.message && result.details) {
100
135
  fieldsErrors = result.details;
@@ -105,17 +140,17 @@
105
140
  }
106
141
  }
107
142
 
108
- if (onSuccessfullSave) await onSuccessfullSave(localEntry);
143
+ if (onSuccessfullSave) await onSuccessfullSave(snap);
109
144
  toast.success(`The record was successfully created`);
110
145
  onCancel?.();
111
146
  }
112
147
  </script>
113
148
 
114
- <Drawer onHide={onCancel}>
149
+ <Drawer onHide={handleCancel}>
115
150
  <div class="flex h-12 items-center gap-4 border-b px-4">
116
151
  <Button
117
152
  variant="outline"
118
- onclick={onCancel}
153
+ onclick={handleCancel}
119
154
  class=" h-8 w-8 rounded-full text-xs font-normal"
120
155
  Icon={ArrowLeft}
121
156
  ></Button>
@@ -175,7 +210,7 @@
175
210
  <div class="flex gap-3">
176
211
  <Button
177
212
  variant="outline"
178
- onclick={onCancel}
213
+ onclick={handleCancel}
179
214
  class="h-7 px-3 text-xs font-normal"
180
215
  Icon={X}
181
216
  >
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { CreateDetailViewProp } from "./createDetailView.svelte";
3
- import type { ButtonProps } from "../../../components/ui/button/button.svelte";
4
- import Button from "../../../components/ui/button/button.svelte";
3
+ import type { ButtonProps } from "../../ui/button/button.svelte";
4
+ import Button from "../../ui/button/button.svelte";
5
5
  import CreateDetailView from "./createDetailView.svelte";
6
6
 
7
7
  interface LocalProp extends CreateDetailViewProp {
@@ -11,11 +11,15 @@
11
11
  import Table, { type TableProps } from "../../dataTable/table.svelte";
12
12
  import Button from "../../ui/button/button.svelte";
13
13
  import CreateDetailViewButton from "./createDetailViewButton.svelte";
14
- import { getCollectionColumns } from "../../../components/dataTable/utils";
15
- import SelectRecord from "../../../components/selectRecord.svelte";
16
- import FieldCell from "../../../components/dataTable/fieldCell.svelte";
14
+ import type { Changes } from "../utils";
15
+
16
+ let addChanges = $state<Changes>({ data: {}, children: {} });
17
+ let editChanges = $state<Changes>({ data: {}, children: {} });
18
+ import { getCollectionColumns } from "../../dataTable/utils";
19
+ import SelectRecord from "../../selectRecord.svelte";
20
+ import FieldCell from "../../dataTable/fieldCell.svelte";
17
21
  import SubRecords from "./subRecords.svelte";
18
- import ListViewChildren from "../../../components/dataTable/listViewChildren.svelte";
22
+ import ListViewChildren from "../../dataTable/listViewChildren.svelte";
19
23
  import { getStudioContext } from "../../../context";
20
24
 
21
25
  const { ctx } = getStudioContext();
@@ -141,7 +145,7 @@
141
145
  class="h-7 px-2 font-normal text-xs"
142
146
  Icon={Plus}
143
147
  {collectionName}
144
- rollback={true}
148
+ changes={addChanges}
145
149
  showRelatedRecords={true}
146
150
  onSuccessfullSave={onRecordAdd}
147
151
  values={createValues}
@@ -197,7 +201,7 @@
197
201
  class="h-6 w-6 text-muted-foreground hover:bg-transparent p-0"
198
202
  Icon={Pencil}
199
203
  {collectionName}
200
- rollback={true}
204
+ changes={editChanges}
201
205
  showRelatedRecords={true}
202
206
  onSuccessfullSave={(entry) =>
203
207
  onRecordOverride(entry, index)}
@@ -6,7 +6,7 @@
6
6
  import Button from "../ui/button/button.svelte";
7
7
  import FieldCustomInput from "./fieldCustomInput.svelte";
8
8
  import Input from "../ui/input/input.svelte";
9
- import * as Select from "../../components/ui/select/index";
9
+ import * as Select from "../ui/select/index";
10
10
  import EnumBadge from "../dataTable/enumBadge.svelte";
11
11
  import type { EnumOption } from "@lobb-js/core";
12
12
  import Textarea from "../ui/textarea/textarea.svelte";
@@ -2,7 +2,7 @@
2
2
  import { Ban, Check, CircleAlert, X } from "lucide-svelte";
3
3
  import Button from "../ui/button/button.svelte";
4
4
  import Input from "../ui/input/input.svelte";
5
- import * as Select from "../../components/ui/select/index";
5
+ import * as Select from "../ui/select/index";
6
6
  import Textarea from "../ui/textarea/textarea.svelte";
7
7
  import type { EntryField } from "./detailViewForm.svelte";
8
8
  import type { Snippet } from "svelte";
@@ -1,43 +1,32 @@
1
1
  <script lang="ts">
2
- import DataTable, { type RecordOperation } from "../../../components/dataTable/dataTable.svelte";
2
+ import DataTable from "../../dataTable/dataTable.svelte";
3
3
  import { getStudioContext } from "../../../context";
4
4
  import { Table, Link } from "lucide-svelte";
5
5
 
6
6
  const { ctx } = getStudioContext();
7
7
 
8
- type PendingOps = {
9
- link?: (string | number)[];
10
- unlink?: (string | number)[];
11
- delete?: (string | number)[];
12
- create?: any[];
13
- };
8
+ import type { Changes, ChildrenChanges } from "../utils";
14
9
 
15
10
  interface LocalProp {
16
11
  collectionName: string;
17
12
  entry: any;
18
- pendingChildren?: Record<string, PendingOps>;
13
+ activeChanges?: Changes;
19
14
  }
20
15
 
21
- let { collectionName, entry, pendingChildren = $bindable({}) }: LocalProp = $props();
22
-
23
- function makeHandler(collection: string) {
24
- return (op: RecordOperation) => {
25
- if (!pendingChildren[collection]) pendingChildren[collection] = {};
26
- const c = pendingChildren[collection];
27
- if (op.type === "link") {
28
- (c.link ??= []).push(op.record.id);
29
- } else if (op.type === "unlink") {
30
- (c.unlink ??= []).push(op.id);
31
- } else if (op.type === "delete") {
32
- (c.delete ??= []).push(op.id);
33
- } else if (op.type === "create") {
34
- (c.create ??= []).push(op.record);
35
- }
36
- };
37
- }
16
+ let { collectionName, entry, activeChanges }: LocalProp = $props();
38
17
 
39
18
  const children = (ctx.meta.collections[collectionName]?.children ?? [])
40
19
  .filter((c: any) => c.type === "fk" || c.type === "m2m" || c.type === "polymorphic");
20
+
21
+ // Ensure all child collection slots exist before the template renders so bind:changes works
22
+ $effect.pre(() => {
23
+ if (!activeChanges) return;
24
+ for (const child of children) {
25
+ if (!activeChanges.children[child.collection]) {
26
+ activeChanges.children[child.collection] = { created: [], updated: [], deleted: [], linked: [], unlinked: [] };
27
+ }
28
+ }
29
+ });
41
30
  </script>
42
31
 
43
32
  {#if children.length}
@@ -52,7 +41,7 @@
52
41
  collectionName={child.collection}
53
42
  searchParams={{ children_of: collectionName, parent_id: entry.id }}
54
43
  parentContext={{ collectionName, recordId: entry.id }}
55
- onOperation={makeHandler(child.collection)}
44
+ bind:changes={activeChanges!.children[child.collection]}
56
45
  showImport={false}
57
46
  showHeader={true}
58
47
  showFooter={true}