@lobb-js/studio 0.1.31

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 (208) hide show
  1. package/.env.example +1 -0
  2. package/.storybook/main.ts +31 -0
  3. package/.storybook/preview.ts +21 -0
  4. package/.storybook/vitest.setup.ts +7 -0
  5. package/README.md +47 -0
  6. package/components.json +16 -0
  7. package/docker-entrypoint.sh +7 -0
  8. package/dockerfile +27 -0
  9. package/index.html +13 -0
  10. package/package.json +77 -0
  11. package/public/lobb.svg +15 -0
  12. package/src/Studio.svelte +150 -0
  13. package/src/app.css +121 -0
  14. package/src/components-export.ts +21 -0
  15. package/src/extensions/extension.types.ts +93 -0
  16. package/src/extensions/extensionUtils.ts +192 -0
  17. package/src/lib/Lobb.ts +241 -0
  18. package/src/lib/components/LlmButton.svelte +136 -0
  19. package/src/lib/components/alertView.svelte +20 -0
  20. package/src/lib/components/breadCrumbs.svelte +60 -0
  21. package/src/lib/components/combobox.svelte +92 -0
  22. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +33 -0
  23. package/src/lib/components/confirmationDialog/store.svelte.ts +28 -0
  24. package/src/lib/components/createManyButton.svelte +107 -0
  25. package/src/lib/components/dataTable/childRecords.svelte +140 -0
  26. package/src/lib/components/dataTable/dataTable.svelte +223 -0
  27. package/src/lib/components/dataTable/fieldCell.svelte +74 -0
  28. package/src/lib/components/dataTable/filter.svelte +282 -0
  29. package/src/lib/components/dataTable/filterButton.svelte +39 -0
  30. package/src/lib/components/dataTable/footer.svelte +84 -0
  31. package/src/lib/components/dataTable/header.svelte +154 -0
  32. package/src/lib/components/dataTable/sort.svelte +171 -0
  33. package/src/lib/components/dataTable/sortButton.svelte +36 -0
  34. package/src/lib/components/dataTable/table.svelte +337 -0
  35. package/src/lib/components/dataTable/utils.ts +127 -0
  36. package/src/lib/components/detailView/create/children.svelte +68 -0
  37. package/src/lib/components/detailView/create/createDetailView.svelte +226 -0
  38. package/src/lib/components/detailView/create/createDetailViewButton.svelte +32 -0
  39. package/src/lib/components/detailView/create/createManyView.svelte +250 -0
  40. package/src/lib/components/detailView/create/subRecords.svelte +48 -0
  41. package/src/lib/components/detailView/detailViewForm.svelte +104 -0
  42. package/src/lib/components/detailView/fieldCustomInput.svelte +23 -0
  43. package/src/lib/components/detailView/fieldInput.svelte +287 -0
  44. package/src/lib/components/detailView/fieldInputReplacement.svelte +199 -0
  45. package/src/lib/components/detailView/store.svelte.ts +61 -0
  46. package/src/lib/components/detailView/update/children.svelte +94 -0
  47. package/src/lib/components/detailView/update/updateDetailView.svelte +175 -0
  48. package/src/lib/components/detailView/update/updateDetailViewButton.svelte +32 -0
  49. package/src/lib/components/detailView/utils.ts +177 -0
  50. package/src/lib/components/diffViewer.svelte +102 -0
  51. package/src/lib/components/drawer.svelte +28 -0
  52. package/src/lib/components/extensionsComponents.svelte +31 -0
  53. package/src/lib/components/foreingKeyInput.svelte +80 -0
  54. package/src/lib/components/header.svelte +45 -0
  55. package/src/lib/components/loadingTypesForMonacoEditor.ts +36 -0
  56. package/src/lib/components/miniSidebar.svelte +238 -0
  57. package/src/lib/components/monacoEditor.svelte +181 -0
  58. package/src/lib/components/rangeCalendarButton.svelte +257 -0
  59. package/src/lib/components/selectRecord.svelte +126 -0
  60. package/src/lib/components/setServerPage.svelte +48 -0
  61. package/src/lib/components/sidebar/index.ts +4 -0
  62. package/src/lib/components/sidebar/sidebar.svelte +149 -0
  63. package/src/lib/components/sidebar/sidebarElements.svelte +144 -0
  64. package/src/lib/components/sidebar/sidebarTrigger.svelte +33 -0
  65. package/src/lib/components/singletone.svelte +69 -0
  66. package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
  67. package/src/lib/components/ui/accordion/accordion-item.svelte +12 -0
  68. package/src/lib/components/ui/accordion/accordion-trigger.svelte +31 -0
  69. package/src/lib/components/ui/accordion/index.ts +17 -0
  70. package/src/lib/components/ui/alert/alert-description.svelte +16 -0
  71. package/src/lib/components/ui/alert/alert-title.svelte +24 -0
  72. package/src/lib/components/ui/alert/alert.svelte +39 -0
  73. package/src/lib/components/ui/alert/index.ts +14 -0
  74. package/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte +13 -0
  75. package/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte +17 -0
  76. package/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte +26 -0
  77. package/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte +16 -0
  78. package/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte +20 -0
  79. package/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte +20 -0
  80. package/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte +19 -0
  81. package/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte +18 -0
  82. package/src/lib/components/ui/alert-dialog/index.ts +40 -0
  83. package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
  84. package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +16 -0
  85. package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
  86. package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
  87. package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
  88. package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
  89. package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +15 -0
  90. package/src/lib/components/ui/breadcrumb/index.ts +25 -0
  91. package/src/lib/components/ui/button/button.svelte +110 -0
  92. package/src/lib/components/ui/button/index.ts +17 -0
  93. package/src/lib/components/ui/checkbox/checkbox.svelte +35 -0
  94. package/src/lib/components/ui/checkbox/index.ts +6 -0
  95. package/src/lib/components/ui/command/command-dialog.svelte +35 -0
  96. package/src/lib/components/ui/command/command-empty.svelte +12 -0
  97. package/src/lib/components/ui/command/command-group.svelte +31 -0
  98. package/src/lib/components/ui/command/command-input.svelte +25 -0
  99. package/src/lib/components/ui/command/command-item.svelte +19 -0
  100. package/src/lib/components/ui/command/command-link-item.svelte +19 -0
  101. package/src/lib/components/ui/command/command-list.svelte +16 -0
  102. package/src/lib/components/ui/command/command-separator.svelte +12 -0
  103. package/src/lib/components/ui/command/command-shortcut.svelte +20 -0
  104. package/src/lib/components/ui/command/command.svelte +21 -0
  105. package/src/lib/components/ui/command/index.ts +40 -0
  106. package/src/lib/components/ui/dialog/dialog-content.svelte +38 -0
  107. package/src/lib/components/ui/dialog/dialog-description.svelte +16 -0
  108. package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
  109. package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
  110. package/src/lib/components/ui/dialog/dialog-overlay.svelte +19 -0
  111. package/src/lib/components/ui/dialog/dialog-title.svelte +16 -0
  112. package/src/lib/components/ui/dialog/index.ts +37 -0
  113. package/src/lib/components/ui/input/index.ts +7 -0
  114. package/src/lib/components/ui/input/input.svelte +46 -0
  115. package/src/lib/components/ui/label/index.ts +7 -0
  116. package/src/lib/components/ui/label/label.svelte +19 -0
  117. package/src/lib/components/ui/popover/index.ts +17 -0
  118. package/src/lib/components/ui/popover/popover-content.svelte +28 -0
  119. package/src/lib/components/ui/range-calendar/index.ts +30 -0
  120. package/src/lib/components/ui/range-calendar/range-calendar-cell.svelte +19 -0
  121. package/src/lib/components/ui/range-calendar/range-calendar-day.svelte +35 -0
  122. package/src/lib/components/ui/range-calendar/range-calendar-grid-body.svelte +12 -0
  123. package/src/lib/components/ui/range-calendar/range-calendar-grid-head.svelte +12 -0
  124. package/src/lib/components/ui/range-calendar/range-calendar-grid-row.svelte +12 -0
  125. package/src/lib/components/ui/range-calendar/range-calendar-grid.svelte +16 -0
  126. package/src/lib/components/ui/range-calendar/range-calendar-head-cell.svelte +16 -0
  127. package/src/lib/components/ui/range-calendar/range-calendar-header.svelte +16 -0
  128. package/src/lib/components/ui/range-calendar/range-calendar-heading.svelte +16 -0
  129. package/src/lib/components/ui/range-calendar/range-calendar-months.svelte +20 -0
  130. package/src/lib/components/ui/range-calendar/range-calendar-next-button.svelte +27 -0
  131. package/src/lib/components/ui/range-calendar/range-calendar-prev-button.svelte +27 -0
  132. package/src/lib/components/ui/range-calendar/range-calendar.svelte +57 -0
  133. package/src/lib/components/ui/select/index.ts +34 -0
  134. package/src/lib/components/ui/select/select-content.svelte +38 -0
  135. package/src/lib/components/ui/select/select-group-heading.svelte +16 -0
  136. package/src/lib/components/ui/select/select-item.svelte +37 -0
  137. package/src/lib/components/ui/select/select-scroll-down-button.svelte +19 -0
  138. package/src/lib/components/ui/select/select-scroll-up-button.svelte +19 -0
  139. package/src/lib/components/ui/select/select-separator.svelte +13 -0
  140. package/src/lib/components/ui/select/select-trigger.svelte +24 -0
  141. package/src/lib/components/ui/separator/index.ts +7 -0
  142. package/src/lib/components/ui/separator/separator.svelte +22 -0
  143. package/src/lib/components/ui/skeleton/index.ts +7 -0
  144. package/src/lib/components/ui/skeleton/skeleton.svelte +22 -0
  145. package/src/lib/components/ui/sonner/index.ts +1 -0
  146. package/src/lib/components/ui/sonner/sonner.svelte +20 -0
  147. package/src/lib/components/ui/switch/index.ts +7 -0
  148. package/src/lib/components/ui/switch/switch.svelte +27 -0
  149. package/src/lib/components/ui/textarea/index.ts +7 -0
  150. package/src/lib/components/ui/textarea/textarea.svelte +22 -0
  151. package/src/lib/components/ui/tooltip/index.ts +18 -0
  152. package/src/lib/components/ui/tooltip/tooltip-content.svelte +21 -0
  153. package/src/lib/components/workflowEditor.svelte +187 -0
  154. package/src/lib/eventSystem.ts +38 -0
  155. package/src/lib/index.ts +40 -0
  156. package/src/lib/store.svelte.ts +21 -0
  157. package/src/lib/store.types.ts +28 -0
  158. package/src/lib/utils.ts +84 -0
  159. package/src/main.ts +18 -0
  160. package/src/routes/collections/collection.svelte +46 -0
  161. package/src/routes/collections/collections.svelte +43 -0
  162. package/src/routes/data_model/dataModel.svelte +40 -0
  163. package/src/routes/data_model/flow.css +22 -0
  164. package/src/routes/data_model/flow.svelte +82 -0
  165. package/src/routes/data_model/syncManager.svelte +93 -0
  166. package/src/routes/data_model/utils.ts +35 -0
  167. package/src/routes/extensions/extension.svelte +16 -0
  168. package/src/routes/home.svelte +36 -0
  169. package/src/routes/workflows/workflows.svelte +135 -0
  170. package/src/stories/Configure.mdx +364 -0
  171. package/src/stories/assets/accessibility.png +0 -0
  172. package/src/stories/assets/accessibility.svg +1 -0
  173. package/src/stories/assets/addon-library.png +0 -0
  174. package/src/stories/assets/assets.png +0 -0
  175. package/src/stories/assets/avif-test-image.avif +0 -0
  176. package/src/stories/assets/context.png +0 -0
  177. package/src/stories/assets/discord.svg +1 -0
  178. package/src/stories/assets/docs.png +0 -0
  179. package/src/stories/assets/figma-plugin.png +0 -0
  180. package/src/stories/assets/github.svg +1 -0
  181. package/src/stories/assets/share.png +0 -0
  182. package/src/stories/assets/styling.png +0 -0
  183. package/src/stories/assets/testing.png +0 -0
  184. package/src/stories/assets/theming.png +0 -0
  185. package/src/stories/assets/tutorials.svg +1 -0
  186. package/src/stories/assets/youtube.svg +1 -0
  187. package/src/stories/detailView/detailViewForm.stories.svelte +79 -0
  188. package/src/stories/examples/Button.stories.svelte +31 -0
  189. package/src/stories/examples/Button.svelte +30 -0
  190. package/src/stories/examples/Header.stories.svelte +26 -0
  191. package/src/stories/examples/Header.svelte +45 -0
  192. package/src/stories/examples/Page.stories.svelte +29 -0
  193. package/src/stories/examples/Page.svelte +70 -0
  194. package/src/stories/examples/button.css +30 -0
  195. package/src/stories/examples/header.css +32 -0
  196. package/src/stories/examples/page.css +68 -0
  197. package/src/vite-env.d.ts +2 -0
  198. package/svelte.config.js +7 -0
  199. package/todo.md +24 -0
  200. package/tsconfig.app.json +25 -0
  201. package/tsconfig.json +14 -0
  202. package/tsconfig.node.json +24 -0
  203. package/vite-plugin-contextual-lib.js +66 -0
  204. package/vite.build.svelte.config.ts +18 -0
  205. package/vite.config.ts +84 -0
  206. package/vite.extension.config.ts +81 -0
  207. package/vite_utils.ts +28 -0
  208. package/vitest.shims.d.ts +1 -0
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import DataTable from "$lib/components/dataTable/dataTable.svelte";
3
+ import { ctx } from "$lib/store.svelte";
4
+ import { Link, Plus, TableIcon } from "lucide-svelte";
5
+ import CreateDetailViewButton from "../create/createDetailViewButton.svelte";
6
+ import ExtensionsComponents from "$lib/components/extensionsComponents.svelte";
7
+ import { getExtensionUtils } from "../../../../extensions/extensionUtils";
8
+
9
+ interface LocalProp {
10
+ collectionName: string;
11
+ entry: any;
12
+ }
13
+
14
+ let { collectionName, entry }: LocalProp = $props();
15
+
16
+ const childrenRelations = ctx.meta.relations.filter(
17
+ (relation) => relation.to.collection === collectionName,
18
+ );
19
+ const refresh: boolean[] = $state(
20
+ new Array(childrenRelations.length).fill(true),
21
+ );
22
+ </script>
23
+
24
+ {#if childrenRelations.length}
25
+ <div class="flex flex-col gap-4 border-t p-4">
26
+ <div class="flex items-center gap-2">
27
+ <Link size="17.5" />
28
+ <div>Sub Records</div>
29
+ </div>
30
+ <div class="flex flex-col gap-4">
31
+ {#each childrenRelations as relation, index}
32
+ {@const childCollection = relation.from.collection}
33
+ {@const childField = relation.from.field}
34
+ <ExtensionsComponents
35
+ name="detailView.update.subRecords.{childCollection}"
36
+ utils={getExtensionUtils()}
37
+ collectionName={childCollection}
38
+ filter={{
39
+ [childField]: entry.id,
40
+ }}
41
+ class="bg-soft border rounded-md overflow-hidden"
42
+ >
43
+ <div class="border rounded-md overflow-clip">
44
+ <div
45
+ class="flex items-center justify-between px-2 h-10 bg-soft border-b"
46
+ >
47
+ <div class="flex-1 flex h-full items-center gap-2">
48
+ <TableIcon
49
+ class="text-muted-foreground"
50
+ size="17.5"
51
+ />
52
+ <div class="text-sm text-muted-foreground">
53
+ {childCollection}
54
+ </div>
55
+ </div>
56
+ <div class="flex gap-2">
57
+ <CreateDetailViewButton
58
+ variant="ghost"
59
+ class="h-7 px-2 font-normal text-xs"
60
+ Icon={Plus}
61
+ collectionName={childCollection}
62
+ onSuccessfullSave={async () => {
63
+ refresh[index] = !refresh[index];
64
+ }}
65
+ >
66
+ Create
67
+ </CreateDetailViewButton>
68
+ </div>
69
+ </div>
70
+ <div class="max-h-72 overflow-auto rounded-md">
71
+ {#key refresh[index]}
72
+ <DataTable
73
+ collectionName={childCollection}
74
+ filter={{
75
+ [childField]: entry.id,
76
+ }}
77
+ unifiedBgColor="bg-soft"
78
+ showHeader={false}
79
+ showFooter={false}
80
+ showDelete={true}
81
+ tableProps={{
82
+ showLastColumnBorder: false,
83
+ showLastRowBorder: false,
84
+ showCheckboxes: false,
85
+ }}
86
+ />
87
+ {/key}
88
+ </div>
89
+ </div>
90
+ </ExtensionsComponents>
91
+ {/each}
92
+ </div>
93
+ </div>
94
+ {/if}
@@ -0,0 +1,175 @@
1
+ <script lang="ts" module>
2
+ interface SubmitButton {
3
+ text: string;
4
+ icon: any;
5
+ }
6
+
7
+ export interface UpdateDetailViewProp {
8
+ collectionName: string;
9
+ recordId: string;
10
+ values?: Record<string, any>;
11
+ showRelatedRecords?: boolean;
12
+ rollback?: boolean;
13
+ submitButton?: SubmitButton;
14
+ title?: Snippet<[string]>;
15
+ onSuccessfullSave?: (entry: any) => Promise<void>;
16
+ onCancel?: () => Promise<void>;
17
+ }
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ import { ArrowLeft, Pencil, X } from "lucide-svelte";
22
+ import Button from "$lib/components/ui/button/button.svelte";
23
+ import { fade, fly } from "svelte/transition";
24
+ import { lobb } from "$lib";
25
+ import { toast } from "svelte-sonner";
26
+ import { ctx } from "$lib/store.svelte";
27
+ import ExtensionsComponents from "../../extensionsComponents.svelte";
28
+ import { getExtensionUtils } from "../../../../extensions/extensionUtils";
29
+ import { calculateDrawerWidth, getChangedProperties } from "$lib/utils";
30
+ import { getField, getFieldIcon } from "../../dataTable/utils";
31
+ import Children from "../update/children.svelte";
32
+ import type { Snippet } from "svelte";
33
+ import { getDefaultEntry, parseDetailViewValues, serializeEntry } from "../utils";
34
+ import FieldInput from "../fieldInput.svelte";
35
+ import Drawer from "$lib/components/drawer.svelte";
36
+
37
+ let {
38
+ collectionName,
39
+ values = {},
40
+ showRelatedRecords = true,
41
+ onCancel,
42
+ onSuccessfullSave,
43
+ title,
44
+ submitButton,
45
+ recordId,
46
+ }: UpdateDetailViewProp = $props();
47
+
48
+ parseDetailViewValues(collectionName, values)
49
+
50
+ const fieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
51
+ let entry: Record<string, any> = $state(
52
+ getDefaultEntry(fieldNames, collectionName, values),
53
+ );
54
+ const initialEntry = $state.snapshot(entry);
55
+ let localEntry = $derived(
56
+ getChangedProperties(initialEntry, $state.snapshot(entry)),
57
+ );
58
+ let fieldsErrors: Record<string, any> = $state({});
59
+
60
+ async function handleSave() {
61
+ delete localEntry.id;
62
+ localEntry = serializeEntry(collectionName, localEntry);
63
+
64
+ const response = await lobb.updateOne(
65
+ collectionName,
66
+ recordId,
67
+ localEntry,
68
+ );
69
+
70
+ if (!response.bodyUsed) {
71
+ const result = await response.json();
72
+ if (response.status >= 400) {
73
+ if (result.message && result.details) {
74
+ fieldsErrors = result.details;
75
+ return;
76
+ } else if (result.message) {
77
+ return;
78
+ }
79
+ }
80
+ }
81
+
82
+ // close detailView side bar
83
+ if (onSuccessfullSave) {
84
+ await onSuccessfullSave(localEntry);
85
+ }
86
+
87
+ toast.success(`The record was successfully updated`);
88
+
89
+ onCancel?.();
90
+ }
91
+ </script>
92
+
93
+ <Drawer onHide={onCancel}>
94
+ <div class="flex h-12 items-center gap-4 border-b px-4">
95
+ <Button
96
+ variant="outline"
97
+ onclick={onCancel}
98
+ class=" h-8 w-8 rounded-full text-xs font-normal"
99
+ Icon={ArrowLeft}
100
+ ></Button>
101
+ <div class="flex items-center gap-2 text-sm">
102
+ {#if title}
103
+ {@render title(collectionName)}
104
+ {:else}
105
+ <div>Update record of</div>
106
+ <div class="rounded-md border bg-muted px-2 py-0.5">
107
+ {collectionName}
108
+ </div>
109
+ {/if}
110
+ </div>
111
+ </div>
112
+ <div class="flex-1 overflow-y-auto">
113
+ <div class="flex flex-col gap-4 p-4">
114
+ {#each fieldNames as fieldName}
115
+ {@const field = getField(fieldName, collectionName)}
116
+ {@const FieldIcon = getFieldIcon(fieldName, collectionName)}
117
+ <div
118
+ class="flex flex-col gap-2"
119
+ >
120
+ <div
121
+ class="flex flex-1 items-end justify-between gap-2 text-xs"
122
+ >
123
+ <div class="flex gap-2">
124
+ <div class="h-fit">{field.label}</div>
125
+ <div
126
+ class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground"
127
+ >
128
+ <FieldIcon size="12" />
129
+ {field.type}
130
+ </div>
131
+ </div>
132
+ <div>
133
+ <ExtensionsComponents
134
+ name="dvFields.topRight.{collectionName}.{fieldName}"
135
+ utils={getExtensionUtils()}
136
+ bind:value={entry[fieldName]}
137
+ />
138
+ </div>
139
+ </div>
140
+ <FieldInput
141
+ {collectionName}
142
+ {fieldName}
143
+ bind:value={entry[fieldName]}
144
+ {entry}
145
+ errorMessages={fieldsErrors[fieldName]}
146
+ />
147
+ </div>
148
+ {/each}
149
+ </div>
150
+ {#if showRelatedRecords}
151
+ <Children {collectionName} {entry} />
152
+ {/if}
153
+ </div>
154
+ <div class="flex h-12 items-center justify-end gap-2 border-t px-4">
155
+ <div class="flex gap-3">
156
+ <Button
157
+ variant="outline"
158
+ onclick={onCancel}
159
+ class="h-7 px-3 text-xs font-normal"
160
+ Icon={X}
161
+ >
162
+ Cancel
163
+ </Button>
164
+ <Button
165
+ variant="default"
166
+ class="h-7 px-3 text-xs font-normal"
167
+ Icon={submitButton?.icon ? submitButton.icon : Pencil}
168
+ onclick={handleSave}
169
+ disabled={!Object.keys(localEntry).length}
170
+ >
171
+ {submitButton?.text ? submitButton.text : "Update"}
172
+ </Button>
173
+ </div>
174
+ </div>
175
+ </Drawer>
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import type { UpdateDetailViewProp } from "./updateDetailView.svelte";
3
+ import type { ButtonProps } from "$lib/components/ui/button/button.svelte";
4
+ import Button from "$lib/components/ui/button/button.svelte";
5
+ import { openUpdateDetailView } from "../store.svelte";
6
+
7
+ interface LocalProp extends UpdateDetailViewProp {
8
+ variant?: ButtonProps["variant"];
9
+ class?: ButtonProps["class"];
10
+ Icon?: ButtonProps["Icon"];
11
+ children?: ButtonProps["children"];
12
+ }
13
+
14
+ let props: LocalProp = $props();
15
+ let entry: Record<string, any> | undefined = $state();
16
+ </script>
17
+
18
+ <Button
19
+ variant={props.variant}
20
+ class={props.class}
21
+ Icon={props.Icon}
22
+ onclick={() => {
23
+ openUpdateDetailView({
24
+ values: entry,
25
+ ...props
26
+ });
27
+ }}
28
+ >
29
+ {#if props.children}
30
+ {@render props.children()}
31
+ {/if}
32
+ </Button>
@@ -0,0 +1,177 @@
1
+ import Mustache from "mustache";
2
+ import { ctx } from "$lib/store.svelte";
3
+ import { getFieldRelation } from "$lib/utils";
4
+ import { getField } from "../dataTable/utils";
5
+ import type { DetailFormField } from "./detailViewForm.svelte";
6
+
7
+ export function getDefaultEntry(fieldNames: string[], collectionName: string, values?: Record<string, any>) {
8
+ return Object.fromEntries(
9
+ fieldNames.map((fieldName) => {
10
+ let value = null;
11
+ const field = getField(fieldName, collectionName);
12
+ if (values && values[fieldName] !== undefined) {
13
+ value = values[fieldName];
14
+ } else if (field.pre_processors?.default) {
15
+ const defualtValue = field.pre_processors.default;
16
+ if (typeof defualtValue === "string") {
17
+ value = Mustache.render(defualtValue, {
18
+ now: new Date().toISOString(),
19
+ });
20
+ } else {
21
+ value = defualtValue;
22
+ }
23
+ }
24
+ return [fieldName, value];
25
+ }),
26
+ );
27
+ }
28
+
29
+ export function serializeEntry(
30
+ collectionName: string,
31
+ entry: Record<string, any>,
32
+ rollback: boolean = false,
33
+ ) {
34
+ // deep clone the object
35
+ entry = { ...entry }
36
+
37
+ // serialize the foreign key field's value
38
+ for (const [fieldName, fieldValue] of Object.entries(entry)) {
39
+ const isRefrenceField = Boolean(getFieldRelation(collectionName, fieldName));
40
+ if (isRefrenceField && fieldValue !== null && fieldValue.id !== undefined) {
41
+ entry[fieldName] = fieldValue.id;
42
+ }
43
+ }
44
+
45
+ // check for related collections properties and serialize them too
46
+ if (!rollback) {
47
+ const childrenRelations = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName);
48
+ const childrenCollectionNames = childrenRelations.map((relation) => relation.from.collection);
49
+ for (let index = 0; index < childrenCollectionNames.length; index++) {
50
+ const childrenCollectionName = childrenCollectionNames[index];
51
+ const childrenEntries = entry[childrenCollectionName];
52
+ if (childrenEntries) {
53
+ for (let index = 0; index < childrenEntries.length; index++) {
54
+ childrenEntries[index] = serializeEntry(childrenCollectionName, childrenEntries[index]);
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ return entry;
61
+ }
62
+
63
+ export function generateTransactionBody(
64
+ collectionName: string,
65
+ entry: Record<string, any>,
66
+ ) {
67
+ entry = { ...entry }
68
+ function handleEntryRecursive(
69
+ transactionBody: any[],
70
+ collectionName: string,
71
+ entry: Record<string, any>,
72
+ parentTransactionIndex?: number,
73
+ ) {
74
+ const parentCollectionName = parentTransactionIndex !== undefined ? transactionBody[parentTransactionIndex].collection : null;
75
+ const foreignKeyFieldName = ctx.meta.relations.find(relation => relation.from.collection === collectionName && relation.to.collection === parentCollectionName)?.from.field;
76
+ const collectionFieldNames = Object.keys(ctx.meta.collections[collectionName].fields);
77
+ const payload: any = {};
78
+ for (let index = 0; index < collectionFieldNames.length; index++) {
79
+ const fieldName = collectionFieldNames[index];
80
+ const isForeignKeyField = fieldName === foreignKeyFieldName;
81
+ if (isForeignKeyField) {
82
+ payload[fieldName] = `{{ responses[${parentTransactionIndex}].data.id }}`
83
+ continue;
84
+ }
85
+
86
+ payload[fieldName] = entry[fieldName];
87
+ }
88
+
89
+ const localTransactionIndex = transactionBody.length;
90
+ if (payload.id) {
91
+ const localPayload = {
92
+ [foreignKeyFieldName]: payload[foreignKeyFieldName],
93
+ };
94
+ transactionBody.push({
95
+ collection: collectionName,
96
+ method: "updateMany",
97
+ args: [
98
+ localPayload,
99
+ {
100
+ id: payload.id
101
+ }
102
+ ],
103
+ });
104
+ } else {
105
+ transactionBody.push({
106
+ collection: collectionName,
107
+ method: "createOne",
108
+ args: [payload],
109
+ });
110
+ }
111
+
112
+ const childrenRelations = ctx.meta.relations.filter((relation) => relation.to.collection === collectionName);
113
+ const childrenCollectionNames = childrenRelations.map((relation) => relation.from.collection);
114
+ for (let index = 0; index < childrenCollectionNames.length; index++) {
115
+ const childrenCollectionName = childrenCollectionNames[index];
116
+ const childrenEntries = entry[childrenCollectionName];
117
+ if (childrenEntries) {
118
+ for (let index = 0; index < childrenEntries.length; index++) {
119
+ const childrenEntry = childrenEntries[index];
120
+ handleEntryRecursive(transactionBody, childrenCollectionName, childrenEntry, localTransactionIndex);
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ const transactionBody: any[] = [];
127
+
128
+ handleEntryRecursive(transactionBody, collectionName, entry);
129
+
130
+ return transactionBody
131
+ }
132
+
133
+ export function parseDetailViewValues(collectionName: string, values: Record<string, any>) {
134
+ const forignFieldNames = ctx.meta.relations
135
+ .filter((relation) => relation.from.collection === collectionName)
136
+ .map((relation) => relation.from.field);
137
+ const childCollectionNames = ctx.meta.relations
138
+ .filter((relation) => relation.to.collection === collectionName)
139
+ .map((relation) => relation.from.collection);
140
+
141
+ for (const [key, value] of Object.entries(values)) {
142
+ if (forignFieldNames.includes(key)) {
143
+ if (typeof value === 'number') {
144
+ values[key] = {
145
+ id: value,
146
+ }
147
+ }
148
+ } else if (childCollectionNames.includes(key)) {
149
+ for (let index = 0; index < values[key].length; index++) {
150
+ parseDetailViewValues(key, values[key][index]);
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ export function getCollectionFields(collectionName: string) {
157
+ let returnedData: DetailFormField[] = [];
158
+
159
+ const collectionFields = ctx.meta.collections[collectionName].fields;
160
+ const isSingleton = ctx.meta.collections[collectionName].singleton;
161
+ for (const [fieldName, value] of Object.entries(collectionFields)) {
162
+
163
+ if (isSingleton && fieldName === "id") {
164
+ continue;
165
+ }
166
+
167
+ returnedData.push({
168
+ type: "input",
169
+ key: fieldName,
170
+ label: fieldName,
171
+ disabled: fieldName == "id",
172
+ placeholder: fieldName == "id" ? "AUTO_GENERATED" : "",
173
+ })
174
+ }
175
+
176
+ return returnedData;
177
+ }
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from "svelte";
3
+ import * as monaco from "monaco-editor";
4
+ import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
5
+ import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
6
+ import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
7
+ import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
8
+ import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
9
+ import { cn } from "$lib/utils";
10
+
11
+ interface Props {
12
+ type: "javascript" | "typescript" | "json";
13
+ original: string;
14
+ modified: string;
15
+ class?: string;
16
+ }
17
+
18
+ let {
19
+ type,
20
+ original,
21
+ modified,
22
+ class: className,
23
+ ...props
24
+ }: Props = $props();
25
+
26
+ let editorContainer: HTMLDivElement;
27
+ let editor: monaco.editor.IStandaloneDiffEditor;
28
+ let originalModel: monaco.editor.ITextModel;
29
+ let modifiedModel: monaco.editor.ITextModel;
30
+
31
+ self.MonacoEnvironment = {
32
+ getWorker(_, label) {
33
+ if (label === "json") {
34
+ return new jsonWorker();
35
+ }
36
+ if (label === "css" || label === "scss" || label === "less") {
37
+ return new cssWorker();
38
+ }
39
+ if (
40
+ label === "html" ||
41
+ label === "handlebars" ||
42
+ label === "razor"
43
+ ) {
44
+ return new htmlWorker();
45
+ }
46
+ if (label === "typescript" || label === "javascript") {
47
+ return new tsWorker();
48
+ }
49
+ return new editorWorker();
50
+ },
51
+ };
52
+
53
+ onMount(async () => {
54
+ monaco.editor.defineTheme("transparentTheme", {
55
+ base: "vs",
56
+ inherit: true,
57
+ rules: [],
58
+ colors: {
59
+ "editor.background": "#EFEFEF00",
60
+ focusBorder: "#00000000",
61
+ },
62
+ });
63
+
64
+ originalModel = monaco.editor.createModel(original, type);
65
+
66
+ modifiedModel = monaco.editor.createModel(modified, type);
67
+
68
+ editor = monaco.editor.createDiffEditor(editorContainer, {
69
+ renderSideBySide: true,
70
+ readOnly: true,
71
+ automaticLayout: true,
72
+ theme: "transparentTheme",
73
+ minimap: { enabled: false },
74
+ glyphMargin: false,
75
+ overviewRulerLanes: 0,
76
+ hideCursorInOverviewRuler: true,
77
+ stickyScroll: { enabled: false },
78
+ lineNumbersMinChars: 2,
79
+ scrollBeyondLastLine: false,
80
+ padding: { top: 10, bottom: 1 },
81
+ });
82
+
83
+ editor.setModel({
84
+ original: originalModel,
85
+ modified: modifiedModel,
86
+ });
87
+ });
88
+
89
+ onDestroy(() => {
90
+ editor?.dispose();
91
+ originalModel.dispose();
92
+ modifiedModel.dispose();
93
+ });
94
+ </script>
95
+
96
+ <div class={cn("resize-y rounded-md border bg-soft shadow-sm h-60", className)}>
97
+ <div
98
+ bind:this={editorContainer}
99
+ class="editor pl-2"
100
+ style="height: 100%; width: 100%;"
101
+ ></div>
102
+ </div>
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import { calculateDrawerWidth } from "$lib/utils";
3
+ import type { Snippet } from "svelte";
4
+ import { fade, fly } from "svelte/transition";
5
+
6
+ interface Props {
7
+ children?: Snippet<[]>;
8
+ onHide?: () => Promise<void>;
9
+ }
10
+
11
+ let { onHide, children }: Props = $props();
12
+ </script>
13
+
14
+ <button
15
+ transition:fade={{ duration: 250 }}
16
+ onclick={() => onHide?.()}
17
+ class="backgroundDrawerButton fixed left-0 top-0 z-30 h-screen w-screen bg-background opacity-50 cursor-default"
18
+ aria-label="background used to hide the background"
19
+ ></button>
20
+
21
+ <!-- the drawer -->
22
+ <div
23
+ transition:fly={{ x: "100%", duration: 250 }}
24
+ class="fixed right-0 top-0 z-30 flex h-full w-full flex-col border-l bg-background"
25
+ style="max-width: {calculateDrawerWidth()}px;"
26
+ >
27
+ {@render children?.()}
28
+ </div>
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import { loadExtensionComponents } from "../../extensions/extensionUtils";
4
+
5
+ interface Props {
6
+ name: string;
7
+ filterByExtensions?: string[];
8
+ value?: unknown;
9
+ children?: Snippet<[]>;
10
+ [key: string]: any;
11
+ }
12
+
13
+ let {
14
+ name,
15
+ filterByExtensions,
16
+ value = $bindable(),
17
+ children,
18
+ ...props
19
+ }: Props = $props();
20
+
21
+ const Components = loadExtensionComponents(name, filterByExtensions);
22
+ </script>
23
+
24
+ {#if Components.length}
25
+ {#each Components as Component}
26
+ <Component bind:value {...props} />
27
+ {/each}
28
+ {:else}
29
+ {@render children?.()}
30
+ {/if}
31
+