@lobb-js/studio 0.25.0 → 0.27.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.
- package/dist/components/dataTable/dataTable.svelte +77 -14
- package/dist/components/dataTable/dataTable.svelte.d.ts +25 -0
- package/dist/components/dataTable/header.svelte +88 -24
- package/dist/components/dataTable/header.svelte.d.ts +4 -0
- package/dist/components/dataTable/listViewChildren.svelte +106 -0
- package/dist/components/dataTable/listViewChildren.svelte.d.ts +9 -0
- package/dist/components/dataTable/table.svelte +1 -1
- package/dist/components/detailView/create/createManyView.svelte +2 -2
- package/dist/components/detailView/update/detailViewChildren.svelte +72 -0
- package/dist/components/detailView/update/detailViewChildren.svelte.d.ts +14 -0
- package/dist/components/detailView/update/updateDetailView.svelte +6 -3
- package/dist/components/routes/collections/collections.svelte +44 -23
- package/dist/components/routes/data_model/dataModel.svelte +2 -8
- package/dist/components/routes/workflows/workflows.svelte +24 -11
- package/dist/components/sidebar/sidebar.svelte +12 -5
- package/dist/components/sidebar/sidebar.svelte.d.ts +1 -2
- package/dist/components/sidebar/sidebarElements.svelte +50 -75
- package/dist/components/sidebar/sidebarElements.svelte.d.ts +10 -3
- package/dist/utils.js +2 -1
- package/package.json +2 -2
- package/src/lib/components/dataTable/dataTable.svelte +77 -14
- package/src/lib/components/dataTable/header.svelte +88 -24
- package/src/lib/components/dataTable/listViewChildren.svelte +106 -0
- package/src/lib/components/dataTable/table.svelte +1 -1
- package/src/lib/components/detailView/create/createManyView.svelte +2 -2
- package/src/lib/components/detailView/update/detailViewChildren.svelte +72 -0
- package/src/lib/components/detailView/update/updateDetailView.svelte +6 -3
- package/src/lib/components/routes/collections/collections.svelte +44 -23
- package/src/lib/components/routes/data_model/dataModel.svelte +2 -8
- package/src/lib/components/routes/workflows/workflows.svelte +24 -11
- package/src/lib/components/sidebar/sidebar.svelte +12 -5
- package/src/lib/components/sidebar/sidebarElements.svelte +50 -75
- package/src/lib/utils.ts +2 -1
- package/dist/components/dataTable/childRecords.svelte +0 -142
- package/dist/components/dataTable/childRecords.svelte.d.ts +0 -9
- package/dist/components/detailView/update/children.svelte +0 -96
- package/dist/components/detailView/update/children.svelte.d.ts +0 -7
- package/src/lib/components/dataTable/childRecords.svelte +0 -142
- package/src/lib/components/detailView/update/children.svelte +0 -96
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
export interface SideBarElement {
|
|
3
|
+
type: "element";
|
|
3
4
|
name: string;
|
|
4
5
|
onclick?: () => Promise<void> | void;
|
|
5
6
|
href?: string;
|
|
6
7
|
icon?: any;
|
|
7
|
-
path?: string;
|
|
8
8
|
meta?: Record<string, any>;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
11
|
+
export interface SideBarDirectory {
|
|
12
|
+
type: "directory";
|
|
13
|
+
name: string;
|
|
14
|
+
icon?: any;
|
|
15
|
+
collapsed?: boolean;
|
|
16
|
+
children: SideBarNode[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SideBarNode = SideBarElement | SideBarDirectory;
|
|
20
|
+
export type SideBarData = SideBarNode[];
|
|
12
21
|
|
|
13
22
|
export interface SidebarElementsProps {
|
|
14
23
|
data: SideBarData;
|
|
15
|
-
path?: string[];
|
|
16
24
|
elementRightSide?: Snippet<[SideBarElement]>;
|
|
17
25
|
}
|
|
18
26
|
</script>
|
|
19
27
|
|
|
20
28
|
<script lang="ts">
|
|
21
|
-
import { Ban } from "lucide-svelte";
|
|
29
|
+
import { Ban, ChevronDown, Folder } from "lucide-svelte";
|
|
22
30
|
import type { Snippet } from "svelte";
|
|
23
31
|
import SidebarElements from "./sidebarElements.svelte";
|
|
24
32
|
import Button from "../ui/button/button.svelte";
|
|
@@ -26,117 +34,84 @@
|
|
|
26
34
|
|
|
27
35
|
let {
|
|
28
36
|
data = $bindable(),
|
|
29
|
-
path = [],
|
|
30
37
|
elementRightSide,
|
|
31
38
|
}: SidebarElementsProps = $props();
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (firstPath && item.path && !acc.includes(firstPath)) {
|
|
37
|
-
acc.push(firstPath);
|
|
38
|
-
} else if (!item.path) {
|
|
39
|
-
acc.push(item);
|
|
40
|
-
}
|
|
41
|
-
return acc;
|
|
42
|
-
},
|
|
43
|
-
[],
|
|
40
|
+
let expandedHeights: number[] = $state(Array(data.length).fill(0));
|
|
41
|
+
let collapsedDirs: boolean[] = $state(
|
|
42
|
+
data.map((node) => node.type === "directory" ? (node.collapsed ?? false) : false)
|
|
44
43
|
);
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
function toggleDir(index: number) {
|
|
46
|
+
collapsedDirs[index] = !collapsedDirs[index];
|
|
47
|
+
}
|
|
47
48
|
|
|
48
49
|
async function handleElementClick(element: SideBarElement) {
|
|
49
50
|
if (element.onclick) {
|
|
50
51
|
await element.onclick();
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
-
function getDirElements(dirName: string) {
|
|
55
|
-
let elements = data.filter((item) => item.path?.startsWith(dirName));
|
|
56
|
-
elements = elements.map((item) => {
|
|
57
|
-
return {
|
|
58
|
-
...item,
|
|
59
|
-
path: item.path?.split("/").slice(1).join("/"),
|
|
60
|
-
};
|
|
61
|
-
});
|
|
62
|
-
return elements;
|
|
63
|
-
}
|
|
64
54
|
</script>
|
|
65
55
|
|
|
66
56
|
<div class="flex flex-col">
|
|
67
|
-
{#if
|
|
68
|
-
{#each
|
|
69
|
-
{#if
|
|
70
|
-
{@const directoryName = element.split("/")[0]}
|
|
57
|
+
{#if data.length}
|
|
58
|
+
{#each data as node, index}
|
|
59
|
+
{#if node.type === "directory"}
|
|
71
60
|
<button
|
|
72
|
-
class="
|
|
73
|
-
|
|
74
|
-
rounded-md cursor-default
|
|
75
|
-
"
|
|
61
|
+
class="flex items-center justify-between p-2 gap-2 text-muted-foreground rounded-md hover:bg-muted/30 cursor-pointer"
|
|
62
|
+
onclick={() => toggleDir(index)}
|
|
76
63
|
>
|
|
77
64
|
<div class="flex items-center gap-2">
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
65
|
+
{#if node.icon}
|
|
66
|
+
<node.icon size="17.5" />
|
|
67
|
+
{:else}
|
|
68
|
+
<Folder size="17.5" />
|
|
69
|
+
{/if}
|
|
70
|
+
<div class="text-xs">{node.name}</div>
|
|
81
71
|
</div>
|
|
72
|
+
<ChevronDown
|
|
73
|
+
size="14"
|
|
74
|
+
class="transition-transform duration-200 {collapsedDirs[index] ? '-rotate-90' : ''}"
|
|
75
|
+
/>
|
|
82
76
|
</button>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
>
|
|
90
|
-
<div
|
|
91
|
-
bind:clientHeight={expandedHeights[index]}
|
|
92
|
-
class="border-l ml-4 pl-2"
|
|
93
|
-
>
|
|
94
|
-
<SidebarElements
|
|
95
|
-
data={getDirElements(element)}
|
|
96
|
-
path={[...path, element]}
|
|
97
|
-
{elementRightSide}
|
|
98
|
-
/>
|
|
99
|
-
</div>
|
|
77
|
+
<div
|
|
78
|
+
class="overflow-hidden transition-[height] duration-200"
|
|
79
|
+
style="height: {collapsedDirs[index] ? 0 : expandedHeights[index]}px;"
|
|
80
|
+
>
|
|
81
|
+
<div bind:clientHeight={expandedHeights[index]} class="border-l ml-4 pl-2">
|
|
82
|
+
<SidebarElements data={node.children} {elementRightSide} />
|
|
100
83
|
</div>
|
|
101
|
-
|
|
84
|
+
</div>
|
|
102
85
|
{:else}
|
|
103
|
-
{@const
|
|
104
|
-
{@const isselected = location.url.pathname === element.href}
|
|
86
|
+
{@const isselected = location.url.pathname === node.href}
|
|
105
87
|
<Button
|
|
106
|
-
onclick={() => handleElementClick(
|
|
107
|
-
href={
|
|
88
|
+
onclick={() => handleElementClick(node)}
|
|
89
|
+
href={node.href}
|
|
108
90
|
variant="ghost"
|
|
109
91
|
class="
|
|
110
92
|
flex items-center justify-between p-2 gap-2 hover:bg-muted/30 text-muted-foreground
|
|
111
93
|
rounded-md {isselected ? 'bg-muted' : ''}
|
|
112
94
|
"
|
|
113
|
-
title={
|
|
95
|
+
title={node.name}
|
|
114
96
|
>
|
|
115
97
|
<div class="flex items-center gap-2 truncate">
|
|
116
|
-
{#if
|
|
117
|
-
<
|
|
98
|
+
{#if node.icon}
|
|
99
|
+
<node.icon size="17.5" />
|
|
118
100
|
{/if}
|
|
119
|
-
<div
|
|
120
|
-
|
|
121
|
-
text-xs
|
|
122
|
-
{isselected ? 'text-primary font-medium' : ''}
|
|
123
|
-
"
|
|
124
|
-
>
|
|
125
|
-
{element.name}
|
|
101
|
+
<div class="text-xs {isselected ? 'text-primary font-medium' : ''}">
|
|
102
|
+
{node.name}
|
|
126
103
|
</div>
|
|
127
104
|
</div>
|
|
128
105
|
<div class="flex gap-2 items-center">
|
|
129
106
|
{#if elementRightSide}
|
|
130
|
-
{@render elementRightSide(
|
|
107
|
+
{@render elementRightSide(node)}
|
|
131
108
|
{/if}
|
|
132
109
|
</div>
|
|
133
110
|
</Button>
|
|
134
111
|
{/if}
|
|
135
112
|
{/each}
|
|
136
113
|
{:else}
|
|
137
|
-
<div
|
|
138
|
-
class="flex justify-center items-center gap-2 text-muted-foreground"
|
|
139
|
-
>
|
|
114
|
+
<div class="flex justify-center items-center gap-2 text-muted-foreground">
|
|
140
115
|
<Ban size="17.5" />
|
|
141
116
|
<div class="text-xs text-center">No result</div>
|
|
142
117
|
</div>
|
package/src/lib/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { clsx, type ClassValue } from "clsx";
|
|
2
2
|
import { twMerge } from "tailwind-merge";
|
|
3
|
+
import { isEqual } from "lodash";
|
|
3
4
|
|
|
4
5
|
import { MediaQuery } from 'svelte/reactivity';
|
|
5
6
|
|
|
@@ -34,7 +35,7 @@ export function calculateDrawerWidth() {
|
|
|
34
35
|
export function getChangedProperties(oldObj: Record<string, any>, newObj: Record<string, any>) {
|
|
35
36
|
const changes: Record<string, any> = {};
|
|
36
37
|
for (const key of Object.keys(newObj)) {
|
|
37
|
-
if (oldObj[key]
|
|
38
|
+
if (!isEqual(oldObj[key], newObj[key])) {
|
|
38
39
|
changes[key] = newObj[key];
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getStudioContext } from "../../context";
|
|
3
|
-
import { ChevronRight, Plus, Smartphone, Table } from "lucide-svelte";
|
|
4
|
-
import DataTable from "./dataTable.svelte";
|
|
5
|
-
import CreateDetailViewButton from "../detailView/create/createDetailViewButton.svelte";
|
|
6
|
-
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
7
|
-
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
8
|
-
|
|
9
|
-
const { ctx, lobb } = getStudioContext();
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
collectionName: string;
|
|
13
|
-
recordId: string;
|
|
14
|
-
width: number;
|
|
15
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let { collectionName, recordId, width, unifiedBgColor }: Props = $props();
|
|
19
|
-
|
|
20
|
-
const relations = ctx.meta.relations.filter(
|
|
21
|
-
(relation) => relation.to.collection === collectionName,
|
|
22
|
-
);
|
|
23
|
-
let expandedRows: boolean[] = $state(
|
|
24
|
-
new Array(relations.length).fill(false),
|
|
25
|
-
);
|
|
26
|
-
let refreshDataTable = $state(true);
|
|
27
|
-
let tableHeaderWidth = $state(0);
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<div class="flex" style="width: {width}px;">
|
|
31
|
-
<div
|
|
32
|
-
class="
|
|
33
|
-
flex justify-center border-r
|
|
34
|
-
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
35
|
-
"
|
|
36
|
-
style="width: 40px"
|
|
37
|
-
></div>
|
|
38
|
-
<div class="flex-1 flex flex-col">
|
|
39
|
-
{#each relations as relation, index}
|
|
40
|
-
{@const lastRow = relations.length - 1 === index}
|
|
41
|
-
{@const fromCollection = relation.from.collection}
|
|
42
|
-
{@const fromField = relation.from.field}
|
|
43
|
-
<div
|
|
44
|
-
class="
|
|
45
|
-
overflow-hidden
|
|
46
|
-
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
47
|
-
"
|
|
48
|
-
>
|
|
49
|
-
<div
|
|
50
|
-
bind:clientWidth={tableHeaderWidth}
|
|
51
|
-
class="
|
|
52
|
-
flex justify-between items-center gap-2 text-sm h-10
|
|
53
|
-
{expandedRows[index] || !lastRow ? 'border-b' : ''}
|
|
54
|
-
"
|
|
55
|
-
>
|
|
56
|
-
<button
|
|
57
|
-
class="flex gap-2 px-2 flex-1 h-full items-center"
|
|
58
|
-
onclick={() => {
|
|
59
|
-
expandedRows[index] = !expandedRows[index];
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
<ChevronRight
|
|
63
|
-
size="17.5"
|
|
64
|
-
class="text-muted-foreground transition-transform"
|
|
65
|
-
style={expandedRows[index]
|
|
66
|
-
? "transform: rotate(90deg);"
|
|
67
|
-
: "transform: rotate(0deg);"}
|
|
68
|
-
/>
|
|
69
|
-
<Table size="17.5" class="text-muted-foreground" />
|
|
70
|
-
<div class="text-muted-foreground">
|
|
71
|
-
{relation.from.collection}
|
|
72
|
-
</div>
|
|
73
|
-
</button>
|
|
74
|
-
<div class="flex items-center px-2">
|
|
75
|
-
<CreateDetailViewButton
|
|
76
|
-
collectionName={relation.from.collection}
|
|
77
|
-
variant="ghost"
|
|
78
|
-
class="h-7 px-3 text-xs font-normal"
|
|
79
|
-
Icon={Plus}
|
|
80
|
-
values={{
|
|
81
|
-
[fromField]: {
|
|
82
|
-
id: recordId,
|
|
83
|
-
},
|
|
84
|
-
}}
|
|
85
|
-
onSuccessfullSave={async () => {
|
|
86
|
-
refreshDataTable = !refreshDataTable;
|
|
87
|
-
}}
|
|
88
|
-
>
|
|
89
|
-
Create
|
|
90
|
-
</CreateDetailViewButton>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
{#if expandedRows[index]}
|
|
94
|
-
<div
|
|
95
|
-
class="
|
|
96
|
-
flex max-h-96 overflow-auto
|
|
97
|
-
{lastRow ? '' : 'border-b'}
|
|
98
|
-
"
|
|
99
|
-
>
|
|
100
|
-
<div
|
|
101
|
-
class="border-r {unifiedBgColor
|
|
102
|
-
? unifiedBgColor
|
|
103
|
-
: ''}"
|
|
104
|
-
style="width: 100vw; max-width: 40px"
|
|
105
|
-
></div>
|
|
106
|
-
<div
|
|
107
|
-
class="flex-1"
|
|
108
|
-
style="width: {tableHeaderWidth - 40}px;"
|
|
109
|
-
>
|
|
110
|
-
{#key refreshDataTable}
|
|
111
|
-
<ExtensionsComponents
|
|
112
|
-
name="listView.entry.children.{fromCollection}"
|
|
113
|
-
collectionName={fromCollection}
|
|
114
|
-
filter={{
|
|
115
|
-
[fromField]: recordId,
|
|
116
|
-
}}
|
|
117
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
118
|
-
>
|
|
119
|
-
<DataTable
|
|
120
|
-
collectionName={fromCollection}
|
|
121
|
-
filter={{
|
|
122
|
-
[fromField]: recordId,
|
|
123
|
-
}}
|
|
124
|
-
showHeader={false}
|
|
125
|
-
showFooter={false}
|
|
126
|
-
showDelete={true}
|
|
127
|
-
{unifiedBgColor}
|
|
128
|
-
tableProps={{
|
|
129
|
-
showLastRowBorder: false,
|
|
130
|
-
showLastColumnBorder: false,
|
|
131
|
-
showCheckboxes: false,
|
|
132
|
-
}}
|
|
133
|
-
/>
|
|
134
|
-
</ExtensionsComponents>
|
|
135
|
-
{/key}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
{/if}
|
|
139
|
-
</div>
|
|
140
|
-
{/each}
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
interface Props {
|
|
2
|
-
collectionName: string;
|
|
3
|
-
recordId: string;
|
|
4
|
-
width: number;
|
|
5
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
6
|
-
}
|
|
7
|
-
declare const ChildRecords: import("svelte").Component<Props, {}, "">;
|
|
8
|
-
type ChildRecords = ReturnType<typeof ChildRecords>;
|
|
9
|
-
export default ChildRecords;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import DataTable from "../../../components/dataTable/dataTable.svelte";
|
|
3
|
-
import { getStudioContext } from "../../../context";
|
|
4
|
-
import { Link, Plus, TableIcon } from "lucide-svelte";
|
|
5
|
-
import CreateDetailViewButton from "../create/createDetailViewButton.svelte";
|
|
6
|
-
import ExtensionsComponents from "../../../components/extensionsComponents.svelte";
|
|
7
|
-
import { getExtensionUtils } from "../../../extensions/extensionUtils";
|
|
8
|
-
|
|
9
|
-
const { ctx, lobb } = getStudioContext();
|
|
10
|
-
|
|
11
|
-
interface LocalProp {
|
|
12
|
-
collectionName: string;
|
|
13
|
-
entry: any;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let { collectionName, entry }: LocalProp = $props();
|
|
17
|
-
|
|
18
|
-
const childrenRelations = ctx.meta.relations.filter(
|
|
19
|
-
(relation) => relation.to.collection === collectionName,
|
|
20
|
-
);
|
|
21
|
-
const refresh: boolean[] = $state(
|
|
22
|
-
new Array(childrenRelations.length).fill(true),
|
|
23
|
-
);
|
|
24
|
-
</script>
|
|
25
|
-
|
|
26
|
-
{#if childrenRelations.length}
|
|
27
|
-
<div class="flex flex-col gap-4 border-t p-4">
|
|
28
|
-
<div class="flex items-center gap-2">
|
|
29
|
-
<Link size="17.5" />
|
|
30
|
-
<div>Sub Records</div>
|
|
31
|
-
</div>
|
|
32
|
-
<div class="flex flex-col gap-4">
|
|
33
|
-
{#each childrenRelations as relation, index}
|
|
34
|
-
{@const childCollection = relation.from.collection}
|
|
35
|
-
{@const childField = relation.from.field}
|
|
36
|
-
<ExtensionsComponents
|
|
37
|
-
name="detailView.update.subRecords.{childCollection}"
|
|
38
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
39
|
-
collectionName={childCollection}
|
|
40
|
-
filter={{
|
|
41
|
-
[childField]: entry.id,
|
|
42
|
-
}}
|
|
43
|
-
class="bg-muted/30 border rounded-md overflow-hidden"
|
|
44
|
-
>
|
|
45
|
-
<div class="border rounded-md overflow-clip">
|
|
46
|
-
<div
|
|
47
|
-
class="flex items-center justify-between px-2 h-10 bg-muted/30 border-b"
|
|
48
|
-
>
|
|
49
|
-
<div class="flex-1 flex h-full items-center gap-2">
|
|
50
|
-
<TableIcon
|
|
51
|
-
class="text-muted-foreground"
|
|
52
|
-
size="17.5"
|
|
53
|
-
/>
|
|
54
|
-
<div class="text-sm text-muted-foreground">
|
|
55
|
-
{childCollection}
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
<div class="flex gap-2">
|
|
59
|
-
<CreateDetailViewButton
|
|
60
|
-
variant="ghost"
|
|
61
|
-
class="h-7 px-2 font-normal text-xs"
|
|
62
|
-
Icon={Plus}
|
|
63
|
-
collectionName={childCollection}
|
|
64
|
-
onSuccessfullSave={async () => {
|
|
65
|
-
refresh[index] = !refresh[index];
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
68
|
-
Create
|
|
69
|
-
</CreateDetailViewButton>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
<div class="max-h-72 overflow-auto rounded-md">
|
|
73
|
-
{#key refresh[index]}
|
|
74
|
-
<DataTable
|
|
75
|
-
collectionName={childCollection}
|
|
76
|
-
filter={{
|
|
77
|
-
[childField]: entry.id,
|
|
78
|
-
}}
|
|
79
|
-
unifiedBgColor="bg-muted/30"
|
|
80
|
-
showHeader={false}
|
|
81
|
-
showFooter={false}
|
|
82
|
-
showDelete={true}
|
|
83
|
-
tableProps={{
|
|
84
|
-
showLastColumnBorder: false,
|
|
85
|
-
showLastRowBorder: false,
|
|
86
|
-
showCheckboxes: false,
|
|
87
|
-
}}
|
|
88
|
-
/>
|
|
89
|
-
{/key}
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</ExtensionsComponents>
|
|
93
|
-
{/each}
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
{/if}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { getStudioContext } from "../../context";
|
|
3
|
-
import { ChevronRight, Plus, Smartphone, Table } from "lucide-svelte";
|
|
4
|
-
import DataTable from "./dataTable.svelte";
|
|
5
|
-
import CreateDetailViewButton from "../detailView/create/createDetailViewButton.svelte";
|
|
6
|
-
import ExtensionsComponents from "../extensionsComponents.svelte";
|
|
7
|
-
import { getExtensionUtils } from "../../extensions/extensionUtils";
|
|
8
|
-
|
|
9
|
-
const { ctx, lobb } = getStudioContext();
|
|
10
|
-
|
|
11
|
-
interface Props {
|
|
12
|
-
collectionName: string;
|
|
13
|
-
recordId: string;
|
|
14
|
-
width: number;
|
|
15
|
-
unifiedBgColor?: "bg-muted/30" | "bg-background";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let { collectionName, recordId, width, unifiedBgColor }: Props = $props();
|
|
19
|
-
|
|
20
|
-
const relations = ctx.meta.relations.filter(
|
|
21
|
-
(relation) => relation.to.collection === collectionName,
|
|
22
|
-
);
|
|
23
|
-
let expandedRows: boolean[] = $state(
|
|
24
|
-
new Array(relations.length).fill(false),
|
|
25
|
-
);
|
|
26
|
-
let refreshDataTable = $state(true);
|
|
27
|
-
let tableHeaderWidth = $state(0);
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<div class="flex" style="width: {width}px;">
|
|
31
|
-
<div
|
|
32
|
-
class="
|
|
33
|
-
flex justify-center border-r
|
|
34
|
-
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
35
|
-
"
|
|
36
|
-
style="width: 40px"
|
|
37
|
-
></div>
|
|
38
|
-
<div class="flex-1 flex flex-col">
|
|
39
|
-
{#each relations as relation, index}
|
|
40
|
-
{@const lastRow = relations.length - 1 === index}
|
|
41
|
-
{@const fromCollection = relation.from.collection}
|
|
42
|
-
{@const fromField = relation.from.field}
|
|
43
|
-
<div
|
|
44
|
-
class="
|
|
45
|
-
overflow-hidden
|
|
46
|
-
{unifiedBgColor ? unifiedBgColor : 'bg-background'}
|
|
47
|
-
"
|
|
48
|
-
>
|
|
49
|
-
<div
|
|
50
|
-
bind:clientWidth={tableHeaderWidth}
|
|
51
|
-
class="
|
|
52
|
-
flex justify-between items-center gap-2 text-sm h-10
|
|
53
|
-
{expandedRows[index] || !lastRow ? 'border-b' : ''}
|
|
54
|
-
"
|
|
55
|
-
>
|
|
56
|
-
<button
|
|
57
|
-
class="flex gap-2 px-2 flex-1 h-full items-center"
|
|
58
|
-
onclick={() => {
|
|
59
|
-
expandedRows[index] = !expandedRows[index];
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
<ChevronRight
|
|
63
|
-
size="17.5"
|
|
64
|
-
class="text-muted-foreground transition-transform"
|
|
65
|
-
style={expandedRows[index]
|
|
66
|
-
? "transform: rotate(90deg);"
|
|
67
|
-
: "transform: rotate(0deg);"}
|
|
68
|
-
/>
|
|
69
|
-
<Table size="17.5" class="text-muted-foreground" />
|
|
70
|
-
<div class="text-muted-foreground">
|
|
71
|
-
{relation.from.collection}
|
|
72
|
-
</div>
|
|
73
|
-
</button>
|
|
74
|
-
<div class="flex items-center px-2">
|
|
75
|
-
<CreateDetailViewButton
|
|
76
|
-
collectionName={relation.from.collection}
|
|
77
|
-
variant="ghost"
|
|
78
|
-
class="h-7 px-3 text-xs font-normal"
|
|
79
|
-
Icon={Plus}
|
|
80
|
-
values={{
|
|
81
|
-
[fromField]: {
|
|
82
|
-
id: recordId,
|
|
83
|
-
},
|
|
84
|
-
}}
|
|
85
|
-
onSuccessfullSave={async () => {
|
|
86
|
-
refreshDataTable = !refreshDataTable;
|
|
87
|
-
}}
|
|
88
|
-
>
|
|
89
|
-
Create
|
|
90
|
-
</CreateDetailViewButton>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
{#if expandedRows[index]}
|
|
94
|
-
<div
|
|
95
|
-
class="
|
|
96
|
-
flex max-h-96 overflow-auto
|
|
97
|
-
{lastRow ? '' : 'border-b'}
|
|
98
|
-
"
|
|
99
|
-
>
|
|
100
|
-
<div
|
|
101
|
-
class="border-r {unifiedBgColor
|
|
102
|
-
? unifiedBgColor
|
|
103
|
-
: ''}"
|
|
104
|
-
style="width: 100vw; max-width: 40px"
|
|
105
|
-
></div>
|
|
106
|
-
<div
|
|
107
|
-
class="flex-1"
|
|
108
|
-
style="width: {tableHeaderWidth - 40}px;"
|
|
109
|
-
>
|
|
110
|
-
{#key refreshDataTable}
|
|
111
|
-
<ExtensionsComponents
|
|
112
|
-
name="listView.entry.children.{fromCollection}"
|
|
113
|
-
collectionName={fromCollection}
|
|
114
|
-
filter={{
|
|
115
|
-
[fromField]: recordId,
|
|
116
|
-
}}
|
|
117
|
-
utils={getExtensionUtils(lobb, ctx)}
|
|
118
|
-
>
|
|
119
|
-
<DataTable
|
|
120
|
-
collectionName={fromCollection}
|
|
121
|
-
filter={{
|
|
122
|
-
[fromField]: recordId,
|
|
123
|
-
}}
|
|
124
|
-
showHeader={false}
|
|
125
|
-
showFooter={false}
|
|
126
|
-
showDelete={true}
|
|
127
|
-
{unifiedBgColor}
|
|
128
|
-
tableProps={{
|
|
129
|
-
showLastRowBorder: false,
|
|
130
|
-
showLastColumnBorder: false,
|
|
131
|
-
showCheckboxes: false,
|
|
132
|
-
}}
|
|
133
|
-
/>
|
|
134
|
-
</ExtensionsComponents>
|
|
135
|
-
{/key}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
{/if}
|
|
139
|
-
</div>
|
|
140
|
-
{/each}
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|