@lobb-js/studio 0.29.0 → 0.29.1
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 +1 -0
- package/dist/actions.d.ts +2 -0
- package/dist/components/Studio.svelte +39 -43
- package/dist/components/StudioRoot.svelte +19 -0
- package/dist/components/StudioRoot.svelte.d.ts +6 -0
- package/dist/components/breadCrumbs.svelte +5 -4
- package/dist/components/codeEditor.svelte +1 -1
- package/dist/components/dataTable/dataTable.svelte +35 -20
- package/dist/components/dataTable/dataTable.svelte.d.ts +2 -1
- package/dist/components/dataTable/dataTableTabs.svelte +4 -2
- package/dist/components/dataTable/dataTableTabs.svelte.d.ts +2 -0
- package/dist/components/dataTable/header.svelte +15 -11
- package/dist/components/dataTable/header.svelte.d.ts +1 -0
- package/dist/components/dataTable/listViewChildren.svelte +4 -6
- package/dist/components/dataTable/listViewChildren.svelte.d.ts +0 -1
- package/dist/components/dataTable/table.svelte +8 -10
- package/dist/components/dataTable/table.svelte.d.ts +0 -1
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte +4 -1
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte.d.ts +2 -0
- package/dist/components/detailView/create/children.svelte +1 -1
- package/dist/components/detailView/create/createDetailView.svelte +19 -61
- package/dist/components/detailView/create/createManyView.svelte +2 -4
- package/dist/components/detailView/detailView.svelte +81 -0
- package/dist/components/detailView/detailView.svelte.d.ts +8 -0
- package/dist/components/detailView/fieldInput.svelte +10 -10
- package/dist/components/detailView/fieldInputReplacement.svelte +7 -7
- package/dist/components/detailView/passwordInput.svelte +1 -1
- package/dist/components/detailView/update/updateDetailView.svelte +32 -69
- package/dist/components/diffViewer.svelte +1 -1
- package/dist/components/extensionsComponents.svelte +3 -1
- package/dist/components/foreingKeyInput.svelte +2 -2
- package/dist/components/importButton.svelte +12 -9
- package/dist/components/landing.svelte +7 -0
- package/dist/components/landing.svelte.d.ts +6 -14
- package/dist/components/miniSidebar.svelte +86 -15
- package/dist/components/miniSidebar.svelte.d.ts +2 -17
- package/dist/components/polymorphicInput.svelte +1 -1
- package/dist/components/rangeCalendarButton.svelte +10 -10
- package/dist/components/richTextEditor.svelte +1 -1
- package/dist/components/routes/collections/collections.svelte +32 -10
- package/dist/components/routes/data_model/dataModel.svelte +6 -28
- package/dist/components/routes/data_model/dataModel.svelte.d.ts +17 -2
- package/dist/components/routes/extensions/publicExtension.svelte +19 -0
- package/dist/components/routes/extensions/publicExtension.svelte.d.ts +13 -0
- package/dist/components/routes/home.svelte +2 -2
- package/dist/components/routes/workflows/workflows.svelte +4 -4
- package/dist/components/sidebar/sidebar.svelte +1 -1
- package/dist/components/sidebar/sidebarElements.svelte +4 -4
- package/dist/components/singletone.svelte +4 -6
- package/dist/components/ui/button/button.svelte +2 -3
- package/dist/components/workflowEditor.svelte +2 -2
- package/dist/eventSystem.d.ts +1 -1
- package/dist/eventSystem.js +7 -5
- package/dist/extensions/extension.types.d.ts +38 -14
- package/dist/extensions/extensionUtils.js +4 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/store.types.d.ts +1 -1
- package/dist/studioLifecycle.svelte.d.ts +2 -0
- package/dist/studioLifecycle.svelte.js +15 -0
- package/package.json +3 -4
- package/src/app.css +3 -0
- package/src/lib/actions.ts +2 -0
- package/src/lib/components/Studio.svelte +39 -43
- package/src/lib/components/StudioRoot.svelte +19 -0
- package/src/lib/components/breadCrumbs.svelte +5 -4
- package/src/lib/components/codeEditor.svelte +1 -1
- package/src/lib/components/dataTable/dataTable.svelte +35 -20
- package/src/lib/components/dataTable/dataTableTabs.svelte +4 -2
- package/src/lib/components/dataTable/header.svelte +15 -11
- package/src/lib/components/dataTable/listViewChildren.svelte +4 -6
- package/src/lib/components/dataTable/table.svelte +8 -10
- package/src/lib/components/dataTableDrawer/dataTableDrawer.svelte +4 -1
- package/src/lib/components/detailView/create/children.svelte +1 -1
- package/src/lib/components/detailView/create/createDetailView.svelte +19 -61
- package/src/lib/components/detailView/create/createManyView.svelte +2 -4
- package/src/lib/components/detailView/detailView.svelte +81 -0
- package/src/lib/components/detailView/fieldInput.svelte +10 -10
- package/src/lib/components/detailView/fieldInputReplacement.svelte +7 -7
- package/src/lib/components/detailView/passwordInput.svelte +1 -1
- package/src/lib/components/detailView/update/updateDetailView.svelte +32 -69
- package/src/lib/components/diffViewer.svelte +1 -1
- package/src/lib/components/extensionsComponents.svelte +3 -1
- package/src/lib/components/foreingKeyInput.svelte +2 -2
- package/src/lib/components/importButton.svelte +12 -9
- package/src/lib/components/landing.svelte +7 -0
- package/src/lib/components/miniSidebar.svelte +86 -15
- package/src/lib/components/polymorphicInput.svelte +1 -1
- package/src/lib/components/rangeCalendarButton.svelte +10 -10
- package/src/lib/components/richTextEditor.svelte +1 -1
- package/src/lib/components/routes/collections/collections.svelte +32 -10
- package/src/lib/components/routes/data_model/dataModel.svelte +6 -28
- package/src/lib/components/routes/extensions/publicExtension.svelte +19 -0
- package/src/lib/components/routes/home.svelte +2 -2
- package/src/lib/components/routes/workflows/workflows.svelte +4 -4
- package/src/lib/components/sidebar/sidebar.svelte +1 -1
- package/src/lib/components/sidebar/sidebarElements.svelte +4 -4
- package/src/lib/components/singletone.svelte +4 -6
- package/src/lib/components/ui/button/button.svelte +2 -3
- package/src/lib/components/workflowEditor.svelte +2 -2
- package/src/lib/eventSystem.ts +8 -7
- package/src/lib/extensions/extension.types.ts +39 -6
- package/src/lib/extensions/extensionUtils.ts +4 -2
- package/src/lib/index.ts +3 -1
- package/src/lib/store.types.ts +1 -1
- package/src/lib/studioLifecycle.svelte.ts +17 -0
- package/dist/components/routes/data_model/syncManager.svelte +0 -94
- package/dist/components/routes/data_model/syncManager.svelte.d.ts +0 -3
- package/src/lib/components/routes/data_model/syncManager.svelte +0 -94
|
@@ -24,8 +24,11 @@
|
|
|
24
24
|
import type { Changes, ChildrenChanges } from "../detailView/utils";
|
|
25
25
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
26
26
|
import { getExtensionUtils, loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
27
|
+
import { emitEvent } from "../../eventSystem";
|
|
28
|
+
import { onMount } from "svelte";
|
|
27
29
|
import Tabs from "./dataTableTabs.svelte";
|
|
28
30
|
import { fade } from "svelte/transition";
|
|
31
|
+
import type { CollectionTab } from "../../store.types";
|
|
29
32
|
|
|
30
33
|
const { lobb, ctx } = getStudioContext();
|
|
31
34
|
|
|
@@ -38,9 +41,9 @@
|
|
|
38
41
|
showHeader?: boolean;
|
|
39
42
|
showFooter?: boolean;
|
|
40
43
|
showImport?: boolean;
|
|
41
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
42
44
|
showDelete?: boolean;
|
|
43
45
|
tableProps?: Partial<TableProps>;
|
|
46
|
+
tabs?: CollectionTab[];
|
|
44
47
|
headerLeft?: Snippet<[]>;
|
|
45
48
|
}
|
|
46
49
|
|
|
@@ -53,12 +56,26 @@
|
|
|
53
56
|
showHeader = true,
|
|
54
57
|
showFooter = true,
|
|
55
58
|
showImport = true,
|
|
56
|
-
unifiedBgColor,
|
|
57
59
|
showDelete = false,
|
|
58
60
|
tableProps,
|
|
61
|
+
tabs,
|
|
59
62
|
headerLeft,
|
|
60
63
|
}: Props = $props();
|
|
61
64
|
|
|
65
|
+
// Gate row/header buttons by the current user's permissions:
|
|
66
|
+
// - showUpdate → per-row edit button
|
|
67
|
+
// - showCreate → header's Create + Import buttons (passed to Header)
|
|
68
|
+
let showUpdate = $state(false);
|
|
69
|
+
let showCreate = $state(false);
|
|
70
|
+
onMount(async () => {
|
|
71
|
+
const [update, create] = await Promise.all([
|
|
72
|
+
emitEvent({ lobb, ctx }, "auth.canAccess", { collection: collectionName, action: "update" }),
|
|
73
|
+
emitEvent({ lobb, ctx }, "auth.canAccess", { collection: collectionName, action: "create" }),
|
|
74
|
+
]);
|
|
75
|
+
showUpdate = update === true;
|
|
76
|
+
showCreate = create === true;
|
|
77
|
+
});
|
|
78
|
+
|
|
62
79
|
function getOrCreateUpdatedSlot(recordId: string): Changes | undefined {
|
|
63
80
|
if (!changes) return undefined;
|
|
64
81
|
let slot = changes.updated.find((u) => String(u.id) === String(recordId));
|
|
@@ -191,10 +208,7 @@
|
|
|
191
208
|
|
|
192
209
|
<div
|
|
193
210
|
bind:clientWidth={dataTableContainerWidth}
|
|
194
|
-
class="
|
|
195
|
-
flex flex-col overflow-auto h-full w-full
|
|
196
|
-
{unifiedBgColor ? unifiedBgColor : ''}
|
|
197
|
-
"
|
|
211
|
+
class="flex flex-col overflow-auto h-full w-full"
|
|
198
212
|
>
|
|
199
213
|
{#snippet rowActionsSnippet(entry: Record<string, any>)}
|
|
200
214
|
<ExtensionsComponents
|
|
@@ -212,6 +226,7 @@
|
|
|
212
226
|
{collectionName}
|
|
213
227
|
bind:selectedRecords
|
|
214
228
|
{showImport}
|
|
229
|
+
{showCreate}
|
|
215
230
|
{parentContext}
|
|
216
231
|
{changes}
|
|
217
232
|
>
|
|
@@ -220,7 +235,7 @@
|
|
|
220
235
|
{/snippet}
|
|
221
236
|
</Header>
|
|
222
237
|
{/if}
|
|
223
|
-
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
238
|
+
<Tabs {collectionName} {filter} {tabs} bind:activeTabFilter />
|
|
224
239
|
<div class="relative flex-1 overflow-auto w-full">
|
|
225
240
|
{#key activeTabFilter}
|
|
226
241
|
<div class="h-full w-full" in:fade={{ duration: 120 }}>
|
|
@@ -240,22 +255,23 @@
|
|
|
240
255
|
showLastColumnBorder={true}
|
|
241
256
|
bind:sort={params.sort}
|
|
242
257
|
bind:selectedRecords
|
|
243
|
-
{unifiedBgColor}
|
|
244
258
|
bind:tableWidth={dataTableWidth}
|
|
245
259
|
{...tableProps}
|
|
246
260
|
rowActions={hasRowActions ? rowActionsSnippet : undefined}>
|
|
247
261
|
{#snippet tools(entry)}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
262
|
+
{#if showUpdate}
|
|
263
|
+
<UpdateDetailViewButton
|
|
264
|
+
{collectionName}
|
|
265
|
+
recordId={entry.id}
|
|
266
|
+
variant="ghost"
|
|
267
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
268
|
+
Icon={Pencil}
|
|
269
|
+
changes={getOrCreateUpdatedSlot(String(entry.id))}
|
|
270
|
+
onSuccessfullSave={async () => {
|
|
271
|
+
params = { ...params };
|
|
272
|
+
}}
|
|
273
|
+
></UpdateDetailViewButton>
|
|
274
|
+
{/if}
|
|
259
275
|
{#if parentContext}
|
|
260
276
|
<Button
|
|
261
277
|
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
@@ -293,7 +309,6 @@
|
|
|
293
309
|
width={dataTableWidth > dataTableContainerWidth
|
|
294
310
|
? dataTableContainerWidth
|
|
295
311
|
: dataTableWidth}
|
|
296
|
-
unifiedBgColor={unifiedBgColor ?? "bg-background"}
|
|
297
312
|
/>
|
|
298
313
|
{/snippet}
|
|
299
314
|
</Table>
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getStudioContext } from "../../context";
|
|
3
|
+
import type { CollectionTab } from "../../store.types";
|
|
3
4
|
|
|
4
5
|
const { lobb, ctx } = getStudioContext();
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
collectionName: string;
|
|
8
9
|
filter?: any;
|
|
10
|
+
tabs?: CollectionTab[];
|
|
9
11
|
activeTabFilter?: any;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
let { collectionName, filter, activeTabFilter = $bindable() }: Props = $props();
|
|
14
|
+
let { collectionName, filter, tabs: tabsProp, activeTabFilter = $bindable() }: Props = $props();
|
|
13
15
|
|
|
14
|
-
const tabs = ctx.meta.collections[collectionName].ui?.tabs;
|
|
16
|
+
const tabs: CollectionTab[] | undefined = $derived(tabsProp ?? ctx.meta.collections[collectionName].ui?.tabs);
|
|
15
17
|
let activeTab = $state<string | null>(null);
|
|
16
18
|
let tabCounts = $state<Record<string, number>>({});
|
|
17
19
|
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
parentContext?: ParentContext;
|
|
26
26
|
changes?: ChildrenChanges;
|
|
27
27
|
showImport?: boolean;
|
|
28
|
+
showCreate?: boolean;
|
|
28
29
|
left?: Snippet<[]>;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
parentContext,
|
|
36
37
|
changes,
|
|
37
38
|
showImport = true,
|
|
39
|
+
showCreate = false,
|
|
38
40
|
left
|
|
39
41
|
}: Props = $props();
|
|
40
42
|
|
|
@@ -167,7 +169,7 @@
|
|
|
167
169
|
>
|
|
168
170
|
{headerIsSmall ? "" : "Refresh"}
|
|
169
171
|
</Button>
|
|
170
|
-
{#if showImport}
|
|
172
|
+
{#if showImport && showCreate}
|
|
171
173
|
<Tooltip.Provider delayDuration={0}>
|
|
172
174
|
<Tooltip.Root>
|
|
173
175
|
<Tooltip.Trigger>
|
|
@@ -200,15 +202,17 @@
|
|
|
200
202
|
{headerIsSmall ? "" : "Link"}
|
|
201
203
|
</SelectRecord>
|
|
202
204
|
{/if}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
{#if showCreate}
|
|
206
|
+
<CreateDetailViewButton
|
|
207
|
+
{collectionName}
|
|
208
|
+
variant="default"
|
|
209
|
+
class="h-7 px-3 text-xs font-normal"
|
|
210
|
+
Icon={Plus}
|
|
211
|
+
changes={changes ? createChanges : undefined}
|
|
212
|
+
onSuccessfullSave={handleCreateSuccess}
|
|
213
|
+
>
|
|
214
|
+
{headerIsSmall ? "" : "Create"}
|
|
215
|
+
</CreateDetailViewButton>
|
|
216
|
+
{/if}
|
|
213
217
|
</div>
|
|
214
218
|
</div>
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
collectionName: string;
|
|
13
13
|
recordId: string;
|
|
14
14
|
width: number;
|
|
15
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
let { collectionName, recordId, width
|
|
17
|
+
let { collectionName, recordId, width }: Props = $props();
|
|
19
18
|
|
|
20
19
|
const children = (ctx.meta.collections[collectionName]?.children ?? [])
|
|
21
20
|
.filter((c: any) => c.type === "fk" || c.type === "m2m" || c.type === "polymorphic");
|
|
@@ -27,13 +26,13 @@
|
|
|
27
26
|
|
|
28
27
|
<div class="flex" style="width: {width}px;">
|
|
29
28
|
<div
|
|
30
|
-
class="flex justify-center border-r
|
|
29
|
+
class="flex justify-center border-r bg-background"
|
|
31
30
|
style="width: 40px"
|
|
32
31
|
></div>
|
|
33
32
|
<div class="flex-1 flex flex-col">
|
|
34
33
|
{#each children as child, index}
|
|
35
34
|
{@const lastRow = children.length - 1 === index}
|
|
36
|
-
<div class="overflow-hidden
|
|
35
|
+
<div class="overflow-hidden bg-background">
|
|
37
36
|
<div
|
|
38
37
|
bind:clientWidth={tableHeaderWidth}
|
|
39
38
|
class="flex justify-between items-center gap-2 text-sm h-10 {expandedRows[index] || !lastRow ? 'border-b' : ''}"
|
|
@@ -75,7 +74,7 @@
|
|
|
75
74
|
{#if expandedRows[index]}
|
|
76
75
|
<div class="flex max-h-96 overflow-auto {lastRow ? '' : 'border-b'}">
|
|
77
76
|
<div
|
|
78
|
-
class="border-r
|
|
77
|
+
class="border-r"
|
|
79
78
|
style="width: 100vw; max-width: 40px"
|
|
80
79
|
></div>
|
|
81
80
|
<div class="flex-1" style="width: {tableHeaderWidth - 40}px;">
|
|
@@ -92,7 +91,6 @@
|
|
|
92
91
|
showHeader={false}
|
|
93
92
|
showFooter={false}
|
|
94
93
|
showDelete={child.type === "fk"}
|
|
95
|
-
{unifiedBgColor}
|
|
96
94
|
tableProps={{ showLastRowBorder: false, showLastColumnBorder: false, showCheckboxes: false }}
|
|
97
95
|
/>
|
|
98
96
|
</ExtensionsComponents>
|
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
|
|
39
39
|
// other
|
|
40
40
|
parentWidth?: number;
|
|
41
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
42
41
|
select?: Select;
|
|
43
42
|
tableWidth?: number;
|
|
44
43
|
}
|
|
@@ -77,7 +76,6 @@
|
|
|
77
76
|
tools,
|
|
78
77
|
rowActions,
|
|
79
78
|
collapsible,
|
|
80
|
-
unifiedBgColor,
|
|
81
79
|
select,
|
|
82
80
|
tableWidth = $bindable(),
|
|
83
81
|
}: TableProps = $props();
|
|
@@ -184,7 +182,7 @@
|
|
|
184
182
|
flex items-center p-2.5 text-xs h-10
|
|
185
183
|
border-r border-b gap-2
|
|
186
184
|
{headerBorderTop ? 'border-t' : ''}
|
|
187
|
-
|
|
185
|
+
bg-muted-soft
|
|
188
186
|
"
|
|
189
187
|
>
|
|
190
188
|
<!-- collapsable toggle -->
|
|
@@ -209,7 +207,7 @@
|
|
|
209
207
|
class="
|
|
210
208
|
sticky top-0 z-10
|
|
211
209
|
flex items-center p-2.5 text-xs h-10
|
|
212
|
-
|
|
210
|
+
bg-muted-soft
|
|
213
211
|
{lastColumn && !showLastColumnBorder ? '' : 'border-r'}
|
|
214
212
|
border-b gap-2
|
|
215
213
|
{headerBorderTop ? 'border-t' : ''}
|
|
@@ -236,7 +234,7 @@
|
|
|
236
234
|
class="
|
|
237
235
|
sticky top-0 right-0 z-20
|
|
238
236
|
flex items-center p-2.5 h-10
|
|
239
|
-
|
|
237
|
+
bg-muted-soft
|
|
240
238
|
border-l border-b
|
|
241
239
|
{headerBorderTop ? 'border-t' : ''}
|
|
242
240
|
"
|
|
@@ -251,7 +249,7 @@
|
|
|
251
249
|
class="
|
|
252
250
|
sticky left-0
|
|
253
251
|
flex items-center p-2.5 text-xs h-10
|
|
254
|
-
|
|
252
|
+
bg-background
|
|
255
253
|
border-r gap-2
|
|
256
254
|
"
|
|
257
255
|
>
|
|
@@ -298,7 +296,7 @@
|
|
|
298
296
|
class="
|
|
299
297
|
flex items-center p-2.5 text-xs h-10 text-nowrap overflow-clip
|
|
300
298
|
{select ? 'cursor-pointer' : ''}
|
|
301
|
-
|
|
299
|
+
bg-background
|
|
302
300
|
{lastColumn && !showLastColumnBorder ? '' : 'border-r'}
|
|
303
301
|
"
|
|
304
302
|
>
|
|
@@ -315,7 +313,7 @@
|
|
|
315
313
|
sticky right-0 z-10
|
|
316
314
|
flex items-center p-2.5 text-xs h-10
|
|
317
315
|
border-l gap-2
|
|
318
|
-
|
|
316
|
+
bg-background
|
|
319
317
|
"
|
|
320
318
|
>
|
|
321
319
|
{@render rowActions?.(entry, index)}
|
|
@@ -336,8 +334,8 @@
|
|
|
336
334
|
{expandedRows[index] ? '' : 'height: 0px;'}
|
|
337
335
|
"
|
|
338
336
|
class="
|
|
339
|
-
sticky left-0 top-0 overflow-auto bg-muted
|
|
340
|
-
|
|
337
|
+
sticky left-0 top-0 overflow-auto bg-muted-soft
|
|
338
|
+
|
|
341
339
|
{expandedRows[index] ? 'border-t' : ''}
|
|
342
340
|
"
|
|
343
341
|
>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import DataTable from "../dataTable/dataTable.svelte";
|
|
5
5
|
import Drawer from "../drawer.svelte";
|
|
6
6
|
import type { TableProps } from "../dataTable/table.svelte";
|
|
7
|
+
import type { CollectionTab } from "../../store.types";
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
collectionName: string;
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
showFooter?: boolean;
|
|
14
15
|
tableProps?: Partial<TableProps>;
|
|
15
16
|
position?: "side" | "bottom";
|
|
17
|
+
tabs?: CollectionTab[];
|
|
16
18
|
onClose?: () => void;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -24,6 +26,7 @@
|
|
|
24
26
|
showFooter = true,
|
|
25
27
|
tableProps,
|
|
26
28
|
position = "side",
|
|
29
|
+
tabs,
|
|
27
30
|
onClose,
|
|
28
31
|
}: Props = $props();
|
|
29
32
|
</script>
|
|
@@ -47,7 +50,7 @@
|
|
|
47
50
|
{showHeader}
|
|
48
51
|
{showFooter}
|
|
49
52
|
{tableProps}
|
|
50
|
-
|
|
53
|
+
{tabs}
|
|
51
54
|
/>
|
|
52
55
|
</div>
|
|
53
56
|
</Drawer>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
parentCollectionName={collectionName}
|
|
38
38
|
collectionName={child.collection}
|
|
39
39
|
parentRecord={{ id: entry.id, collectionName }}
|
|
40
|
-
class="bg-muted
|
|
40
|
+
class="bg-muted-soft border rounded-md overflow-hidden"
|
|
41
41
|
bind:value={entry[child.collection]}
|
|
42
42
|
>
|
|
43
43
|
<CreateManyView
|
|
@@ -24,53 +24,46 @@
|
|
|
24
24
|
import { untrack } from "svelte";
|
|
25
25
|
|
|
26
26
|
const { lobb, ctx } = getStudioContext();
|
|
27
|
-
import ExtensionsComponents from "../../extensionsComponents.svelte";
|
|
28
|
-
import { getExtensionUtils } from "../../../extensions/extensionUtils";
|
|
29
|
-
import { getField, getFieldIcon } from "../../dataTable/utils";
|
|
30
27
|
import Children from "./children.svelte";
|
|
31
28
|
import { buildChildren, getDefaultEntry } from "../utils";
|
|
32
29
|
import type { Changes } from "../utils";
|
|
33
30
|
import { getChangedProperties } from "../../../utils";
|
|
34
31
|
import type { Snippet } from "svelte";
|
|
35
|
-
import
|
|
32
|
+
import DetailView from "../detailView.svelte";
|
|
36
33
|
import Drawer from "../../drawer.svelte";
|
|
37
34
|
|
|
38
35
|
let {
|
|
39
36
|
collectionName,
|
|
40
|
-
values = {},
|
|
37
|
+
values: passedValues = {} as Record<string, any>,
|
|
41
38
|
showRelatedRecords = true,
|
|
42
39
|
onCancel,
|
|
43
40
|
onSuccessfullSave,
|
|
44
41
|
title,
|
|
45
42
|
submitButton,
|
|
46
|
-
changes = $bindable<Changes | undefined>(undefined),
|
|
43
|
+
changes: passedChanges = $bindable<Changes | undefined>(undefined),
|
|
47
44
|
}: CreateDetailViewProp = $props();
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
const isRecordingMode = passedChanges !== undefined;
|
|
47
|
+
if (!isRecordingMode) passedChanges = { data: {}, children: {} };
|
|
48
|
+
const changes = passedChanges as Changes;
|
|
51
49
|
|
|
52
50
|
const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
53
|
-
let
|
|
54
|
-
getDefaultEntry(ctx, fieldNames, collectionName, values),
|
|
55
|
-
);
|
|
56
|
-
const initialEntry = $state.snapshot(entry);
|
|
51
|
+
let values = $state(getDefaultEntry(ctx, fieldNames, collectionName, passedValues));
|
|
57
52
|
let fieldsErrors: Record<string, any> = $state({});
|
|
58
53
|
|
|
59
54
|
const childCollections = ctx.meta.relations
|
|
60
55
|
.filter((r) => r.to.collection === collectionName)
|
|
61
56
|
.map((r) => (r as any).from.collection);
|
|
62
57
|
|
|
63
|
-
const subCollections = childCollections;
|
|
64
58
|
const subCollectionsValues: Record<string, any> = {};
|
|
65
|
-
for (const col of
|
|
66
|
-
if (
|
|
59
|
+
for (const col of childCollections) {
|
|
60
|
+
if (passedValues[col]) subCollectionsValues[col] = passedValues[col];
|
|
67
61
|
}
|
|
68
62
|
|
|
69
63
|
$effect(() => {
|
|
70
|
-
const snap = $state.snapshot(
|
|
64
|
+
const snap = $state.snapshot(values);
|
|
71
65
|
|
|
72
66
|
untrack(() => {
|
|
73
|
-
const target = changes ?? _changes;
|
|
74
67
|
const data: Record<string, any> = {};
|
|
75
68
|
const children: Record<string, any> = {};
|
|
76
69
|
|
|
@@ -88,17 +81,17 @@
|
|
|
88
81
|
}
|
|
89
82
|
}
|
|
90
83
|
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
changes.data = data;
|
|
85
|
+
changes.children = children;
|
|
93
86
|
|
|
94
87
|
if (!isRecordingMode) {
|
|
95
|
-
console.log(`[${collectionName}] changes:`, $state.snapshot(
|
|
88
|
+
console.log(`[${collectionName}] changes:`, $state.snapshot(changes));
|
|
96
89
|
}
|
|
97
90
|
});
|
|
98
91
|
});
|
|
99
92
|
|
|
100
93
|
function handleCancel() {
|
|
101
|
-
if (
|
|
94
|
+
if (isRecordingMode) {
|
|
102
95
|
changes.data = {};
|
|
103
96
|
changes.children = {};
|
|
104
97
|
}
|
|
@@ -106,14 +99,13 @@
|
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
async function handleSave() {
|
|
109
|
-
const
|
|
110
|
-
const snap = $state.snapshot(target);
|
|
102
|
+
const snap = $state.snapshot(changes);
|
|
111
103
|
|
|
112
104
|
const children = buildChildren(ctx, collectionName, { ...snap.data, ...Object.fromEntries(
|
|
113
105
|
Object.entries(snap.children).map(([col, ops]) => [
|
|
114
106
|
col,
|
|
115
107
|
[
|
|
116
|
-
...(ops.created.map((
|
|
108
|
+
...(ops.created.map((op) => op.data)),
|
|
117
109
|
...(ops.linked.map((id) => ({ id }))),
|
|
118
110
|
]
|
|
119
111
|
])
|
|
@@ -135,6 +127,7 @@
|
|
|
135
127
|
fieldsErrors = result.details;
|
|
136
128
|
return;
|
|
137
129
|
} else if (result.message) {
|
|
130
|
+
toast.error(result.message);
|
|
138
131
|
return;
|
|
139
132
|
}
|
|
140
133
|
}
|
|
@@ -166,44 +159,9 @@
|
|
|
166
159
|
</div>
|
|
167
160
|
</div>
|
|
168
161
|
<div class="flex-1 overflow-y-auto">
|
|
169
|
-
<
|
|
170
|
-
{#each fieldNames as fieldName}
|
|
171
|
-
{#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
|
|
172
|
-
{@const field = getField(ctx, fieldName, collectionName)}
|
|
173
|
-
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
174
|
-
<div class="flex flex-col gap-2">
|
|
175
|
-
<div class="flex flex-1 items-end justify-between gap-2 text-xs">
|
|
176
|
-
<div class="flex gap-2">
|
|
177
|
-
<div class="h-fit">{field.label}</div>
|
|
178
|
-
<div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
|
|
179
|
-
<FieldIcon size="12" />
|
|
180
|
-
{field.type}
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
<div>
|
|
184
|
-
<ExtensionsComponents
|
|
185
|
-
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
186
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
187
|
-
bind:value={entry[fieldName]}
|
|
188
|
-
/>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
<FieldInput
|
|
192
|
-
{collectionName}
|
|
193
|
-
{fieldName}
|
|
194
|
-
bind:value={
|
|
195
|
-
() => entry[fieldName],
|
|
196
|
-
(v) => (entry = { ...entry, [fieldName]: v })
|
|
197
|
-
}
|
|
198
|
-
bind:entry
|
|
199
|
-
errorMessages={fieldsErrors[fieldName]}
|
|
200
|
-
/>
|
|
201
|
-
</div>
|
|
202
|
-
{/if}
|
|
203
|
-
{/each}
|
|
204
|
-
</div>
|
|
162
|
+
<DetailView {collectionName} bind:entry={values} {fieldsErrors} />
|
|
205
163
|
{#if showRelatedRecords}
|
|
206
|
-
<Children {collectionName} values={subCollectionsValues} bind:entry />
|
|
164
|
+
<Children {collectionName} values={subCollectionsValues} bind:entry={values} />
|
|
207
165
|
{/if}
|
|
208
166
|
</div>
|
|
209
167
|
<div class="flex h-12 items-center justify-end gap-2 border-t px-4">
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
>
|
|
113
113
|
<div
|
|
114
114
|
class="
|
|
115
|
-
flex items-center justify-between px-2 h-10 bg-muted
|
|
115
|
+
flex items-center justify-between px-2 h-10 bg-muted-soft
|
|
116
116
|
{expanded ? 'border-b' : ''}
|
|
117
117
|
"
|
|
118
118
|
>
|
|
@@ -165,13 +165,12 @@
|
|
|
165
165
|
</div>
|
|
166
166
|
</div>
|
|
167
167
|
{#if expanded}
|
|
168
|
-
<div bind:clientWidth={tableWidth} class="bg-muted
|
|
168
|
+
<div bind:clientWidth={tableWidth} class="bg-muted-soft overflow-auto">
|
|
169
169
|
<Table
|
|
170
170
|
data={entries}
|
|
171
171
|
{columns}
|
|
172
172
|
selectByColumn="id"
|
|
173
173
|
showCollapsible={doesCollectionHasChildren}
|
|
174
|
-
unifiedBgColor="bg-muted/30"
|
|
175
174
|
>
|
|
176
175
|
{#snippet tools(entry, index)}
|
|
177
176
|
<Button
|
|
@@ -240,7 +239,6 @@
|
|
|
240
239
|
{collectionName}
|
|
241
240
|
recordId={entry.id}
|
|
242
241
|
width={tableWidth}
|
|
243
|
-
unifiedBgColor="bg-muted/30"
|
|
244
242
|
/>
|
|
245
243
|
{:else}
|
|
246
244
|
<SubRecords
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { CircleHelp } from "lucide-svelte";
|
|
3
|
+
import * as Tooltip from "../ui/tooltip";
|
|
4
|
+
import { getStudioContext } from "../../context";
|
|
5
|
+
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
6
|
+
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
7
|
+
import { getField, getFieldIcon } from "../dataTable/utils";
|
|
8
|
+
import FieldInput from "./fieldInput.svelte";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
collectionName: string;
|
|
12
|
+
entry: Record<string, any>;
|
|
13
|
+
fieldsErrors?: Record<string, string[]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
collectionName,
|
|
18
|
+
entry = $bindable(),
|
|
19
|
+
fieldsErrors = {},
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
const { lobb, ctx } = getStudioContext();
|
|
23
|
+
const fieldNames = $derived(
|
|
24
|
+
Object.keys(ctx.meta.collections[collectionName].fields),
|
|
25
|
+
);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class="flex flex-col gap-4 p-4">
|
|
29
|
+
{#each fieldNames as fieldName}
|
|
30
|
+
{#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
|
|
31
|
+
{@const field = getField(ctx, fieldName, collectionName)}
|
|
32
|
+
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
33
|
+
{@const description = ctx.meta.collections[collectionName].fields[fieldName]?.description}
|
|
34
|
+
<div class="flex flex-col gap-2">
|
|
35
|
+
<div class="flex flex-1 items-end justify-between gap-2 text-xs">
|
|
36
|
+
<div class="flex items-center gap-1.5">
|
|
37
|
+
<ExtensionsComponents
|
|
38
|
+
name="detailView.field.label"
|
|
39
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
40
|
+
{collectionName}
|
|
41
|
+
{fieldName}
|
|
42
|
+
bind:value={entry[fieldName]}
|
|
43
|
+
>
|
|
44
|
+
<div class="flex items-center gap-1.5">
|
|
45
|
+
<div class="h-fit">{field.label}</div>
|
|
46
|
+
<div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
|
|
47
|
+
<FieldIcon size="12" />
|
|
48
|
+
{field.type}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</ExtensionsComponents>
|
|
52
|
+
{#if description}
|
|
53
|
+
<Tooltip.Root>
|
|
54
|
+
<Tooltip.Trigger>
|
|
55
|
+
<CircleHelp size="12" class="text-muted-foreground" />
|
|
56
|
+
</Tooltip.Trigger>
|
|
57
|
+
<Tooltip.Content class="max-w-64 text-xs">
|
|
58
|
+
{description}
|
|
59
|
+
</Tooltip.Content>
|
|
60
|
+
</Tooltip.Root>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
<div>
|
|
64
|
+
<ExtensionsComponents
|
|
65
|
+
name="dvFields.topRight.{collectionName}.{fieldName}"
|
|
66
|
+
utils={getExtensionUtils(lobb, ctx)}
|
|
67
|
+
bind:value={entry[fieldName]}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<FieldInput
|
|
72
|
+
{collectionName}
|
|
73
|
+
{fieldName}
|
|
74
|
+
bind:value={entry[fieldName]}
|
|
75
|
+
bind:entry
|
|
76
|
+
errorMessages={fieldsErrors[fieldName]}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
{/if}
|
|
80
|
+
{/each}
|
|
81
|
+
</div>
|