@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,104 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { Icon as IconType } from 'lucide-svelte';
4
+ import FieldInputReplacement, { type CustomFieldSnippet } from "./fieldInputReplacement.svelte";
5
+ import type { HTMLInputTypeAttribute } from "svelte/elements";
6
+
7
+ export interface BaseField {
8
+ key: string;
9
+ label: string;
10
+ icon?: typeof IconType;
11
+ errorMessages?: string[];
12
+ disabled?: boolean;
13
+ customFieldSnippet?: CustomFieldSnippet;
14
+ }
15
+
16
+ export interface InputField extends BaseField {
17
+ type: 'input';
18
+ placeholder?: string;
19
+ inputType?: HTMLInputTypeAttribute;
20
+ inputStep?: number | "any" | undefined;
21
+ }
22
+
23
+ export interface TextAreaField extends BaseField {
24
+ type: 'textarea';
25
+ placeholder?: string;
26
+ }
27
+
28
+ export interface SelectField extends BaseField {
29
+ type: 'select';
30
+ options: string[];
31
+ placeholder?: string;
32
+ }
33
+
34
+ export interface DateField extends BaseField {
35
+ type: 'date';
36
+ }
37
+
38
+ export interface timeField extends BaseField {
39
+ type: 'time';
40
+ }
41
+
42
+ export interface DateTimeField extends BaseField {
43
+ type: 'datetime';
44
+ }
45
+
46
+ export interface BooleanField extends BaseField {
47
+ type: 'switch';
48
+ }
49
+
50
+ export type DetailFormField = InputField | TextAreaField | SelectField | DateField | timeField | DateTimeField | BooleanField;
51
+
52
+ interface DetailViewFormProps {
53
+ fields: DetailFormField[];
54
+ value?: Record<string, unknown>;
55
+ topRight?: Snippet<[]>;
56
+ }
57
+
58
+ let { value = $bindable({}), fields, topRight }: DetailViewFormProps = $props();
59
+ </script>
60
+
61
+ <div class="flex flex-col gap-4 p-4">
62
+ {#each fields as field}
63
+ <div
64
+ class="flex flex-col gap-2"
65
+ >
66
+ <div
67
+ class="flex flex-1 items-end justify-between gap-2 text-xs"
68
+ >
69
+ <div class="flex gap-2">
70
+ <div class="h-fit">{field.label}</div>
71
+ <div
72
+ class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground"
73
+ >
74
+ {#if field.icon}
75
+ <field.icon size="12" />
76
+ {/if}
77
+ {field.type}
78
+ </div>
79
+ </div>
80
+ <div>
81
+ {@render topRight?.()}
82
+ <!-- TODO: WHEN YOU USE THIS COMPONENT IN THE CREATE AND UPDATE DETAIL VIEW ADD THIS PART TO IT -->
83
+ <!-- <ExtensionsComponents
84
+ name="detailView.fields.topRight.{collectionName}.{fieldName}"
85
+ utils={getExtensionUtils()}
86
+ bind:value={value[field.key]}
87
+ /> -->
88
+ </div>
89
+ </div>
90
+ <FieldInputReplacement
91
+ field={field}
92
+ bind:value={
93
+ () => value[field.key],
94
+ (v) =>
95
+ (value = {
96
+ ...value,
97
+ [field.key]: v,
98
+ })
99
+ }
100
+ customFieldInput={field.customFieldSnippet}
101
+ ></FieldInputReplacement>
102
+ </div>
103
+ {/each}
104
+ </div>
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import MonacoEditor from "../monacoEditor.svelte";
3
+
4
+ interface Props {
5
+ value: any;
6
+ type: string;
7
+ args: any;
8
+ field: any;
9
+ destructive?: boolean;
10
+ }
11
+
12
+ let {
13
+ value = $bindable(),
14
+ type,
15
+ args,
16
+ field,
17
+ destructive,
18
+ }: Props = $props();
19
+ </script>
20
+
21
+ {#if type === "code"}
22
+ <MonacoEditor name={field.key} {...args} bind:value />
23
+ {/if}
@@ -0,0 +1,287 @@
1
+ <script lang="ts">
2
+ import { ctx } from "$lib/store.svelte";
3
+ import {
4
+ getDiscriminatorFieldRelation,
5
+ getFieldRelation,
6
+ } from "$lib/utils";
7
+ import { Ban, Check, CircleAlert, X } from "lucide-svelte";
8
+ import { getField } from "../dataTable/utils";
9
+ import Button from "../ui/button/button.svelte";
10
+ import FieldCustomInput from "./fieldCustomInput.svelte";
11
+ import Input from "../ui/input/input.svelte";
12
+ import Combobox from "../combobox.svelte";
13
+ import * as Select from "$lib/components/ui/select/index";
14
+ import Textarea from "../ui/textarea/textarea.svelte";
15
+ import ForeingKeyInput from "../foreingKeyInput.svelte";
16
+ import ExtensionsComponents from "../extensionsComponents.svelte";
17
+ import { getExtensionUtils } from "../../../extensions/extensionUtils";
18
+
19
+ interface Props {
20
+ collectionName: string;
21
+ fieldName: string;
22
+ value: any;
23
+ errorMessages?: string[];
24
+ entry?: Record<string, any>;
25
+ }
26
+
27
+ let {
28
+ collectionName,
29
+ fieldName,
30
+ value = $bindable(),
31
+ errorMessages = [],
32
+ entry,
33
+ }: Props = $props();
34
+
35
+ const ui_input =
36
+ ctx.meta.collections[collectionName].fields[fieldName].ui_input;
37
+ const ui =
38
+ ctx.meta.collections[collectionName].fields[fieldName].ui;
39
+ const field = getField(fieldName, collectionName);
40
+ const fieldRelation = getFieldRelation(collectionName, fieldName);
41
+ const discriminatorFieldRelation = getDiscriminatorFieldRelation(
42
+ collectionName,
43
+ fieldName,
44
+ );
45
+ const isDisabled = field.key === 'id' || Boolean(ui?.disabled)
46
+ const disabledClasses = "pointer-events-none opacity-50";
47
+ const destructive: boolean = $derived(Boolean(errorMessages.length));
48
+
49
+ </script>
50
+
51
+ <div class="{isDisabled ? "cursor-not-allowed" : ''}">
52
+ <div
53
+ class="
54
+ relative flex flex-col gap-2
55
+ {isDisabled ? disabledClasses : ''}
56
+ "
57
+ style="flex: 2;"
58
+ >
59
+ <Button
60
+ onclick={() => (value = null)}
61
+ variant="outline"
62
+ class="absolute right-0 top-0 z-10 mr-1.5 mt-1.5 aspect-square h-6 w-6 p-0"
63
+ Icon={Ban}
64
+ tabindex={-1}
65
+ ></Button>
66
+ {#if ui_input}
67
+ <FieldCustomInput
68
+ bind:value
69
+ type={ui_input.type}
70
+ args={ui_input.args}
71
+ {field}
72
+ {destructive}
73
+ />
74
+ {:else if field.label === "id"}
75
+ <Input
76
+ placeholder="AUTO GENERATED"
77
+ class="bg-soft text-xs"
78
+ bind:value
79
+ />
80
+ {:else if discriminatorFieldRelation}
81
+ {@const availableCollections =
82
+ discriminatorFieldRelation.to === "*"
83
+ ? Object.keys(ctx.meta.collections)
84
+ : discriminatorFieldRelation.to.map(
85
+ (item: any) => item.collection,
86
+ )}
87
+ {@const collections = availableCollections.map((key: any) => {
88
+ return {
89
+ label: key,
90
+ value: key,
91
+ };
92
+ })}
93
+ <Combobox
94
+ placeholder="NULL"
95
+ searchPlaceholder="Search Collections"
96
+ options={collections}
97
+ buttonClass="
98
+ bg-soft text-muted-foreground px-0 pl-4 pr-8 font-normal
99
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
100
+ "
101
+ bind:value
102
+ />
103
+ {:else if fieldRelation && entry}
104
+ <ExtensionsComponents
105
+ name="detailView.fields.foreignKey.{fieldRelation.to.collection}"
106
+ utils={getExtensionUtils()}
107
+ parentCollectionName={collectionName}
108
+ collectionName={fieldRelation.to.collection}
109
+ bind:value
110
+ {entry}
111
+ fieldName={field.key}
112
+ {destructive}
113
+ >
114
+ <ForeingKeyInput
115
+ parentCollectionName={collectionName}
116
+ collectionName={fieldRelation.to.collection}
117
+ bind:value
118
+ {entry}
119
+ fieldName={field.key}
120
+ {destructive}
121
+ />
122
+ </ExtensionsComponents>
123
+ {:else if field.type === "string"}
124
+ <!-- if the string has a validator of type enum -->
125
+ {#if field.validators && field.validators.enum}
126
+ <Select.Root
127
+ type="single"
128
+ onValueChange={(newValue) => {
129
+ value = newValue;
130
+ }}
131
+ >
132
+ <Select.Trigger
133
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
134
+ class="
135
+ h-9 w-full bg-soft pr-8
136
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
137
+ "
138
+ >
139
+ {value ? value : "NULL"}
140
+ </Select.Trigger>
141
+ <Select.Content>
142
+ <Select.Group>
143
+ {#each field.validators.enum as option}
144
+ <Select.Item value={option} label={option} />
145
+ {/each}
146
+ </Select.Group>
147
+ </Select.Content>
148
+ </Select.Root>
149
+ {:else}
150
+ <Input
151
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
152
+ type="text"
153
+ class="
154
+ bg-soft text-xs
155
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
156
+ "
157
+ bind:value
158
+ />
159
+ {/if}
160
+ {:else if field.type === "text"}
161
+ <Textarea
162
+ placeholder={ui?.placeholder ? ui.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
163
+ rows={5}
164
+ class="
165
+ bg-soft text-xs
166
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
167
+ "
168
+ bind:value
169
+ />
170
+ {:else if field.type === "date"}
171
+ <Input
172
+ type="date"
173
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
174
+ class="
175
+ dateInput block w-full bg-soft pr-9 text-xs
176
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
177
+ "
178
+ bind:value={
179
+ () => {
180
+ if (!value) {
181
+ return;
182
+ }
183
+ const date = new Date(value);
184
+ return date.toISOString().split("T")[0];
185
+ },
186
+ (v) => (value = v)
187
+ }
188
+ />
189
+ {:else if field.type === "time"}
190
+ <Input
191
+ type="time"
192
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
193
+ class="
194
+ dateInput block w-full bg-soft pr-9 text-xs
195
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
196
+ "
197
+ bind:value={
198
+ () => {
199
+ if (!value) {
200
+ return;
201
+ }
202
+ return value;
203
+ },
204
+ (v) => (value = v)
205
+ }
206
+ />
207
+ {:else if field.type === "datetime"}
208
+ <Input
209
+ type="datetime-local"
210
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
211
+ class="
212
+ dateInput block w-full bg-soft pr-9 text-xs
213
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
214
+ "
215
+ bind:value={
216
+ () => {
217
+ if (!value) {
218
+ return;
219
+ }
220
+ const date = new Date(value);
221
+ return date.toISOString().slice(0, 16);
222
+ },
223
+ (v) => {
224
+ value = v;
225
+ }
226
+ }
227
+ />
228
+ {:else if field.type === "bool"}
229
+ <Select.Root type="single" bind:value>
230
+ <Select.Trigger
231
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
232
+ class="
233
+ bg-soft pr-9
234
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
235
+ "
236
+ >
237
+ {String(value).toUpperCase()}
238
+ </Select.Trigger>
239
+ <Select.Content>
240
+ <Select.Item value={"true"} class="flex gap-1.5">
241
+ <Check size="15" />
242
+ TRUE
243
+ </Select.Item>
244
+ <Select.Item value={"false"} class="flex gap-1.5">
245
+ <X size="15" />
246
+ FALSE
247
+ </Select.Item>
248
+ <Select.Item value={"null"} class="flex gap-1.5">
249
+ <Ban size="15" />
250
+ NULL
251
+ </Select.Item>
252
+ </Select.Content>
253
+ </Select.Root>
254
+ {:else if field.type === "decimal"}
255
+ <Input
256
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
257
+ type="number"
258
+ step="any"
259
+ class="
260
+ bg-soft text-xs
261
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
262
+ "
263
+ bind:value
264
+ />
265
+ {:else}
266
+ <Input
267
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
268
+ type="number"
269
+ class="
270
+ bg-soft text-xs
271
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
272
+ "
273
+ bind:value
274
+ />
275
+ {/if}
276
+ {#if errorMessages}
277
+ {#each errorMessages as message}
278
+ <div class="flex gap-1 text-destructive">
279
+ <CircleAlert size="15" class="translate-y-[0.025rem]" />
280
+ <div class="text-[0.7rem]">
281
+ {message}
282
+ </div>
283
+ </div>
284
+ {/each}
285
+ {/if}
286
+ </div>
287
+ </div>
@@ -0,0 +1,199 @@
1
+ <script lang="ts">
2
+ import { Ban, Check, CircleAlert, X } from "lucide-svelte";
3
+ import Button from "../ui/button/button.svelte";
4
+ import Input from "../ui/input/input.svelte";
5
+ import * as Select from "$lib/components/ui/select/index";
6
+ import Textarea from "../ui/textarea/textarea.svelte";
7
+ import type { EntryField } from "./detailViewForm.svelte";
8
+ import type { Snippet } from "svelte";
9
+ import AlertView from "../alertView.svelte";
10
+
11
+ export type CustomFieldSnippet = Snippet<[EntryField, unknown]>;
12
+
13
+ interface Props {
14
+ field: EntryField;
15
+ value: unknown;
16
+ customFieldInput?: CustomFieldSnippet;
17
+ }
18
+
19
+ let {
20
+ field,
21
+ value = $bindable(),
22
+ customFieldInput,
23
+ }: Props = $props();
24
+
25
+ const disabledClasses = "pointer-events-none opacity-50";
26
+ const destructive: boolean = $derived(Boolean(field.errorMessages && field.errorMessages.length));
27
+ </script>
28
+
29
+ <div class="{field.disabled ? "cursor-not-allowed" : ''}">
30
+ <div
31
+ class="
32
+ relative flex flex-col gap-2
33
+ {field.disabled ? disabledClasses : ''}
34
+ "
35
+ style="flex: 2;"
36
+ >
37
+ <Button
38
+ onclick={() => (value = null)}
39
+ variant="outline"
40
+ class="absolute right-0 top-0 z-10 mr-1.5 mt-1.5 aspect-square h-6 w-6 p-0"
41
+ Icon={Ban}
42
+ tabindex={-1}
43
+ ></Button>
44
+ {#if customFieldInput}
45
+ {@render customFieldInput(field, value)}
46
+ {:else if field.type === "input"}
47
+ <!-- if the string has a validator of type enum -->
48
+ <Input
49
+ placeholder={field.placeholder ? field.placeholder : "NULL"}
50
+ type="text"
51
+ class="
52
+ bg-soft text-xs
53
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
54
+ "
55
+ bind:value
56
+ />
57
+ {:else if field.type === "select"}
58
+ <Select.Root
59
+ type="single"
60
+ onValueChange={(newValue) => {
61
+ value = newValue;
62
+ }}
63
+ >
64
+ <Select.Trigger
65
+ placeholder={field.placeholder ? field.placeholder : "NULL"}
66
+ class="
67
+ h-9 w-full bg-soft pr-8
68
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
69
+ "
70
+ >
71
+ {value ? value : "NULL"}
72
+ </Select.Trigger>
73
+ <Select.Content>
74
+ <Select.Group>
75
+ {#each field.options as option}
76
+ <Select.Item value={option} label={option} />
77
+ {/each}
78
+ </Select.Group>
79
+ </Select.Content>
80
+ </Select.Root>
81
+ {:else if field.type === "textarea"}
82
+ {#if typeof value === 'string' || typeof value === 'undefined' || value === null}
83
+ <Textarea
84
+ placeholder={field.placeholder ? field.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
85
+ rows={5}
86
+ class="
87
+ bg-soft text-xs
88
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
89
+ "
90
+ bind:value
91
+ />
92
+ {:else}
93
+ <AlertView title="Couldnt render component">
94
+ The value passed to the Textarea in (fieldInputReplacement) is something different from these (string, undefined, null)
95
+ </AlertView>
96
+ {/if}
97
+ {:else if field.type === "date"}
98
+ <Input
99
+ type="date"
100
+ class="
101
+ dateInput block w-full bg-soft pr-9 text-xs
102
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
103
+ "
104
+ bind:value={
105
+ () => {
106
+ if (typeof value !== "string") {
107
+ return;
108
+ }
109
+ const date = new Date(value);
110
+ return date.toISOString().split("T")[0];
111
+ },
112
+ (v) => (value = v)
113
+ }
114
+ />
115
+ {:else if field.type === "time"}
116
+ <Input
117
+ type="time"
118
+ class="
119
+ dateInput block w-full bg-soft pr-9 text-xs
120
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
121
+ "
122
+ bind:value={
123
+ () => {
124
+ if (typeof value !== "string") {
125
+ return;
126
+ }
127
+ return value;
128
+ },
129
+ (v) => (value = v)
130
+ }
131
+ />
132
+ {:else if field.type === "datetime"}
133
+ <Input
134
+ type="datetime-local"
135
+ class="
136
+ dateInput block w-full bg-soft pr-9 text-xs
137
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
138
+ "
139
+ bind:value={
140
+ () => {
141
+ if (typeof value !== "string") {
142
+ return;
143
+ }
144
+ const date = new Date(value);
145
+ return date.toISOString().slice(0, 16);
146
+ },
147
+ (v) => {
148
+ value = v;
149
+ }
150
+ }
151
+ />
152
+ {:else if field.type === "switch"}
153
+ {#if typeof value === "string" || typeof value === "undefined"}
154
+ <Select.Root type="single" bind:value>
155
+ <Select.Trigger
156
+ class="
157
+ bg-soft pr-9
158
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
159
+ "
160
+ >
161
+ {String(value).toUpperCase()}
162
+ </Select.Trigger>
163
+ <Select.Content>
164
+ <Select.Item value={"true"} class="flex gap-1.5">
165
+ <Check size="15" />
166
+ TRUE
167
+ </Select.Item>
168
+ <Select.Item value={"false"} class="flex gap-1.5">
169
+ <X size="15" />
170
+ FALSE
171
+ </Select.Item>
172
+ <Select.Item value={"null"} class="flex gap-1.5">
173
+ <Ban size="15" />
174
+ NULL
175
+ </Select.Item>
176
+ </Select.Content>
177
+ </Select.Root>
178
+ {:else}
179
+ <AlertView title="Couldnt render component">
180
+ The value passed to the switch element in (fieldInputReplacement) is not one of these (string, undefined)
181
+ </AlertView>
182
+ {/if}
183
+ {:else}
184
+ <AlertView title="Couldnt render component">
185
+ For some reason couldnt render the (fieldInputReplacement)
186
+ </AlertView>
187
+ {/if}
188
+ {#if field.errorMessages}
189
+ {#each field.errorMessages as message}
190
+ <div class="flex gap-1 text-destructive">
191
+ <CircleAlert size="15" class="translate-y-[0.025rem]" />
192
+ <div class="text-[0.7rem]">
193
+ {message}
194
+ </div>
195
+ </div>
196
+ {/each}
197
+ {/if}
198
+ </div>
199
+ </div>
@@ -0,0 +1,61 @@
1
+ import type { CreateDetailViewProp } from "./create/createDetailView.svelte";
2
+ import type { UpdateDetailViewProp } from "./update/updateDetailView.svelte";
3
+ import CreateDetailView from "./create/createDetailView.svelte";
4
+ import UpdateDetailView from "./update/updateDetailView.svelte";
5
+ import { lobb } from "$lib";
6
+ import { getCollectionParamsFields } from "../dataTable/utils";
7
+ import { mount, unmount } from "svelte";
8
+
9
+ export function openCreateDetailView(props: CreateDetailViewProp) {
10
+ const targetElement = document.querySelector('main');
11
+
12
+ if (!targetElement) {
13
+ throw new Error("main html element doesn't exist for some reason")
14
+ }
15
+
16
+ const mountedCreateDetailView = mount(CreateDetailView, {
17
+ target: targetElement,
18
+ props: {
19
+ ...props,
20
+ onCancel: async () => {
21
+ props.onCancel?.()
22
+ await unmount(mountedCreateDetailView, {
23
+ outro: true
24
+ });
25
+ }
26
+ },
27
+ })
28
+ }
29
+
30
+ export async function openUpdateDetailView(props: UpdateDetailViewProp) {
31
+ let params = {
32
+ fields: getCollectionParamsFields(props.collectionName, true),
33
+ filter: {
34
+ id: props.recordId,
35
+ },
36
+ limit: 1,
37
+ };
38
+ const response = await lobb.findAll(props.collectionName, params);
39
+ const result = await response.json();
40
+ const entry = result.data[0];
41
+
42
+ const targetElement = document.querySelector('main');
43
+
44
+ if (!targetElement) {
45
+ throw new Error("main html element doesn't exist for some reason")
46
+ }
47
+
48
+ const mountedUpdateDetailView = mount(UpdateDetailView, {
49
+ target: targetElement,
50
+ props: {
51
+ ...props,
52
+ onCancel: async () => {
53
+ props.onCancel?.()
54
+ await unmount(mountedUpdateDetailView, {
55
+ outro: true
56
+ });
57
+ },
58
+ values: entry,
59
+ },
60
+ })
61
+ }