@lobb-js/studio 0.19.1 → 0.21.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/actions.d.ts +1 -0
- package/dist/components/dataTable/fieldCell.svelte +38 -37
- package/dist/components/dataTable/polymorphicFieldCell.svelte +43 -0
- package/dist/components/dataTable/polymorphicFieldCell.svelte.d.ts +9 -0
- package/dist/components/dataTable/utils.js +27 -17
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte +14 -10
- package/dist/components/dataTableDrawer/dataTableDrawer.svelte.d.ts +1 -0
- package/dist/components/detailView/create/createDetailView.svelte +38 -69
- package/dist/components/detailView/fieldInput.svelte +27 -11
- package/dist/components/detailView/fieldInput.svelte.d.ts +1 -1
- package/dist/components/detailView/update/updateDetailView.svelte +26 -30
- package/dist/components/detailView/utils.d.ts +5 -2
- package/dist/components/detailView/utils.js +53 -71
- package/dist/components/drawer.svelte +24 -8
- package/dist/components/drawer.svelte.d.ts +1 -0
- package/dist/components/polymorphicInput.svelte +141 -0
- package/dist/components/polymorphicInput.svelte.d.ts +10 -0
- package/dist/extensions/extension.types.d.ts +2 -0
- package/dist/extensions/extensionUtils.js +2 -0
- package/dist/relations.d.ts +14 -0
- package/dist/relations.js +47 -0
- package/dist/store.types.d.ts +1 -0
- package/dist/utils.d.ts +0 -3
- package/dist/utils.js +0 -21
- package/package.json +2 -2
- package/src/lib/actions.ts +1 -0
- package/src/lib/components/dataTable/fieldCell.svelte +38 -37
- package/src/lib/components/dataTable/polymorphicFieldCell.svelte +43 -0
- package/src/lib/components/dataTable/utils.ts +21 -18
- package/src/lib/components/dataTableDrawer/dataTableDrawer.svelte +14 -10
- package/src/lib/components/detailView/create/createDetailView.svelte +38 -69
- package/src/lib/components/detailView/fieldInput.svelte +27 -11
- package/src/lib/components/detailView/update/updateDetailView.svelte +26 -30
- package/src/lib/components/detailView/utils.ts +44 -75
- package/src/lib/components/drawer.svelte +24 -8
- package/src/lib/components/polymorphicInput.svelte +141 -0
- package/src/lib/extensions/extension.types.ts +2 -1
- package/src/lib/extensions/extensionUtils.ts +2 -0
- package/src/lib/relations.ts +52 -0
- package/src/lib/store.types.ts +1 -0
- package/src/lib/utils.ts +0 -21
|
@@ -10,7 +10,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
12
|
import Mustache from "mustache";
|
|
13
|
-
import {
|
|
13
|
+
import { isRelationField } from "../../relations";
|
|
14
14
|
import { getField } from "../dataTable/utils";
|
|
15
15
|
export function getDefaultEntry(ctx, fieldNames, collectionName, values) {
|
|
16
16
|
return Object.fromEntries(fieldNames.map(function (fieldName) {
|
|
@@ -33,92 +33,62 @@ export function getDefaultEntry(ctx, fieldNames, collectionName, values) {
|
|
|
33
33
|
return [fieldName, value];
|
|
34
34
|
}));
|
|
35
35
|
}
|
|
36
|
-
export function serializeEntry(ctx, collectionName, entry
|
|
37
|
-
if (rollback === void 0) { rollback = false; }
|
|
38
|
-
// deep clone the object
|
|
36
|
+
export function serializeEntry(ctx, collectionName, entry) {
|
|
39
37
|
entry = __assign({}, entry);
|
|
40
|
-
//
|
|
38
|
+
// extract FK object fields → ID
|
|
41
39
|
for (var _i = 0, _a = Object.entries(entry); _i < _a.length; _i++) {
|
|
42
40
|
var _b = _a[_i], fieldName = _b[0], fieldValue = _b[1];
|
|
43
|
-
var isRefrenceField =
|
|
41
|
+
var isRefrenceField = isRelationField(ctx, collectionName, fieldName);
|
|
44
42
|
if (isRefrenceField && fieldValue !== null && fieldValue.id !== undefined) {
|
|
45
43
|
entry[fieldName] = fieldValue.id;
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
46
|
+
// extract polymorphic id_field objects → ID
|
|
47
|
+
for (var _c = 0, _d = ctx.meta.relations; _c < _d.length; _c++) {
|
|
48
|
+
var relation = _d[_c];
|
|
49
|
+
if (relation.type !== "polymorphic")
|
|
50
|
+
continue;
|
|
51
|
+
if (relation.from.collection !== collectionName)
|
|
52
|
+
continue;
|
|
53
|
+
var idField = relation.from.id_field;
|
|
54
|
+
var fieldValue = entry[idField];
|
|
55
|
+
if (fieldValue !== null && typeof fieldValue === "object" && fieldValue.id !== undefined) {
|
|
56
|
+
entry[idField] = fieldValue.id;
|
|
60
57
|
}
|
|
61
58
|
}
|
|
62
59
|
return entry;
|
|
63
60
|
}
|
|
64
|
-
export function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
var
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
var localPayload = (_a = {},
|
|
85
|
-
_a[foreignKeyFieldName] = payload[foreignKeyFieldName],
|
|
86
|
-
_a);
|
|
87
|
-
transactionBody.push({
|
|
88
|
-
method: "updateMany",
|
|
89
|
-
props: {
|
|
90
|
-
collectionName: collectionName,
|
|
91
|
-
data: localPayload,
|
|
92
|
-
filter: { id: payload.id },
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
transactionBody.push({
|
|
98
|
-
method: "createOne",
|
|
99
|
-
props: { collectionName: collectionName, data: payload },
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
var childrenRelations = ctx.meta.relations.filter(function (relation) { return relation.to.collection === collectionName; });
|
|
103
|
-
var childrenCollectionNames = childrenRelations.map(function (relation) { return relation.from.collection; });
|
|
104
|
-
for (var index = 0; index < childrenCollectionNames.length; index++) {
|
|
105
|
-
var childrenCollectionName = childrenCollectionNames[index];
|
|
106
|
-
var childrenEntries = entry[childrenCollectionName];
|
|
107
|
-
if (childrenEntries) {
|
|
108
|
-
for (var index_2 = 0; index_2 < childrenEntries.length; index_2++) {
|
|
109
|
-
var childrenEntry = childrenEntries[index_2];
|
|
110
|
-
handleEntryRecursive(transactionBody, childrenCollectionName, childrenEntry, localTransactionIndex);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
61
|
+
export function buildChildren(ctx, collectionName, entry) {
|
|
62
|
+
var childrenRelations = ctx.meta.relations.filter(function (relation) { return relation.type !== "polymorphic" && relation.to.collection === collectionName; });
|
|
63
|
+
var children = {};
|
|
64
|
+
var _loop_1 = function (relation) {
|
|
65
|
+
var childCollection = relation.from.collection;
|
|
66
|
+
var childEntries = entry[childCollection];
|
|
67
|
+
if (!(childEntries === null || childEntries === void 0 ? void 0 : childEntries.length))
|
|
68
|
+
return "continue";
|
|
69
|
+
var toCreate = childEntries
|
|
70
|
+
.filter(function (e) { return !e.id; })
|
|
71
|
+
.map(function (e) { return serializeEntry(ctx, childCollection, e); });
|
|
72
|
+
var toLink = childEntries
|
|
73
|
+
.filter(function (e) { return e.id; })
|
|
74
|
+
.map(function (e) { return e.id; });
|
|
75
|
+
if (toCreate.length || toLink.length) {
|
|
76
|
+
children[childCollection] = {};
|
|
77
|
+
if (toCreate.length)
|
|
78
|
+
children[childCollection].create = toCreate;
|
|
79
|
+
if (toLink.length)
|
|
80
|
+
children[childCollection].link = toLink;
|
|
113
81
|
}
|
|
82
|
+
};
|
|
83
|
+
for (var _i = 0, childrenRelations_1 = childrenRelations; _i < childrenRelations_1.length; _i++) {
|
|
84
|
+
var relation = childrenRelations_1[_i];
|
|
85
|
+
_loop_1(relation);
|
|
114
86
|
}
|
|
115
|
-
|
|
116
|
-
handleEntryRecursive(transactionBody, collectionName, entry);
|
|
117
|
-
return transactionBody;
|
|
87
|
+
return Object.keys(children).length ? children : undefined;
|
|
118
88
|
}
|
|
119
89
|
export function parseDetailViewValues(ctx, collectionName, values) {
|
|
120
90
|
var forignFieldNames = ctx.meta.relations
|
|
121
|
-
.filter(function (relation) { return relation.from.collection === collectionName; })
|
|
91
|
+
.filter(function (relation) { return relation.type !== "polymorphic" && relation.from.collection === collectionName; })
|
|
122
92
|
.map(function (relation) { return relation.from.field; });
|
|
123
93
|
var childCollectionNames = ctx.meta.relations
|
|
124
94
|
.filter(function (relation) { return relation.to.collection === collectionName; })
|
|
@@ -138,6 +108,18 @@ export function parseDetailViewValues(ctx, collectionName, values) {
|
|
|
138
108
|
}
|
|
139
109
|
}
|
|
140
110
|
}
|
|
111
|
+
// wrap polymorphic id_field scalar values into { id: value }
|
|
112
|
+
for (var _c = 0, _d = ctx.meta.relations; _c < _d.length; _c++) {
|
|
113
|
+
var relation = _d[_c];
|
|
114
|
+
if (relation.type !== "polymorphic")
|
|
115
|
+
continue;
|
|
116
|
+
if (relation.from.collection !== collectionName)
|
|
117
|
+
continue;
|
|
118
|
+
var idField = relation.from.id_field;
|
|
119
|
+
if (idField in values && typeof values[idField] === 'number') {
|
|
120
|
+
values[idField] = { id: values[idField] };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
141
123
|
}
|
|
142
124
|
export function getCollectionFields(ctx, collectionName) {
|
|
143
125
|
var returnedData = [];
|
|
@@ -1,30 +1,46 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { calculateDrawerWidth } from "../utils";
|
|
3
3
|
import type { Snippet } from "svelte";
|
|
4
|
-
import { fade
|
|
4
|
+
import { fade } from "svelte/transition";
|
|
5
|
+
import { cubicOut } from "svelte/easing";
|
|
5
6
|
import Portal from "svelte-portal";
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
children?: Snippet<[]>;
|
|
9
10
|
onHide?: () => Promise<void>;
|
|
11
|
+
position?: "side" | "bottom";
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
let { onHide, children }: Props = $props();
|
|
14
|
+
let { onHide, children, position = "side" }: Props = $props();
|
|
15
|
+
|
|
16
|
+
function slide(_node: Element, { duration = 250, axis }: { duration?: number; axis: "x" | "y" }) {
|
|
17
|
+
return {
|
|
18
|
+
duration,
|
|
19
|
+
easing: cubicOut,
|
|
20
|
+
css: (t: number) => {
|
|
21
|
+
const offset = (1 - t) * 100;
|
|
22
|
+
return axis === "y"
|
|
23
|
+
? `transform: translateY(${offset}%)`
|
|
24
|
+
: `transform: translateX(${offset}%)`;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
13
28
|
</script>
|
|
14
29
|
|
|
15
30
|
<Portal target="body">
|
|
16
31
|
<button
|
|
17
|
-
transition:fade={{ duration:
|
|
32
|
+
transition:fade={{ duration: 200 }}
|
|
18
33
|
onclick={() => onHide?.()}
|
|
19
|
-
class="backgroundDrawerButton fixed left-0 top-0 z-40 h-screen w-screen bg-
|
|
34
|
+
class="backgroundDrawerButton fixed left-0 top-0 z-40 h-screen w-screen bg-black/50 cursor-default"
|
|
20
35
|
aria-label="background used to hide the background"
|
|
21
36
|
></button>
|
|
22
37
|
|
|
23
|
-
<!-- the drawer -->
|
|
24
38
|
<div
|
|
25
|
-
transition:
|
|
26
|
-
class=
|
|
27
|
-
|
|
39
|
+
transition:slide={{ axis: position === "bottom" ? "y" : "x" }}
|
|
40
|
+
class={position === "bottom"
|
|
41
|
+
? "fixed bottom-0 left-0 z-40 flex h-[60vh] w-full flex-col border-t bg-background"
|
|
42
|
+
: "fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-background"}
|
|
43
|
+
style={position === "side" ? `max-width: ${calculateDrawerWidth()}px;` : ""}
|
|
28
44
|
>
|
|
29
45
|
{@render children?.()}
|
|
30
46
|
</div>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from "./ui/button/button.svelte";
|
|
3
|
+
import DataTable from "./dataTable/dataTable.svelte";
|
|
4
|
+
import Drawer from "./drawer.svelte";
|
|
5
|
+
import * as Popover from "./ui/popover/index";
|
|
6
|
+
import { getCollectionPrimaryField } from "./dataTable/utils";
|
|
7
|
+
import { getStudioContext } from "../context";
|
|
8
|
+
import { ArrowLeft, Link, ChevronDown } from "lucide-svelte";
|
|
9
|
+
|
|
10
|
+
const { ctx } = getStudioContext();
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
collectionField: string;
|
|
14
|
+
idField: string;
|
|
15
|
+
targetCollections: string[];
|
|
16
|
+
entry: Record<string, any>;
|
|
17
|
+
destructive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
collectionField,
|
|
22
|
+
idField,
|
|
23
|
+
targetCollections,
|
|
24
|
+
entry = $bindable(),
|
|
25
|
+
destructive,
|
|
26
|
+
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
const selectedCollection = $derived(entry[collectionField] ?? null);
|
|
29
|
+
const selectedId = $derived(entry[idField]?.id ?? entry[idField] ?? null);
|
|
30
|
+
const primaryField = $derived(entry[idField] ? Object.values(entry[idField])[1] : null);
|
|
31
|
+
|
|
32
|
+
let collectionPopoverOpen = $state(false);
|
|
33
|
+
let recordDrawerOpen = $state(false);
|
|
34
|
+
|
|
35
|
+
function onCollectionChange(col: string) {
|
|
36
|
+
collectionPopoverOpen = false;
|
|
37
|
+
if (entry[collectionField] !== col) {
|
|
38
|
+
entry = { ...entry, [collectionField]: col, [idField]: null };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function onIdChange(e: Event) {
|
|
43
|
+
const raw = (e.target as HTMLInputElement).value;
|
|
44
|
+
const id = raw === "" ? null : Number(raw);
|
|
45
|
+
entry = { ...entry, [idField]: id === null ? null : { id } };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function onRecordSelect(record: any) {
|
|
49
|
+
const primaryFieldName = getCollectionPrimaryField(ctx, selectedCollection!);
|
|
50
|
+
const value: any = { id: record.id };
|
|
51
|
+
if (primaryFieldName) value[primaryFieldName] = record[primaryFieldName];
|
|
52
|
+
entry = { ...entry, [idField]: value };
|
|
53
|
+
recordDrawerOpen = false;
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="flex h-9 w-full items-center gap-1.5 rounded-md border pl-1.5 pr-9 text-xs bg-muted/30 {destructive ? 'border-destructive bg-destructive/10' : ''}">
|
|
58
|
+
<!-- Collection picker -->
|
|
59
|
+
<Popover.Root bind:open={collectionPopoverOpen}>
|
|
60
|
+
<Popover.Trigger>
|
|
61
|
+
{#snippet child({ props })}
|
|
62
|
+
<button
|
|
63
|
+
{...props}
|
|
64
|
+
class="flex shrink-0 items-center gap-1 h-6 px-2 rounded-sm border bg-muted text-xs text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
65
|
+
>
|
|
66
|
+
{selectedCollection ?? "NULL"}
|
|
67
|
+
<ChevronDown size="11" />
|
|
68
|
+
</button>
|
|
69
|
+
{/snippet}
|
|
70
|
+
</Popover.Trigger>
|
|
71
|
+
<Popover.Content class="w-48 p-2">
|
|
72
|
+
<div class="flex flex-col gap-1">
|
|
73
|
+
{#each targetCollections as col}
|
|
74
|
+
<Button
|
|
75
|
+
variant={selectedCollection === col ? "default" : "ghost"}
|
|
76
|
+
class="justify-start text-xs font-normal h-7 px-2"
|
|
77
|
+
onclick={() => onCollectionChange(col)}
|
|
78
|
+
>
|
|
79
|
+
{col}
|
|
80
|
+
</Button>
|
|
81
|
+
{/each}
|
|
82
|
+
</div>
|
|
83
|
+
</Popover.Content>
|
|
84
|
+
</Popover.Root>
|
|
85
|
+
|
|
86
|
+
<!-- Transparent id input -->
|
|
87
|
+
<input
|
|
88
|
+
placeholder="NULL"
|
|
89
|
+
type="number"
|
|
90
|
+
class="min-w-0 flex-1 bg-transparent outline-none text-xs placeholder:text-muted-foreground"
|
|
91
|
+
value={selectedId ?? ""}
|
|
92
|
+
oninput={onIdChange}
|
|
93
|
+
/>
|
|
94
|
+
|
|
95
|
+
<!-- Primary field badge -->
|
|
96
|
+
{#if primaryField}
|
|
97
|
+
<div class="flex shrink-0 items-center bg-background rounded-full border h-6 px-3 shadow-sm">
|
|
98
|
+
{primaryField}
|
|
99
|
+
</div>
|
|
100
|
+
{/if}
|
|
101
|
+
|
|
102
|
+
<!-- Select record button -->
|
|
103
|
+
{#if selectedCollection}
|
|
104
|
+
<Button
|
|
105
|
+
class="h-6 shrink-0 px-2 font-normal text-xs"
|
|
106
|
+
variant="outline"
|
|
107
|
+
onclick={() => (recordDrawerOpen = true)}
|
|
108
|
+
>
|
|
109
|
+
<Link size="13" />
|
|
110
|
+
Select Record
|
|
111
|
+
</Button>
|
|
112
|
+
{/if}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
{#if recordDrawerOpen}
|
|
116
|
+
<Drawer onHide={async () => { recordDrawerOpen = false }}>
|
|
117
|
+
<div class="flex h-12 items-center gap-4 border-b px-4">
|
|
118
|
+
<Button
|
|
119
|
+
variant="outline"
|
|
120
|
+
onclick={() => (recordDrawerOpen = false)}
|
|
121
|
+
class="h-8 w-8 rounded-full text-xs font-normal"
|
|
122
|
+
Icon={ArrowLeft}
|
|
123
|
+
/>
|
|
124
|
+
<div class="flex items-center gap-2">
|
|
125
|
+
<div class="text-sm">Select record from</div>
|
|
126
|
+
<span class="rounded-md border bg-muted px-2 py-0.5 text-sm">
|
|
127
|
+
{selectedCollection}
|
|
128
|
+
</span>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="flex-1 overflow-y-auto bg-muted">
|
|
132
|
+
<DataTable
|
|
133
|
+
collectionName={selectedCollection!}
|
|
134
|
+
tableProps={{
|
|
135
|
+
showCheckboxes: false,
|
|
136
|
+
select: { onSelect: onRecordSelect },
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
</Drawer>
|
|
141
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
collectionField: string;
|
|
3
|
+
idField: string;
|
|
4
|
+
targetCollections: string[];
|
|
5
|
+
entry: Record<string, any>;
|
|
6
|
+
destructive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const PolymorphicInput: import("svelte").Component<Props, {}, "entry">;
|
|
9
|
+
type PolymorphicInput = ReturnType<typeof PolymorphicInput>;
|
|
10
|
+
export default PolymorphicInput;
|
|
@@ -59,7 +59,9 @@ export interface ExtensionUtils {
|
|
|
59
59
|
title?: string;
|
|
60
60
|
showHeader?: boolean;
|
|
61
61
|
showFooter?: boolean;
|
|
62
|
+
position?: "side" | "bottom";
|
|
62
63
|
}) => void;
|
|
64
|
+
emitEvent: (eventName: string, input: Record<string, any>) => Promise<Record<string, any>>;
|
|
63
65
|
components: Components;
|
|
64
66
|
mediaQueries: typeof mediaQueries;
|
|
65
67
|
intlDate: typeof intlDate;
|
|
@@ -45,6 +45,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
45
45
|
};
|
|
46
46
|
import { toast } from "svelte-sonner";
|
|
47
47
|
import { showDialog, openDataTableDrawer } from "../actions";
|
|
48
|
+
import { emitEvent } from "../eventSystem";
|
|
48
49
|
import { Button } from "../components/ui/button";
|
|
49
50
|
import { Input } from "../components/ui/input";
|
|
50
51
|
import { Separator } from "../components/ui/separator";
|
|
@@ -100,6 +101,7 @@ export function getExtensionUtils(lobb, ctx) {
|
|
|
100
101
|
toast: toast,
|
|
101
102
|
showDialog: showDialog,
|
|
102
103
|
openDataTableDrawer: function (props) { return openDataTableDrawer({ lobb: lobb, ctx: ctx }, props); },
|
|
104
|
+
emitEvent: function (eventName, input) { return emitEvent({ lobb: lobb, ctx: ctx }, eventName, input); },
|
|
103
105
|
components: getComponents(),
|
|
104
106
|
mediaQueries: mediaQueries,
|
|
105
107
|
intlDate: intlDate,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CTX } from "./store.types";
|
|
2
|
+
export declare function isRelationField(ctx: CTX, collectionName: string, fieldName: string): boolean;
|
|
3
|
+
export declare function getFieldRelationTarget(ctx: CTX, collectionName: string, fieldName: string): string | null;
|
|
4
|
+
export declare function getPolymorphicRelation(ctx: CTX, collectionName: string, fieldName: string): {
|
|
5
|
+
type: "polymorphic";
|
|
6
|
+
from: {
|
|
7
|
+
collection: string;
|
|
8
|
+
virtual_field: string;
|
|
9
|
+
collection_field: string;
|
|
10
|
+
id_field: string;
|
|
11
|
+
};
|
|
12
|
+
to: string[];
|
|
13
|
+
};
|
|
14
|
+
export declare function recordHasChildrean(ctx: CTX, collectionName: string): boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function getFieldRelation(ctx, collectionName, fieldName) {
|
|
2
|
+
var relations = ctx.meta.relations;
|
|
3
|
+
for (var index = 0; index < relations.length; index++) {
|
|
4
|
+
var relation = relations[index];
|
|
5
|
+
if (relation.type === "polymorphic")
|
|
6
|
+
continue;
|
|
7
|
+
if (relation.from.collection === collectionName && relation.from.field === fieldName) {
|
|
8
|
+
return relation;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
;
|
|
14
|
+
export function isRelationField(ctx, collectionName, fieldName) {
|
|
15
|
+
return Boolean(getFieldRelation(ctx, collectionName, fieldName));
|
|
16
|
+
}
|
|
17
|
+
;
|
|
18
|
+
export function getFieldRelationTarget(ctx, collectionName, fieldName) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
return (_b = (_a = getFieldRelation(ctx, collectionName, fieldName)) === null || _a === void 0 ? void 0 : _a.to.collection) !== null && _b !== void 0 ? _b : null;
|
|
21
|
+
}
|
|
22
|
+
;
|
|
23
|
+
export function getPolymorphicRelation(ctx, collectionName, fieldName) {
|
|
24
|
+
var relations = ctx.meta.relations;
|
|
25
|
+
for (var index = 0; index < relations.length; index++) {
|
|
26
|
+
var relation = relations[index];
|
|
27
|
+
if (relation.type === "polymorphic" &&
|
|
28
|
+
relation.from.collection === collectionName &&
|
|
29
|
+
relation.from.virtual_field === fieldName) {
|
|
30
|
+
return relation;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
;
|
|
36
|
+
export function recordHasChildrean(ctx, collectionName) {
|
|
37
|
+
for (var index = 0; index < ctx.meta.relations.length; index++) {
|
|
38
|
+
var relation = ctx.meta.relations[index];
|
|
39
|
+
if (relation.type === "polymorphic")
|
|
40
|
+
continue;
|
|
41
|
+
if (relation.to.collection === collectionName) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
;
|
package/dist/store.types.d.ts
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type ClassValue } from "clsx";
|
|
2
2
|
import { MediaQuery } from 'svelte/reactivity';
|
|
3
|
-
import type { CTX } from "./store.types";
|
|
4
3
|
export declare function cn(...inputs: ClassValue[]): string;
|
|
5
4
|
export type WithoutChild<T> = T extends {
|
|
6
5
|
child?: any;
|
|
@@ -19,8 +18,6 @@ export declare const mediaQueries: {
|
|
|
19
18
|
xl: MediaQuery;
|
|
20
19
|
'2xl': MediaQuery;
|
|
21
20
|
};
|
|
22
|
-
export declare function getFieldRelation(ctx: CTX, collectionName: string, fieldName: string): any;
|
|
23
|
-
export declare function recordHasChildrean(ctx: CTX, collectionName: string): boolean;
|
|
24
21
|
export declare function calculateDrawerWidth(): number;
|
|
25
22
|
export declare function getChangedProperties(oldObj: Record<string, any>, newObj: Record<string, any>): Record<string, any>;
|
|
26
23
|
export declare function parseFunction(functionString: string): any;
|
package/dist/utils.js
CHANGED
|
@@ -15,27 +15,6 @@ export var mediaQueries = {
|
|
|
15
15
|
xl: new MediaQuery('min-width: 1280px'),
|
|
16
16
|
'2xl': new MediaQuery('min-width: 1536px'),
|
|
17
17
|
};
|
|
18
|
-
export function getFieldRelation(ctx, collectionName, fieldName) {
|
|
19
|
-
var relations = ctx.meta.relations;
|
|
20
|
-
for (var index = 0; index < relations.length; index++) {
|
|
21
|
-
var relation = relations[index];
|
|
22
|
-
if (relation.from.collection === collectionName && relation.from.field === fieldName) {
|
|
23
|
-
return relation;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
;
|
|
29
|
-
export function recordHasChildrean(ctx, collectionName) {
|
|
30
|
-
for (var index = 0; index < ctx.meta.relations.length; index++) {
|
|
31
|
-
var relation = ctx.meta.relations[index];
|
|
32
|
-
if (relation.to.collection === collectionName) {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
;
|
|
39
18
|
export function calculateDrawerWidth() {
|
|
40
19
|
var backgroundDrawerButtons = document.querySelectorAll(".backgroundDrawerButton");
|
|
41
20
|
var drawersCount = Array.from(backgroundDrawerButtons).length;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobb-js/studio",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.21.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"postpublish": "./scripts/postpublish.sh"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@lobb-js/core": "^0.
|
|
45
|
+
"@lobb-js/core": "^0.25.0",
|
|
46
46
|
"@chromatic-com/storybook": "^4.1.2",
|
|
47
47
|
"@storybook/addon-a11y": "^10.0.1",
|
|
48
48
|
"@storybook/addon-docs": "^10.0.1",
|
package/src/lib/actions.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { getFieldRelationTarget, getPolymorphicRelation, isRelationField } from "../../relations";
|
|
3
3
|
import { ExternalLink } from "lucide-svelte";
|
|
4
4
|
import UpdateDetailViewButton from "../detailView/update/updateDetailViewButton.svelte";
|
|
5
5
|
import { getField } from "./utils";
|
|
6
6
|
import EnumBadge from "./enumBadge.svelte";
|
|
7
|
-
import
|
|
7
|
+
import PolymorphicFieldCell from "./polymorphicFieldCell.svelte";
|
|
8
8
|
import { getStudioContext } from "../../context";
|
|
9
9
|
|
|
10
10
|
const { ctx } = getStudioContext();
|
|
@@ -26,49 +26,50 @@
|
|
|
26
26
|
}: Props = $props();
|
|
27
27
|
|
|
28
28
|
const field = getField(ctx, fieldName, collectionName);
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
);
|
|
29
|
+
const relationTarget = getFieldRelationTarget(ctx, collectionName, fieldName);
|
|
30
|
+
const polymorphicRelation = getPolymorphicRelation(ctx, collectionName, fieldName);
|
|
31
|
+
const isRefrenceField = isRelationField(ctx, collectionName, fieldName);
|
|
33
32
|
const isPasswordField = ctx.meta.collections[collectionName].fields[fieldName]?.ui?.input?.type === "password";
|
|
34
33
|
</script>
|
|
35
34
|
|
|
36
|
-
{#if
|
|
35
|
+
{#if polymorphicRelation}
|
|
36
|
+
<PolymorphicFieldCell
|
|
37
|
+
collectionField={polymorphicRelation.from.collection_field}
|
|
38
|
+
idField={polymorphicRelation.from.id_field}
|
|
39
|
+
{entry}
|
|
40
|
+
bind:tableParams
|
|
41
|
+
/>
|
|
42
|
+
{:else if isRefrenceField}
|
|
43
|
+
{#if value?.id && value.id !== 0}
|
|
44
|
+
<div class="flex items-center gap-2">
|
|
45
|
+
<div>{value.id}</div>
|
|
46
|
+
{#if Object.values(value)[1]}
|
|
47
|
+
<div class="border bg-muted px-3 py-1 rounded-full">
|
|
48
|
+
{Object.values(value)[1]}
|
|
49
|
+
</div>
|
|
50
|
+
{/if}
|
|
51
|
+
<UpdateDetailViewButton
|
|
52
|
+
collectionName={relationTarget!}
|
|
53
|
+
recordId={value.id}
|
|
54
|
+
variant="ghost"
|
|
55
|
+
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
56
|
+
Icon={ExternalLink}
|
|
57
|
+
onSuccessfullSave={async () => {
|
|
58
|
+
tableParams = { ...tableParams };
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
{:else if value?.id === 0}
|
|
63
|
+
<div class="text-muted-foreground">PARENT ID</div>
|
|
64
|
+
{:else}
|
|
65
|
+
<div class="text-muted-foreground">NULL</div>
|
|
66
|
+
{/if}
|
|
67
|
+
{:else if isPasswordField}
|
|
37
68
|
<div class="text-muted-foreground tracking-widest">••••••</div>
|
|
38
69
|
{:else if value === ""}
|
|
39
70
|
<div class="text-muted-foreground">EMPTY STRING</div>
|
|
40
71
|
{:else if value === null || value === undefined}
|
|
41
72
|
<div class="text-muted-foreground">NULL</div>
|
|
42
|
-
{:else if isRefrenceField}
|
|
43
|
-
{#if typeof value !== "object"}
|
|
44
|
-
<div>{value}</div>
|
|
45
|
-
{:else if value.id !== 0}
|
|
46
|
-
{@const primaryField = Object.values(value)[1]}
|
|
47
|
-
{#if value.id}
|
|
48
|
-
<div class="flex items-center gap-2">
|
|
49
|
-
<div>{value.id}</div>
|
|
50
|
-
{#if primaryField}
|
|
51
|
-
<div class="border bg-muted px-3 py-1 rounded-full">
|
|
52
|
-
{primaryField}
|
|
53
|
-
</div>
|
|
54
|
-
{/if}
|
|
55
|
-
<UpdateDetailViewButton
|
|
56
|
-
collectionName={relation.to.collection}
|
|
57
|
-
recordId={value.id}
|
|
58
|
-
variant="ghost"
|
|
59
|
-
class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
|
|
60
|
-
Icon={ExternalLink}
|
|
61
|
-
onSuccessfullSave={async () => {
|
|
62
|
-
tableParams = { ...tableParams };
|
|
63
|
-
}}
|
|
64
|
-
></UpdateDetailViewButton>
|
|
65
|
-
</div>
|
|
66
|
-
{:else}
|
|
67
|
-
<div class="text-muted-foreground">NULL</div>
|
|
68
|
-
{/if}
|
|
69
|
-
{:else}
|
|
70
|
-
<div class="text-muted-foreground">PARENT ID</div>
|
|
71
|
-
{/if}
|
|
72
73
|
{:else if field?.enum}
|
|
73
74
|
<EnumBadge {value} enum={field.enum} />
|
|
74
75
|
{:else if field.type === "datetime"}
|