@lobb-js/studio 0.28.6 → 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 +46 -47
- 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/combobox.svelte +3 -3
- package/dist/components/confirmationDialog/confirmationDialog.svelte +1 -1
- package/dist/components/dataTable/dataTable.svelte +108 -101
- package/dist/components/dataTable/dataTable.svelte.d.ts +5 -20
- package/dist/components/dataTable/dataTableTabs.svelte +4 -2
- package/dist/components/dataTable/dataTableTabs.svelte.d.ts +2 -0
- package/dist/components/dataTable/filter.svelte +1 -1
- package/dist/components/dataTable/filterButton.svelte +1 -1
- package/dist/components/dataTable/header.svelte +30 -47
- package/dist/components/dataTable/header.svelte.d.ts +4 -2
- package/dist/components/dataTable/listViewChildren.svelte +4 -6
- package/dist/components/dataTable/listViewChildren.svelte.d.ts +0 -1
- package/dist/components/dataTable/sort.svelte +1 -1
- package/dist/components/dataTable/sortButton.svelte +2 -2
- 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 +2 -2
- package/dist/components/detailView/create/createDetailView.svelte +81 -88
- package/dist/components/detailView/create/createDetailView.svelte.d.ts +2 -2
- package/dist/components/detailView/create/createDetailViewButton.svelte +2 -2
- package/dist/components/detailView/create/createDetailViewButton.svelte.d.ts +1 -1
- package/dist/components/detailView/create/createManyView.svelte +12 -10
- package/dist/components/detailView/detailView.svelte +81 -0
- package/dist/components/detailView/detailView.svelte.d.ts +8 -0
- package/dist/components/detailView/fieldInput.svelte +11 -11
- package/dist/components/detailView/fieldInputReplacement.svelte +8 -8
- package/dist/components/detailView/passwordInput.svelte +1 -1
- package/dist/components/detailView/update/detailViewChildren.svelte +15 -26
- package/dist/components/detailView/update/detailViewChildren.svelte.d.ts +3 -8
- package/dist/components/detailView/update/updateDetailView.svelte +90 -69
- package/dist/components/detailView/update/updateDetailView.svelte.d.ts +2 -2
- package/dist/components/detailView/update/updateDetailViewButton.svelte +3 -2
- package/dist/components/detailView/update/updateDetailViewButton.svelte.d.ts +1 -1
- package/dist/components/detailView/utils.d.ts +17 -0
- 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 +90 -19
- package/dist/components/miniSidebar.svelte.d.ts +2 -17
- package/dist/components/polymorphicInput.svelte +1 -1
- package/dist/components/rangeCalendarButton.svelte +13 -13
- package/dist/components/richTextEditor.svelte +1 -1
- package/dist/components/routes/collections/collection.svelte +3 -3
- package/dist/components/routes/collections/collections.svelte +34 -12
- 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/extension.svelte +1 -1
- 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 +3 -3
- package/dist/components/routes/workflows/workflows.svelte +9 -9
- package/dist/components/selectRecord.svelte +2 -21
- package/dist/components/setServerPage.svelte +1 -1
- 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/alert-dialog/alert-dialog-action.svelte +1 -1
- package/dist/components/ui/alert-dialog/alert-dialog-cancel.svelte +1 -1
- package/dist/components/ui/button/button.svelte +2 -3
- package/dist/components/ui/command/command-dialog.svelte +1 -1
- package/dist/components/ui/range-calendar/range-calendar-day.svelte +1 -1
- package/dist/components/ui/range-calendar/range-calendar-next-button.svelte +1 -1
- package/dist/components/ui/range-calendar/range-calendar-prev-button.svelte +1 -1
- package/dist/components/ui/select/select-separator.svelte +1 -1
- package/dist/components/workflowEditor.svelte +5 -5
- 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 +2 -2
- 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 +46 -47
- 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/combobox.svelte +3 -3
- package/src/lib/components/confirmationDialog/confirmationDialog.svelte +1 -1
- package/src/lib/components/dataTable/dataTable.svelte +108 -101
- package/src/lib/components/dataTable/dataTableTabs.svelte +4 -2
- package/src/lib/components/dataTable/filter.svelte +1 -1
- package/src/lib/components/dataTable/filterButton.svelte +1 -1
- package/src/lib/components/dataTable/header.svelte +30 -47
- package/src/lib/components/dataTable/listViewChildren.svelte +4 -6
- package/src/lib/components/dataTable/sort.svelte +1 -1
- package/src/lib/components/dataTable/sortButton.svelte +2 -2
- 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 +2 -2
- package/src/lib/components/detailView/create/createDetailView.svelte +81 -88
- package/src/lib/components/detailView/create/createDetailViewButton.svelte +2 -2
- package/src/lib/components/detailView/create/createManyView.svelte +12 -10
- package/src/lib/components/detailView/detailView.svelte +81 -0
- package/src/lib/components/detailView/fieldInput.svelte +11 -11
- package/src/lib/components/detailView/fieldInputReplacement.svelte +8 -8
- package/src/lib/components/detailView/passwordInput.svelte +1 -1
- package/src/lib/components/detailView/update/detailViewChildren.svelte +15 -26
- package/src/lib/components/detailView/update/updateDetailView.svelte +90 -69
- package/src/lib/components/detailView/update/updateDetailViewButton.svelte +3 -2
- package/src/lib/components/detailView/utils.ts +13 -0
- 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 +90 -19
- package/src/lib/components/polymorphicInput.svelte +1 -1
- package/src/lib/components/rangeCalendarButton.svelte +13 -13
- package/src/lib/components/richTextEditor.svelte +1 -1
- package/src/lib/components/routes/collections/collection.svelte +3 -3
- package/src/lib/components/routes/collections/collections.svelte +34 -12
- package/src/lib/components/routes/data_model/dataModel.svelte +6 -28
- package/src/lib/components/routes/extensions/extension.svelte +1 -1
- package/src/lib/components/routes/extensions/publicExtension.svelte +19 -0
- package/src/lib/components/routes/home.svelte +3 -3
- package/src/lib/components/routes/workflows/workflows.svelte +9 -9
- package/src/lib/components/selectRecord.svelte +2 -21
- package/src/lib/components/setServerPage.svelte +1 -1
- 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/alert-dialog/alert-dialog-action.svelte +1 -1
- package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +1 -1
- package/src/lib/components/ui/button/button.svelte +2 -3
- package/src/lib/components/ui/command/command-dialog.svelte +1 -1
- package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +1 -1
- package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +1 -1
- package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +1 -1
- package/src/lib/components/ui/select/select-separator.svelte +1 -1
- package/src/lib/components/workflowEditor.svelte +5 -5
- 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 +2 -2
- package/src/lib/studioLifecycle.svelte.ts +17 -0
- package/vite-plugins/index.js +2 -4
- package/vite-plugins/utils.js +15 -0
- package/vite-plugins/{workspace-optimize.js → workspace-fs-allow.js} +4 -18
- 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
- package/vite-plugins/contextual-lib-alias.js +0 -67
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
recordId: string | number;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export type RecordOperation =
|
|
8
|
-
| { type: "link"; record: any }
|
|
9
|
-
| { type: "unlink"; id: string | number }
|
|
10
|
-
| { type: "delete"; id: string | number }
|
|
11
|
-
| { type: "create"; record: any }
|
|
12
|
-
| { type: "update"; id: string | number; data: any };
|
|
13
7
|
</script>
|
|
14
8
|
|
|
15
9
|
<script lang="ts">
|
|
@@ -20,19 +14,21 @@
|
|
|
20
14
|
import Table, { type TableProps } from "./table.svelte";
|
|
21
15
|
import { getCollectionColumns, getCollectionParamsFields } from "./utils";
|
|
22
16
|
import { Pencil, Trash, Unlink } from "lucide-svelte";
|
|
23
|
-
import * as icons from "lucide-svelte";
|
|
24
17
|
import ListViewChildren from "./listViewChildren.svelte";
|
|
25
18
|
import FieldCell from "./fieldCell.svelte";
|
|
26
19
|
import Skeleton from "../ui/skeleton/skeleton.svelte";
|
|
27
20
|
import Button from "../ui/button/button.svelte";
|
|
28
21
|
import { showDialog } from "../../actions";
|
|
29
22
|
import UpdateDetailViewButton from "../detailView/update/updateDetailViewButton.svelte";
|
|
30
|
-
import { emitEvent } from "../../eventSystem";
|
|
31
23
|
import type { Snippet } from "svelte";
|
|
24
|
+
import type { Changes, ChildrenChanges } from "../detailView/utils";
|
|
32
25
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
33
26
|
import { getExtensionUtils, loadExtensionComponents } from "../../extensions/extensionUtils";
|
|
27
|
+
import { emitEvent } from "../../eventSystem";
|
|
28
|
+
import { onMount } from "svelte";
|
|
34
29
|
import Tabs from "./dataTableTabs.svelte";
|
|
35
30
|
import { fade } from "svelte/transition";
|
|
31
|
+
import type { CollectionTab } from "../../store.types";
|
|
36
32
|
|
|
37
33
|
const { lobb, ctx } = getStudioContext();
|
|
38
34
|
|
|
@@ -41,13 +37,13 @@
|
|
|
41
37
|
filter?: any;
|
|
42
38
|
searchParams?: Record<string, any>;
|
|
43
39
|
parentContext?: ParentContext;
|
|
44
|
-
|
|
40
|
+
changes?: ChildrenChanges;
|
|
45
41
|
showHeader?: boolean;
|
|
46
42
|
showFooter?: boolean;
|
|
47
43
|
showImport?: boolean;
|
|
48
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
49
44
|
showDelete?: boolean;
|
|
50
45
|
tableProps?: Partial<TableProps>;
|
|
46
|
+
tabs?: CollectionTab[];
|
|
51
47
|
headerLeft?: Snippet<[]>;
|
|
52
48
|
}
|
|
53
49
|
|
|
@@ -56,16 +52,70 @@
|
|
|
56
52
|
filter,
|
|
57
53
|
searchParams,
|
|
58
54
|
parentContext,
|
|
59
|
-
|
|
55
|
+
changes = $bindable<ChildrenChanges | undefined>(undefined),
|
|
60
56
|
showHeader = true,
|
|
61
57
|
showFooter = true,
|
|
62
58
|
showImport = true,
|
|
63
|
-
unifiedBgColor,
|
|
64
59
|
showDelete = false,
|
|
65
60
|
tableProps,
|
|
61
|
+
tabs,
|
|
66
62
|
headerLeft,
|
|
67
63
|
}: Props = $props();
|
|
68
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
|
+
|
|
79
|
+
function getOrCreateUpdatedSlot(recordId: string): Changes | undefined {
|
|
80
|
+
if (!changes) return undefined;
|
|
81
|
+
let slot = changes.updated.find((u) => String(u.id) === String(recordId));
|
|
82
|
+
if (!slot) {
|
|
83
|
+
slot = { id: recordId, data: {}, children: {} };
|
|
84
|
+
changes.updated.push(slot);
|
|
85
|
+
}
|
|
86
|
+
return slot;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Derives the displayed rows by applying changes on top of server data.
|
|
90
|
+
// This is the single place responsible for optimistic UI — no handler touches data directly.
|
|
91
|
+
const data = $derived.by(() => {
|
|
92
|
+
if (!changes) return serverData;
|
|
93
|
+
|
|
94
|
+
const removedIds = new Set([
|
|
95
|
+
...changes.deleted.map((r) => String(r.id)),
|
|
96
|
+
...changes.unlinked.map((r) => String(r.id)),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
let result = serverData.filter((r: any) => !removedIds.has(String(r.id)));
|
|
100
|
+
|
|
101
|
+
result = result.map((r: any) => {
|
|
102
|
+
const update = changes.updated.find((u) => String(u.id) === String(r.id));
|
|
103
|
+
return update && Object.keys(update.data).length ? { ...r, ...update.data } : r;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (const record of changes.linked) {
|
|
107
|
+
if (!result.some((r: any) => String(r.id) === String(record.id))) {
|
|
108
|
+
result = [...result, record];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const item of changes.created) {
|
|
113
|
+
result = [...result, { ...item.data, _pending: true }];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
});
|
|
118
|
+
|
|
69
119
|
const hasRowActions = $derived(
|
|
70
120
|
loadExtensionComponents(ctx, "listView.entry.actions", undefined, { collectionName }).length > 0
|
|
71
121
|
);
|
|
@@ -89,7 +139,7 @@
|
|
|
89
139
|
|
|
90
140
|
let selectedRecords = $state([]);
|
|
91
141
|
let totalCount = $state(0);
|
|
92
|
-
let
|
|
142
|
+
let serverData: TableProps["data"] = $state([]);
|
|
93
143
|
let loading = $state(true);
|
|
94
144
|
const columns: TableProps["columns"] = $state(
|
|
95
145
|
getCollectionColumns(ctx, collectionName),
|
|
@@ -108,7 +158,6 @@
|
|
|
108
158
|
|
|
109
159
|
async function loadData(params: any) {
|
|
110
160
|
loading = true;
|
|
111
|
-
// parsing sort before sending the request
|
|
112
161
|
const paramsCopy = $state.snapshot(params);
|
|
113
162
|
const sort: TableProps["sort"] = paramsCopy.sort;
|
|
114
163
|
const sortStrings: string[] = [];
|
|
@@ -118,85 +167,48 @@
|
|
|
118
167
|
}
|
|
119
168
|
}
|
|
120
169
|
paramsCopy.sort = sortStrings.join(",");
|
|
121
|
-
|
|
122
|
-
// sending the request
|
|
123
170
|
const response = await lobb.findAll(collectionName, paramsCopy);
|
|
124
171
|
const res = await response.json();
|
|
125
|
-
|
|
126
|
-
data = res.data;
|
|
172
|
+
serverData = res.data;
|
|
127
173
|
totalCount = res.meta.totalCount;
|
|
128
|
-
|
|
129
174
|
loading = false;
|
|
130
175
|
}
|
|
131
176
|
|
|
132
|
-
// Internal handler: updates data optimistically then calls onOperation
|
|
133
|
-
function applyOperation(op: RecordOperation) {
|
|
134
|
-
if (op.type === "link") {
|
|
135
|
-
data = [...data, op.record];
|
|
136
|
-
} else if (op.type === "unlink" || op.type === "delete") {
|
|
137
|
-
data = data.filter((r: any) => String(r.id) !== String(op.id));
|
|
138
|
-
} else if (op.type === "create") {
|
|
139
|
-
data = [...data, { ...op.record, _pending: true }];
|
|
140
|
-
} else if (op.type === "update") {
|
|
141
|
-
data = data.map((r: any) => String(r.id) === String(op.id) ? { ...r, ...op.data } : r);
|
|
142
|
-
}
|
|
143
|
-
onOperation?.(op);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
177
|
async function handleDelete(entryId: string) {
|
|
147
178
|
const result = await showDialog("Are you sure?", "This will permanently delete the record.");
|
|
148
|
-
if (result)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
179
|
+
if (!result) return;
|
|
180
|
+
if (changes) {
|
|
181
|
+
const record = data.find((r: any) => String(r.id) === String(entryId));
|
|
182
|
+
if (record) changes.deleted.push($state.snapshot(record));
|
|
183
|
+
} else if (parentContext) {
|
|
184
|
+
serverData = serverData.filter((r: any) => String(r.id) !== String(entryId));
|
|
185
|
+
await lobb.updateOne(parentContext.collectionName, String(parentContext.recordId), {}, { [collectionName]: { delete: [entryId] } });
|
|
186
|
+
params = { ...params };
|
|
187
|
+
} else {
|
|
188
|
+
serverData = serverData.filter((r: any) => String(r.id) !== String(entryId));
|
|
189
|
+
await lobb.deleteOne(collectionName, entryId);
|
|
190
|
+
params = { ...params };
|
|
158
191
|
}
|
|
159
192
|
}
|
|
160
193
|
|
|
161
194
|
async function handleUnlink(entryId: string) {
|
|
162
195
|
const result = await showDialog("Are you sure?", "This will unlink the record without deleting it.");
|
|
163
|
-
if (result)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
196
|
+
if (!result) return;
|
|
197
|
+
if (changes) {
|
|
198
|
+
const record = data.find((r: any) => String(r.id) === String(entryId));
|
|
199
|
+
if (record) changes.unlinked.push($state.snapshot(record));
|
|
200
|
+
} else {
|
|
201
|
+
serverData = serverData.filter((r: any) => String(r.id) !== String(entryId));
|
|
202
|
+
await lobb.updateOne(parentContext!.collectionName, String(parentContext!.recordId), {}, { [collectionName]: { unlink: [entryId] } });
|
|
203
|
+
params = { ...params };
|
|
170
204
|
}
|
|
171
205
|
}
|
|
172
206
|
|
|
173
|
-
async function getWorkflowTools(
|
|
174
|
-
entry: Record<string, any>,
|
|
175
|
-
): Promise<any[]> {
|
|
176
|
-
// TODO: instead of firing the events like this. get them all the fire them one by one to get their results
|
|
177
|
-
const eventResult = await emitEvent(
|
|
178
|
-
{ lobb, ctx },
|
|
179
|
-
"studio.collections.listView.tools",
|
|
180
|
-
{
|
|
181
|
-
collectionName,
|
|
182
|
-
entry,
|
|
183
|
-
},
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
if (eventResult) {
|
|
187
|
-
return eventResult.tools;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return [];
|
|
191
|
-
}
|
|
192
207
|
</script>
|
|
193
208
|
|
|
194
209
|
<div
|
|
195
210
|
bind:clientWidth={dataTableContainerWidth}
|
|
196
|
-
class="
|
|
197
|
-
flex flex-col overflow-auto h-full w-full
|
|
198
|
-
{unifiedBgColor ? unifiedBgColor : ''}
|
|
199
|
-
"
|
|
211
|
+
class="flex flex-col overflow-auto h-full w-full"
|
|
200
212
|
>
|
|
201
213
|
{#snippet rowActionsSnippet(entry: Record<string, any>)}
|
|
202
214
|
<ExtensionsComponents
|
|
@@ -209,13 +221,21 @@
|
|
|
209
221
|
{/snippet}
|
|
210
222
|
|
|
211
223
|
{#if showHeader}
|
|
212
|
-
<Header
|
|
224
|
+
<Header
|
|
225
|
+
bind:params
|
|
226
|
+
{collectionName}
|
|
227
|
+
bind:selectedRecords
|
|
228
|
+
{showImport}
|
|
229
|
+
{showCreate}
|
|
230
|
+
{parentContext}
|
|
231
|
+
{changes}
|
|
232
|
+
>
|
|
213
233
|
{#snippet left()}
|
|
214
234
|
{@render headerLeft?.()}
|
|
215
235
|
{/snippet}
|
|
216
236
|
</Header>
|
|
217
237
|
{/if}
|
|
218
|
-
<Tabs {collectionName} {filter} bind:activeTabFilter />
|
|
238
|
+
<Tabs {collectionName} {filter} {tabs} bind:activeTabFilter />
|
|
219
239
|
<div class="relative flex-1 overflow-auto w-full">
|
|
220
240
|
{#key activeTabFilter}
|
|
221
241
|
<div class="h-full w-full" in:fade={{ duration: 120 }}>
|
|
@@ -235,21 +255,23 @@
|
|
|
235
255
|
showLastColumnBorder={true}
|
|
236
256
|
bind:sort={params.sort}
|
|
237
257
|
bind:selectedRecords
|
|
238
|
-
{unifiedBgColor}
|
|
239
258
|
bind:tableWidth={dataTableWidth}
|
|
240
259
|
{...tableProps}
|
|
241
260
|
rowActions={hasRowActions ? rowActionsSnippet : undefined}>
|
|
242
261
|
{#snippet tools(entry)}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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}
|
|
253
275
|
{#if parentContext}
|
|
254
276
|
<Button
|
|
255
277
|
class="h-6 w-6 text-muted-foreground hover:bg-transparent"
|
|
@@ -270,20 +292,6 @@
|
|
|
270
292
|
title="Delete permanently"
|
|
271
293
|
></Button>
|
|
272
294
|
{/if}
|
|
273
|
-
{#await getWorkflowTools($state.snapshot(entry))}
|
|
274
|
-
<div></div>
|
|
275
|
-
{:then workflowTools}
|
|
276
|
-
{#each workflowTools as workflowTool}
|
|
277
|
-
<Button
|
|
278
|
-
variant="ghost"
|
|
279
|
-
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
280
|
-
Icon={icons[
|
|
281
|
-
workflowTool.icon as keyof typeof icons
|
|
282
|
-
]}
|
|
283
|
-
onclick={workflowTool.onclick}
|
|
284
|
-
></Button>
|
|
285
|
-
{/each}
|
|
286
|
-
{/await}
|
|
287
295
|
{/snippet}
|
|
288
296
|
{#snippet overrideCell(value, column, entry)}
|
|
289
297
|
<FieldCell
|
|
@@ -301,7 +309,6 @@
|
|
|
301
309
|
width={dataTableWidth > dataTableContainerWidth
|
|
302
310
|
? dataTableContainerWidth
|
|
303
311
|
: dataTableWidth}
|
|
304
|
-
unifiedBgColor={unifiedBgColor ?? "bg-background"}
|
|
305
312
|
/>
|
|
306
313
|
{/snippet}
|
|
307
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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import * as Popover from "
|
|
2
|
+
import * as Popover from "../ui/popover/index.js";
|
|
3
3
|
import { ListFilter } from "lucide-svelte";
|
|
4
4
|
import { buttonVariants } from "../ui/button";
|
|
5
5
|
import Filter from "./filter.svelte";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getStudioContext } from "../../context";
|
|
3
|
-
|
|
4
|
-
|
|
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,15 +15,17 @@
|
|
|
15
15
|
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
16
16
|
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
17
17
|
import type { Snippet } from "svelte";
|
|
18
|
-
|
|
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
|
-
|
|
26
|
+
changes?: ChildrenChanges;
|
|
26
27
|
showImport?: boolean;
|
|
28
|
+
showCreate?: boolean;
|
|
27
29
|
left?: Snippet<[]>;
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -32,37 +34,28 @@
|
|
|
32
34
|
params = $bindable(),
|
|
33
35
|
selectedRecords = $bindable(),
|
|
34
36
|
parentContext,
|
|
35
|
-
|
|
37
|
+
changes,
|
|
36
38
|
showImport = true,
|
|
39
|
+
showCreate = false,
|
|
37
40
|
left
|
|
38
41
|
}: Props = $props();
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{},
|
|
49
|
-
{ [collectionName]: { link: [selected.id] } },
|
|
50
|
-
);
|
|
43
|
+
// Local changes object for the create form (recording mode only)
|
|
44
|
+
let createChanges = $state<Changes>({ data: {}, children: {} });
|
|
45
|
+
|
|
46
|
+
function handleLink(record: any) {
|
|
47
|
+
if (changes) {
|
|
48
|
+
changes.linked.push(record);
|
|
49
|
+
} else if (parentContext) {
|
|
50
|
+
lobb.updateOne(parentContext.collectionName, String(parentContext.recordId), {}, { [collectionName]: { link: [record.id] } });
|
|
51
51
|
resetTable();
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
async function
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
onOperation({ type: "create", record: formData });
|
|
55
|
+
async function handleCreateSuccess(snap: any) {
|
|
56
|
+
if (changes) {
|
|
57
|
+
changes.created.push({ data: snap.data });
|
|
59
58
|
} else {
|
|
60
|
-
await lobb.updateOne(
|
|
61
|
-
parentContext.collectionName,
|
|
62
|
-
String(parentContext.recordId),
|
|
63
|
-
{},
|
|
64
|
-
{ [collectionName]: { create: [formData] } },
|
|
65
|
-
);
|
|
66
59
|
resetTable();
|
|
67
60
|
}
|
|
68
61
|
}
|
|
@@ -176,7 +169,7 @@
|
|
|
176
169
|
>
|
|
177
170
|
{headerIsSmall ? "" : "Refresh"}
|
|
178
171
|
</Button>
|
|
179
|
-
{#if showImport}
|
|
172
|
+
{#if showImport && showCreate}
|
|
180
173
|
<Tooltip.Provider delayDuration={0}>
|
|
181
174
|
<Tooltip.Root>
|
|
182
175
|
<Tooltip.Trigger>
|
|
@@ -199,34 +192,24 @@
|
|
|
199
192
|
refresh={() => { params = { ...params }; }}
|
|
200
193
|
/>
|
|
201
194
|
{#if parentContext}
|
|
202
|
-
|
|
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
|
|
195
|
+
<SelectRecord
|
|
214
196
|
{collectionName}
|
|
215
|
-
variant="
|
|
197
|
+
variant="outline"
|
|
216
198
|
class="h-7 px-3 text-xs font-normal"
|
|
217
|
-
Icon={
|
|
218
|
-
|
|
219
|
-
onSuccessfullSave={handleChildCreate}
|
|
199
|
+
Icon={Link}
|
|
200
|
+
onSelect={handleLink}
|
|
220
201
|
>
|
|
221
|
-
{headerIsSmall ? "" : "
|
|
222
|
-
</
|
|
223
|
-
{
|
|
202
|
+
{headerIsSmall ? "" : "Link"}
|
|
203
|
+
</SelectRecord>
|
|
204
|
+
{/if}
|
|
205
|
+
{#if showCreate}
|
|
224
206
|
<CreateDetailViewButton
|
|
225
207
|
{collectionName}
|
|
226
208
|
variant="default"
|
|
227
209
|
class="h-7 px-3 text-xs font-normal"
|
|
228
210
|
Icon={Plus}
|
|
229
|
-
|
|
211
|
+
changes={changes ? createChanges : undefined}
|
|
212
|
+
onSuccessfullSave={handleCreateSuccess}
|
|
230
213
|
>
|
|
231
214
|
{headerIsSmall ? "" : "Create"}
|
|
232
215
|
</CreateDetailViewButton>
|
|
@@ -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>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as _ from "lodash-es";
|
|
3
3
|
|
|
4
|
-
import * as Popover from "
|
|
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 "
|
|
2
|
+
import * as Popover from "../ui/popover/index.js";
|
|
3
3
|
import { ArrowDownWideNarrow } from "lucide-svelte";
|
|
4
|
-
import { buttonVariants } from "
|
|
4
|
+
import { buttonVariants } from "../ui/button";
|
|
5
5
|
import Sort from "./sort.svelte";
|
|
6
6
|
|
|
7
7
|
interface Props {
|
|
@@ -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
|
>
|