@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.
Files changed (41) hide show
  1. package/dist/actions.d.ts +1 -0
  2. package/dist/components/dataTable/fieldCell.svelte +38 -37
  3. package/dist/components/dataTable/polymorphicFieldCell.svelte +43 -0
  4. package/dist/components/dataTable/polymorphicFieldCell.svelte.d.ts +9 -0
  5. package/dist/components/dataTable/utils.js +27 -17
  6. package/dist/components/dataTableDrawer/dataTableDrawer.svelte +14 -10
  7. package/dist/components/dataTableDrawer/dataTableDrawer.svelte.d.ts +1 -0
  8. package/dist/components/detailView/create/createDetailView.svelte +38 -69
  9. package/dist/components/detailView/fieldInput.svelte +27 -11
  10. package/dist/components/detailView/fieldInput.svelte.d.ts +1 -1
  11. package/dist/components/detailView/update/updateDetailView.svelte +26 -30
  12. package/dist/components/detailView/utils.d.ts +5 -2
  13. package/dist/components/detailView/utils.js +53 -71
  14. package/dist/components/drawer.svelte +24 -8
  15. package/dist/components/drawer.svelte.d.ts +1 -0
  16. package/dist/components/polymorphicInput.svelte +141 -0
  17. package/dist/components/polymorphicInput.svelte.d.ts +10 -0
  18. package/dist/extensions/extension.types.d.ts +2 -0
  19. package/dist/extensions/extensionUtils.js +2 -0
  20. package/dist/relations.d.ts +14 -0
  21. package/dist/relations.js +47 -0
  22. package/dist/store.types.d.ts +1 -0
  23. package/dist/utils.d.ts +0 -3
  24. package/dist/utils.js +0 -21
  25. package/package.json +2 -2
  26. package/src/lib/actions.ts +1 -0
  27. package/src/lib/components/dataTable/fieldCell.svelte +38 -37
  28. package/src/lib/components/dataTable/polymorphicFieldCell.svelte +43 -0
  29. package/src/lib/components/dataTable/utils.ts +21 -18
  30. package/src/lib/components/dataTableDrawer/dataTableDrawer.svelte +14 -10
  31. package/src/lib/components/detailView/create/createDetailView.svelte +38 -69
  32. package/src/lib/components/detailView/fieldInput.svelte +27 -11
  33. package/src/lib/components/detailView/update/updateDetailView.svelte +26 -30
  34. package/src/lib/components/detailView/utils.ts +44 -75
  35. package/src/lib/components/drawer.svelte +24 -8
  36. package/src/lib/components/polymorphicInput.svelte +141 -0
  37. package/src/lib/extensions/extension.types.ts +2 -1
  38. package/src/lib/extensions/extensionUtils.ts +2 -0
  39. package/src/lib/relations.ts +52 -0
  40. package/src/lib/store.types.ts +1 -0
  41. 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 { getFieldRelation } from "../../utils";
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, rollback) {
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
- // serialize the foreign key field's value
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 = Boolean(getFieldRelation(ctx, collectionName, fieldName));
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
- // check for related collections properties and serialize them too
49
- if (!rollback) {
50
- var childrenRelations = ctx.meta.relations.filter(function (relation) { return relation.to.collection === collectionName; });
51
- var childrenCollectionNames = childrenRelations.map(function (relation) { return relation.from.collection; });
52
- for (var index = 0; index < childrenCollectionNames.length; index++) {
53
- var childrenCollectionName = childrenCollectionNames[index];
54
- var childrenEntries = entry[childrenCollectionName];
55
- if (childrenEntries) {
56
- for (var index_1 = 0; index_1 < childrenEntries.length; index_1++) {
57
- childrenEntries[index_1] = serializeEntry(ctx, childrenCollectionName, childrenEntries[index_1]);
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 generateTransactionBody(ctx, collectionName, entry) {
65
- entry = __assign({}, entry);
66
- function handleEntryRecursive(transactionBody, collectionName, entry, parentTransactionIndex) {
67
- var _a;
68
- var _b;
69
- var parentCollectionName = parentTransactionIndex !== undefined ? transactionBody[parentTransactionIndex].props.collectionName : null;
70
- var foreignKeyFieldName = (_b = ctx.meta.relations.find(function (relation) { return relation.from.collection === collectionName && relation.to.collection === parentCollectionName; })) === null || _b === void 0 ? void 0 : _b.from.field;
71
- var collectionFieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
72
- var payload = {};
73
- for (var index = 0; index < collectionFieldNames.length; index++) {
74
- var fieldName = collectionFieldNames[index];
75
- var isForeignKeyField = fieldName === foreignKeyFieldName;
76
- if (isForeignKeyField) {
77
- payload[fieldName] = "{{ responses[".concat(parentTransactionIndex, "].data.id }}");
78
- continue;
79
- }
80
- payload[fieldName] = entry[fieldName];
81
- }
82
- var localTransactionIndex = transactionBody.length;
83
- if (payload.id) {
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
- var transactionBody = [];
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, fly } from "svelte/transition";
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: 250 }}
32
+ transition:fade={{ duration: 200 }}
18
33
  onclick={() => onHide?.()}
19
- class="backgroundDrawerButton fixed left-0 top-0 z-40 h-screen w-screen bg-background opacity-50 cursor-default"
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:fly={{ x: "100%", duration: 250 }}
26
- class="fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-background"
27
- style="max-width: {calculateDrawerWidth()}px;"
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>
@@ -2,6 +2,7 @@ import type { Snippet } from "svelte";
2
2
  interface Props {
3
3
  children?: Snippet<[]>;
4
4
  onHide?: () => Promise<void>;
5
+ position?: "side" | "bottom";
5
6
  }
6
7
  declare const Drawer: import("svelte").Component<Props, {}, "">;
7
8
  type Drawer = ReturnType<typeof Drawer>;
@@ -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
+ ;
@@ -12,6 +12,7 @@ interface Collection {
12
12
  singleton: boolean;
13
13
  virtual?: boolean;
14
14
  ui?: {
15
+ icon?: string;
15
16
  tabs?: CollectionTab[];
16
17
  };
17
18
  }
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.19.1",
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.23.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",
@@ -15,6 +15,7 @@ export interface OpenDataTableDrawerProps {
15
15
  title?: string;
16
16
  showHeader?: boolean;
17
17
  showFooter?: boolean;
18
+ position?: "side" | "bottom";
18
19
  }
19
20
 
20
21
  export function showDialog(title: string, description: string): Promise<boolean> {
@@ -1,10 +1,10 @@
1
1
  <script lang="ts">
2
- import { getFieldRelation } from "../../utils";
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 _ from "lodash";
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 relation = getFieldRelation(ctx, collectionName, fieldName);
30
- const isRefrenceField = Boolean(
31
- getFieldRelation(ctx, collectionName, fieldName),
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 isPasswordField}
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"}