@lobb-js/studio 0.48.0 → 0.49.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 (63) hide show
  1. package/dist/components/detailView/detailView.svelte +12 -62
  2. package/dist/components/detailView/detailView.svelte.d.ts +1 -1
  3. package/dist/components/detailView/fieldInput.svelte +161 -288
  4. package/dist/components/detailView/fieldInput.svelte.d.ts +1 -1
  5. package/dist/components/detailView/fields/BoolField.svelte +42 -0
  6. package/dist/components/detailView/fields/BoolField.svelte.d.ts +13 -0
  7. package/dist/components/detailView/fields/CodeField.svelte +30 -0
  8. package/dist/components/detailView/fields/CodeField.svelte.d.ts +13 -0
  9. package/dist/components/detailView/fields/CustomInputField.svelte +50 -0
  10. package/dist/components/detailView/fields/CustomInputField.svelte.d.ts +18 -0
  11. package/dist/components/detailView/fields/DateField.svelte +47 -0
  12. package/dist/components/detailView/fields/DateField.svelte.d.ts +14 -0
  13. package/dist/components/detailView/fields/EmbeddedField.svelte +139 -0
  14. package/dist/components/detailView/fields/EmbeddedField.svelte.d.ts +13 -0
  15. package/dist/components/detailView/fields/EmbeddedPolymorphicField.svelte +197 -0
  16. package/dist/components/detailView/fields/EmbeddedPolymorphicField.svelte.d.ts +13 -0
  17. package/dist/components/detailView/fields/EnumField.svelte +70 -0
  18. package/dist/components/detailView/fields/EnumField.svelte.d.ts +17 -0
  19. package/dist/components/detailView/fields/FieldWrapper.svelte +68 -0
  20. package/dist/components/detailView/fields/FieldWrapper.svelte.d.ts +18 -0
  21. package/dist/components/detailView/fields/ForeignKeyField.svelte +78 -0
  22. package/dist/components/detailView/fields/ForeignKeyField.svelte.d.ts +17 -0
  23. package/dist/components/detailView/fields/IdField.svelte +21 -0
  24. package/dist/components/detailView/fields/IdField.svelte.d.ts +12 -0
  25. package/dist/components/detailView/fields/NumberField.svelte +38 -0
  26. package/dist/components/detailView/fields/NumberField.svelte.d.ts +16 -0
  27. package/dist/components/detailView/fields/PasswordField.svelte +29 -0
  28. package/dist/components/detailView/fields/PasswordField.svelte.d.ts +12 -0
  29. package/dist/components/detailView/fields/PolymorphicField.svelte +51 -0
  30. package/dist/components/detailView/fields/PolymorphicField.svelte.d.ts +16 -0
  31. package/dist/components/detailView/fields/RichTextField.svelte +30 -0
  32. package/dist/components/detailView/fields/RichTextField.svelte.d.ts +13 -0
  33. package/dist/components/detailView/fields/StringField.svelte +35 -0
  34. package/dist/components/detailView/fields/StringField.svelte.d.ts +14 -0
  35. package/dist/components/detailView/fields/TextField.svelte +35 -0
  36. package/dist/components/detailView/fields/TextField.svelte.d.ts +14 -0
  37. package/dist/components/foreingKeyInput.svelte +1 -1
  38. package/dist/components/polymorphicInput.svelte +1 -1
  39. package/dist/extensions/extension.types.d.ts +3 -1
  40. package/dist/extensions/extensionUtils.js +2 -0
  41. package/package.json +7 -6
  42. package/src/lib/components/detailView/detailView.svelte +12 -62
  43. package/src/lib/components/detailView/fieldInput.svelte +161 -288
  44. package/src/lib/components/detailView/fields/BoolField.svelte +42 -0
  45. package/src/lib/components/detailView/fields/CodeField.svelte +30 -0
  46. package/src/lib/components/detailView/fields/CustomInputField.svelte +50 -0
  47. package/src/lib/components/detailView/fields/DateField.svelte +47 -0
  48. package/src/lib/components/detailView/fields/EmbeddedField.svelte +139 -0
  49. package/src/lib/components/detailView/fields/EmbeddedPolymorphicField.svelte +197 -0
  50. package/src/lib/components/detailView/fields/EnumField.svelte +70 -0
  51. package/src/lib/components/detailView/fields/FieldWrapper.svelte +68 -0
  52. package/src/lib/components/detailView/fields/ForeignKeyField.svelte +78 -0
  53. package/src/lib/components/detailView/fields/IdField.svelte +21 -0
  54. package/src/lib/components/detailView/fields/NumberField.svelte +38 -0
  55. package/src/lib/components/detailView/fields/PasswordField.svelte +29 -0
  56. package/src/lib/components/detailView/fields/PolymorphicField.svelte +51 -0
  57. package/src/lib/components/detailView/fields/RichTextField.svelte +30 -0
  58. package/src/lib/components/detailView/fields/StringField.svelte +35 -0
  59. package/src/lib/components/detailView/fields/TextField.svelte +35 -0
  60. package/src/lib/components/foreingKeyInput.svelte +1 -1
  61. package/src/lib/components/polymorphicInput.svelte +1 -1
  62. package/src/lib/extensions/extension.types.ts +2 -1
  63. package/src/lib/extensions/extensionUtils.ts +2 -0
@@ -1,17 +1,11 @@
1
1
  <script lang="ts">
2
- import { CircleHelp } from "lucide-svelte";
3
- import * as Tooltip from "../ui/tooltip";
4
2
  import { getStudioContext } from "../../context";
5
- import ExtensionsComponents from "../extensionsComponents.svelte";
6
- import { getExtensionUtils } from "../../extensions/extensionUtils";
7
- import { getField, getFieldIcon } from "../dataTable/utils";
8
3
  import FieldInput from "./fieldInput.svelte";
9
- import { getFieldTypeLabel } from "../../utils";
10
4
 
11
5
  interface Props {
12
6
  collectionName: string;
13
7
  entry: Record<string, any>;
14
- fieldsErrors?: Record<string, string[]>;
8
+ fieldsErrors?: Record<string, any>;
15
9
  changedFields?: string[];
16
10
  }
17
11
 
@@ -22,9 +16,7 @@
22
16
  changedFields = [],
23
17
  }: Props = $props();
24
18
 
25
- const { lobb, ctx } = getStudioContext();
26
- // Singleton collections only ever have one row, and `id` is auto-
27
- // generated for that row — there's no value to showing it in the form.
19
+ const { ctx } = getStudioContext();
28
20
  const fieldNames = $derived(
29
21
  Object.keys(ctx.meta.collections[collectionName].fields).filter(
30
22
  (fieldName) =>
@@ -35,58 +27,16 @@
35
27
 
36
28
  <div class="grid grid-cols-2 gap-4 p-4">
37
29
  {#each fieldNames as fieldName}
38
- {#if !ctx.meta.collections[collectionName].fields[fieldName]?.ui?.hidden}
39
- {@const field = getField(ctx, fieldName, collectionName)}
40
- {@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
41
- {@const description = ctx.meta.collections[collectionName].fields[fieldName]?.description}
42
- {@const fieldDef = ctx.meta.collections[collectionName].fields[fieldName]}
43
- {@const isFullWidth = field.type === "text" || field.type === "polymorphic" || fieldDef?.ui?.input?.type === "richtext" || fieldDef?.ui?.span === 2}
44
- <div class="flex flex-col gap-2 {isFullWidth ? 'col-span-2' : 'col-span-1'}">
45
- <div class="flex flex-1 items-end justify-between gap-2 text-xs">
46
- <div class="flex items-center gap-1.5">
47
- <ExtensionsComponents
48
- name="detailView.field.label"
49
- utils={getExtensionUtils(lobb, ctx)}
50
- {collectionName}
51
- {fieldName}
52
- bind:value={entry[fieldName]}
53
- >
54
- <div class="flex items-center gap-1.5">
55
- <div class="h-fit">{field.label}</div>
56
- <div class="flex h-fit items-center gap-1 text-[0.7rem] text-muted-foreground">
57
- <FieldIcon size="12" />
58
- {getFieldTypeLabel(field.type)}
59
- </div>
60
- </div>
61
- </ExtensionsComponents>
62
- {#if description}
63
- <Tooltip.Root>
64
- <Tooltip.Trigger>
65
- <CircleHelp size="12" class="text-muted-foreground" />
66
- </Tooltip.Trigger>
67
- <Tooltip.Content class="max-w-64 text-xs">
68
- {description}
69
- </Tooltip.Content>
70
- </Tooltip.Root>
71
- {/if}
72
- </div>
73
- <div>
74
- <ExtensionsComponents
75
- name="dvFields.topRight.{collectionName}.{fieldName}"
76
- utils={getExtensionUtils(lobb, ctx)}
77
- bind:value={entry[fieldName]}
78
- />
79
- </div>
80
- </div>
81
- <FieldInput
82
- {collectionName}
83
- {fieldName}
84
- bind:value={entry[fieldName]}
85
- bind:entry
86
- errorMessages={fieldsErrors[fieldName]}
87
- changed={changedFields.includes(fieldName)}
88
- />
89
- </div>
30
+ {@const fieldDef = ctx.meta.collections[collectionName].fields[fieldName]}
31
+ {#if !(fieldDef?.ui?.hidden) || (fieldDef as any)?.embedded}
32
+ <FieldInput
33
+ {collectionName}
34
+ {fieldName}
35
+ bind:value={entry[fieldName]}
36
+ bind:entry
37
+ errorMessages={fieldsErrors[fieldName]}
38
+ changed={changedFields.includes(fieldName)}
39
+ />
90
40
  {/if}
91
41
  {/each}
92
42
  </div>
@@ -1,296 +1,169 @@
1
1
  <script lang="ts">
2
- import { getStudioContext } from "../../context";
3
- import { getFieldRelationTarget, getPolymorphicRelation } from "../../relations";
4
- import { Ban, Check, CircleAlert, X } from "lucide-svelte";
5
- import { getField } from "../dataTable/utils";
6
- import Button from "../ui/button/button.svelte";
7
- import FieldCustomInput from "./fieldCustomInput.svelte";
8
- import Input from "../ui/input/input.svelte";
9
- import NumberInput from "../ui/input/numberInput.svelte";
10
- import * as Select from "../ui/select/index";
11
- import EnumBadge from "../dataTable/enumBadge.svelte";
12
- import type { EnumOption } from "@lobb-js/core";
13
- import Textarea from "../ui/textarea/textarea.svelte";
14
- import ForeingKeyInput from "../foreingKeyInput.svelte";
15
- import PolymorphicInput from "../polymorphicInput.svelte";
16
- import ExtensionsComponents from "../extensionsComponents.svelte";
17
- import { getExtensionUtils } from "../../extensions/extensionUtils";
2
+ import { getStudioContext } from "../../context";
3
+ import { getFieldRelationTarget, getPolymorphicRelation } from "../../relations";
4
+ import { Ban } from "lucide-svelte";
5
+ import { getField, getFieldIcon } from "../dataTable/utils";
6
+ import { getFieldTypeLabel } from "../../utils";
7
+ import Button from "../ui/button/button.svelte";
8
+ import type { EnumOption } from "@lobb-js/core";
18
9
 
19
- const { ctx, lobb } = getStudioContext();
10
+ import ForeignKeyField from "./fields/ForeignKeyField.svelte";
11
+ import PolymorphicField from "./fields/PolymorphicField.svelte";
12
+ import EmbeddedField from "./fields/EmbeddedField.svelte";
13
+ import EmbeddedPolymorphicField from "./fields/EmbeddedPolymorphicField.svelte";
14
+ import RichTextField from "./fields/RichTextField.svelte";
15
+ import CodeField from "./fields/CodeField.svelte";
16
+ import PasswordField from "./fields/PasswordField.svelte";
17
+ import IdField from "./fields/IdField.svelte";
18
+ import StringField from "./fields/StringField.svelte";
19
+ import TextField from "./fields/TextField.svelte";
20
+ import NumberField from "./fields/NumberField.svelte";
21
+ import DateField from "./fields/DateField.svelte";
22
+ import BoolField from "./fields/BoolField.svelte";
23
+ import EnumField from "./fields/EnumField.svelte";
20
24
 
21
- interface Props {
22
- collectionName: string;
23
- fieldName: string;
24
- value: any;
25
- errorMessages?: string[];
26
- entry?: Record<string, any>;
27
- changed?: boolean;
28
- }
25
+ const { ctx } = getStudioContext();
29
26
 
30
- let {
31
- collectionName,
32
- fieldName,
33
- value = $bindable(),
34
- errorMessages = [],
35
- entry = $bindable(),
36
- changed = false,
37
- }: Props = $props();
27
+ interface Props {
28
+ collectionName: string;
29
+ fieldName: string;
30
+ value: any;
31
+ errorMessages?: any;
32
+ entry?: Record<string, any>;
33
+ changed?: boolean;
34
+ }
38
35
 
39
- const ui_input =
40
- ctx.meta.collections[collectionName].fields[fieldName].ui?.input;
41
- const ui =
42
- ctx.meta.collections[collectionName].fields[fieldName].ui;
43
- const field = getField(ctx, fieldName, collectionName);
44
- const fieldRelationTarget = getFieldRelationTarget(ctx, collectionName, fieldName);
45
- const polymorphicRelation = getPolymorphicRelation(ctx, collectionName, fieldName);
46
- const isDisabled = field.key === 'id' || Boolean(ui?.disabled)
47
- const disabledClasses = "pointer-events-none opacity-50";
48
- const destructive: boolean = $derived(!isDisabled && Boolean(errorMessages.length));
49
- const changedClass = $derived(changed && !destructive ? '!bg-orange-500/5' : '');
36
+ let {
37
+ collectionName,
38
+ fieldName,
39
+ value = $bindable(),
40
+ errorMessages = [],
41
+ entry = $bindable(),
42
+ changed = false,
43
+ }: Props = $props();
50
44
 
45
+ const fieldDef = ctx.meta.collections[collectionName].fields[fieldName];
46
+ const ui_input = fieldDef?.ui?.input;
47
+ const ui = fieldDef?.ui;
48
+ const field = getField(ctx, fieldName, collectionName);
49
+ const fieldRelationTarget = getFieldRelationTarget(ctx, collectionName, fieldName);
50
+ const polymorphicRelation = getPolymorphicRelation(ctx, collectionName, fieldName);
51
+ const isDisabled = field.key === 'id' || Boolean(ui?.disabled);
52
+ const destructive: boolean = $derived(Array.isArray(errorMessages) && errorMessages.length > 0);
53
+ const changedClass = $derived(changed && !destructive ? '!bg-orange-500/5' : '');
54
+ const isEmbedded = !!(fieldRelationTarget && entry && (fieldDef as any)?.embedded);
55
+ const isEmbeddedPolymorphic = !!(polymorphicRelation && entry && (fieldDef as any)?.embedded);
56
+
57
+ const FieldIcon = getFieldIcon(ctx, fieldName, collectionName);
58
+ const labelData = {
59
+ text: field.label,
60
+ icon: FieldIcon,
61
+ typeText: getFieldTypeLabel(field.type),
62
+ description: fieldDef?.description as string | undefined,
63
+ };
64
+
65
+ function handleClear(e: MouseEvent) {
66
+ e.stopPropagation();
67
+ value = null;
68
+ if (polymorphicRelation && entry) {
69
+ entry[polymorphicRelation.from.collection_field] = null;
70
+ entry[polymorphicRelation.from.id_field] = null;
71
+ }
72
+ }
51
73
  </script>
52
74
 
53
- <div class="{isDisabled ? "cursor-not-allowed" : ''}">
54
- <div
55
- class="
56
- relative flex flex-col gap-2
57
- {isDisabled ? disabledClasses : ''}
58
- "
59
- style="flex: 2;"
60
- >
61
- <Button
62
- onclick={() => {
63
- value = null;
64
- if (polymorphicRelation && entry) {
65
- entry[polymorphicRelation.from.collection_field] = null;
66
- entry[polymorphicRelation.from.id_field] = null;
67
- }
68
- }}
69
- variant="outline"
70
- class="absolute right-0 top-0 z-10 mr-1.5 mt-1.5 aspect-square h-6 w-6 p-0"
71
- Icon={Ban}
72
- tabindex={-1}
73
- ></Button>
74
- {#if polymorphicRelation && entry}
75
- <PolymorphicInput
76
- collectionField={polymorphicRelation.from.collection_field}
77
- idField={polymorphicRelation.from.id_field}
78
- virtualField={polymorphicRelation.from.virtual_field ?? ''}
79
- targetCollections={polymorphicRelation.to}
80
- bind:entry
81
- {destructive}
82
- />
83
- {:else if ui_input}
84
- <FieldCustomInput
85
- bind:value
86
- type={ui_input.type}
87
- args={ui_input.args}
88
- {field}
89
- {destructive}
90
- />
91
- {:else if field.label === "id"}
92
- <Input
93
- placeholder="AUTO GENERATED"
94
- class="bg-muted text-xs {changedClass}"
95
- bind:value
96
- />
97
- {:else if fieldRelationTarget && entry}
98
- <ExtensionsComponents
99
- name="detailView.fields.foreignKey.{fieldRelationTarget}"
100
- utils={getExtensionUtils(lobb, ctx)}
101
- parentCollectionName={collectionName}
102
- collectionName={fieldRelationTarget}
103
- bind:value
104
- {entry}
105
- fieldName={field.key}
106
- {destructive}
107
- >
108
- <ForeingKeyInput
109
- parentCollectionName={collectionName}
110
- collectionName={fieldRelationTarget}
111
- bind:value
112
- {entry}
113
- fieldName={field.key}
114
- {destructive}
115
- />
116
- </ExtensionsComponents>
117
- {:else if field.enum}
118
- {@const rawEnum = field.enum as (string | number | EnumOption)[]}
119
- {@const isEnumOption = rawEnum.length > 0 && typeof rawEnum[0] === "object"}
120
- {@const enumOptions = isEnumOption ? rawEnum as EnumOption[] : undefined}
121
- <Select.Root
122
- type="single"
123
- bind:value={
124
- () => value != null ? String(value) : null,
125
- (v) => {
126
- if (v == null) { value = null; return; }
127
- const isNumeric = field.type === "integer" || field.type === "long" || field.type === "decimal" || field.type === "float";
128
- value = isNumeric ? Number(v) : v;
129
- }
130
- }
131
- >
132
- <Select.Trigger
133
- class="h-9 w-full bg-muted pr-8 {changedClass} {destructive ? 'border-destructive !bg-destructive/10' : ''}"
134
- >
135
- {#if value != null && enumOptions}
136
- <EnumBadge value={String(value)} enum={enumOptions} />
137
- {:else if value != null}
138
- {value}
139
- {:else}
140
- <span class="text-muted-foreground">{ui?.placeholder ?? "NULL"}</span>
141
- {/if}
142
- </Select.Trigger>
143
- <Select.Content>
144
- <Select.Group>
145
- {#each rawEnum as option}
146
- {@const optionValue = typeof option === "object" ? String(option.value) : String(option)}
147
- <Select.Item value={optionValue} label={optionValue}>
148
- {#if enumOptions}
149
- <EnumBadge value={optionValue} enum={enumOptions} />
150
- {:else}
151
- {optionValue}
152
- {/if}
153
- </Select.Item>
154
- {/each}
155
- </Select.Group>
156
- </Select.Content>
157
- </Select.Root>
158
- {:else if field.type === "string"}
159
- <Input
160
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
161
- type="text"
162
- class="
163
- bg-muted text-xs {changedClass}
164
- {destructive ? 'border-destructive bg-destructive/10' : ''}
165
- "
166
- bind:value
167
- />
168
- {:else if field.type === "text"}
169
- <Textarea
170
- placeholder={ui?.placeholder ? ui.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
171
- rows={5}
172
- class="
173
- bg-muted text-xs {changedClass}
174
- {destructive ? 'border-destructive bg-destructive/10' : ''}
175
- "
176
- bind:value
177
- />
178
- {:else if field.type === "date"}
179
- <Input
180
- type="date"
181
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
182
- class="
183
- dateInput block w-full bg-muted pr-9 text-xs
184
- {destructive ? 'border-destructive bg-destructive/10' : ''}
185
- "
186
- bind:value={
187
- () => {
188
- if (!value) {
189
- return;
190
- }
191
- const date = new Date(value);
192
- return date.toISOString().split("T")[0];
193
- },
194
- (v) => (value = v)
195
- }
196
- />
197
- {:else if field.type === "time"}
198
- <Input
199
- type="time"
200
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
201
- class="
202
- dateInput block w-full bg-muted pr-9 text-xs
203
- {destructive ? 'border-destructive bg-destructive/10' : ''}
204
- "
205
- bind:value={
206
- () => {
207
- if (!value) {
208
- return;
209
- }
210
- return value;
211
- },
212
- (v) => (value = v)
213
- }
214
- />
215
- {:else if field.type === "datetime"}
216
- <Input
217
- type="datetime-local"
218
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
219
- class="
220
- dateInput block w-full bg-muted pr-9 text-xs
221
- {destructive ? 'border-destructive bg-destructive/10' : ''}
222
- "
223
- bind:value={
224
- () => {
225
- if (!value) {
226
- return;
227
- }
228
- const date = new Date(value);
229
- return date.toISOString().slice(0, 16);
230
- },
231
- (v) => {
232
- value = v;
233
- }
234
- }
235
- />
236
- {:else if field.type === "bool"}
237
- <Select.Root type="single" bind:value>
238
- <Select.Trigger
239
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
240
- class="
241
- bg-muted pr-9
242
- {destructive ? 'border-destructive bg-destructive/10' : ''}
243
- "
244
- >
245
- {String(value).toUpperCase()}
246
- </Select.Trigger>
247
- <Select.Content>
248
- <Select.Item value={"true"} class="flex gap-1.5">
249
- <Check size="15" />
250
- TRUE
251
- </Select.Item>
252
- <Select.Item value={"false"} class="flex gap-1.5">
253
- <X size="15" />
254
- FALSE
255
- </Select.Item>
256
- <Select.Item value={"null"} class="flex gap-1.5">
257
- <Ban size="15" />
258
- NULL
259
- </Select.Item>
260
- </Select.Content>
261
- </Select.Root>
262
- {:else if field.type === "decimal" || field.type === "float" || field.type === "integer" || field.type === "long"}
263
- {@const isFloat = field.type === "decimal" || field.type === "float"}
264
- <NumberInput
265
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
266
- scale={isFloat ? 20 : 0}
267
- groupDigits={ui?.groupDigits ?? false}
268
- class="
269
- bg-muted text-xs {changedClass}
270
- {destructive ? 'border-destructive bg-destructive/10' : ''}
271
- "
272
- bind:value
273
- />
274
- {:else}
275
- <Input
276
- placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
277
- type="text"
278
- class="
279
- bg-muted text-xs {changedClass}
280
- {destructive ? 'border-destructive bg-destructive/10' : ''}
281
- "
282
- bind:value
283
- />
284
- {/if}
285
- {#if !isDisabled && errorMessages}
286
- {#each errorMessages as message}
287
- <div class="flex gap-1 text-destructive">
288
- <CircleAlert size="15" class="translate-y-[0.025rem]" />
289
- <div class="text-[0.7rem]">
290
- {message}
291
- </div>
292
- </div>
293
- {/each}
294
- {/if}
295
- </div>
296
- </div>
75
+ {#snippet clearBtn()}
76
+ {#if !isDisabled}
77
+ <Button
78
+ onclick={handleClear}
79
+ variant="outline"
80
+ class="aspect-square h-6 w-6 shrink-0 p-0"
81
+ Icon={Ban}
82
+ tabindex={-1}
83
+ />
84
+ {/if}
85
+ {/snippet}
86
+
87
+ {#if isEmbeddedPolymorphic && entry}
88
+ <EmbeddedPolymorphicField
89
+ virtualField={polymorphicRelation!.from.virtual_field ?? fieldName}
90
+ collectionField={polymorphicRelation!.from.collection_field}
91
+ idField={polymorphicRelation!.from.id_field}
92
+ targetCollections={polymorphicRelation!.to}
93
+ bind:entry
94
+ {isDisabled}
95
+ {errorMessages}
96
+ header={labelData}
97
+ />
98
+ {:else if polymorphicRelation && entry}
99
+ <PolymorphicField
100
+ collectionField={polymorphicRelation.from.collection_field}
101
+ idField={polymorphicRelation.from.id_field}
102
+ virtualField={polymorphicRelation.from.virtual_field ?? ''}
103
+ targetCollections={polymorphicRelation.to}
104
+ bind:entry
105
+ {destructive}
106
+ {isDisabled}
107
+ {errorMessages}
108
+ header={labelData}
109
+ {clearBtn}
110
+ />
111
+ {:else if isEmbedded}
112
+ <EmbeddedField
113
+ collectionName={fieldRelationTarget!}
114
+ {fieldName}
115
+ bind:value
116
+ entry={entry!}
117
+ {isDisabled}
118
+ {errorMessages}
119
+ header={labelData}
120
+ onClear={handleClear}
121
+ />
122
+ {:else if ui_input?.type === 'richtext'}
123
+ <RichTextField bind:value {field} {destructive} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
124
+ {:else if ui_input?.type === 'code'}
125
+ <CodeField bind:value {field} args={ui_input.args} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
126
+ {:else if ui_input?.type === 'password'}
127
+ <PasswordField bind:value {destructive} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
128
+ {:else if fieldRelationTarget && entry}
129
+ <ForeignKeyField
130
+ parentCollectionName={collectionName}
131
+ collectionName={fieldRelationTarget}
132
+ {fieldName}
133
+ bind:value
134
+ bind:entry
135
+ {destructive}
136
+ {isDisabled}
137
+ {errorMessages}
138
+ header={labelData}
139
+ {clearBtn}
140
+ />
141
+ {:else if field.label === "id"}
142
+ <IdField bind:value {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
143
+ {:else if field.enum}
144
+ <EnumField
145
+ bind:value
146
+ rawEnum={field.enum as (string | number | EnumOption)[]}
147
+ isNumeric={field.type === "integer" || field.type === "long" || field.type === "decimal" || field.type === "float"}
148
+ placeholder={ui?.placeholder}
149
+ {destructive} {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn}
150
+ />
151
+ {:else if field.type === "string"}
152
+ <StringField bind:value placeholder={ui?.placeholder} {destructive} {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
153
+ {:else if field.type === "text"}
154
+ <TextField bind:value placeholder={ui?.placeholder ?? (value === "" ? "EMPTY STRING" : "NULL")} {destructive} {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
155
+ {:else if field.type === "date" || field.type === "time" || field.type === "datetime"}
156
+ <DateField bind:value type={field.type} placeholder={ui?.placeholder} {destructive} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
157
+ {:else if field.type === "bool"}
158
+ <BoolField bind:value placeholder={ui?.placeholder} {destructive} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
159
+ {:else if field.type === "decimal" || field.type === "float" || field.type === "integer" || field.type === "long"}
160
+ <NumberField
161
+ bind:value
162
+ scale={field.type === "decimal" || field.type === "float" ? 20 : 0}
163
+ groupDigits={ui?.groupDigits ?? false}
164
+ placeholder={ui?.placeholder}
165
+ {destructive} {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn}
166
+ />
167
+ {:else}
168
+ <StringField bind:value placeholder={ui?.placeholder} {destructive} {changedClass} {isDisabled} {errorMessages} header={labelData} {clearBtn} />
169
+ {/if}
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import { Ban, Check, X } from "lucide-svelte";
4
+ import * as Select from "../../ui/select/index";
5
+ import FieldWrapper from "./FieldWrapper.svelte";
6
+
7
+ interface Props {
8
+ value?: any;
9
+ placeholder?: string;
10
+ destructive?: boolean;
11
+ isDisabled?: boolean;
12
+ errorMessages?: any;
13
+ header?: any;
14
+ clearBtn?: Snippet<[]>;
15
+ }
16
+ let { value = $bindable(), placeholder = "NULL", destructive = false, isDisabled = false, errorMessages = [], header, clearBtn }: Props = $props();
17
+ </script>
18
+
19
+ <FieldWrapper span={1} {isDisabled} {errorMessages} {header}>
20
+ {#snippet children()}
21
+ <div class="relative">
22
+ {#if clearBtn}
23
+ <div class="absolute right-1 inset-y-0 z-10 flex items-center">
24
+ {@render clearBtn()}
25
+ </div>
26
+ {/if}
27
+ <Select.Root type="single" bind:value>
28
+ <Select.Trigger
29
+ {placeholder}
30
+ class="bg-muted pr-9 {destructive ? 'border-destructive' : ''}"
31
+ >
32
+ {String(value).toUpperCase()}
33
+ </Select.Trigger>
34
+ <Select.Content>
35
+ <Select.Item value={"true"} class="flex gap-1.5"><Check size="15" />TRUE</Select.Item>
36
+ <Select.Item value={"false"} class="flex gap-1.5"><X size="15" />FALSE</Select.Item>
37
+ <Select.Item value={"null"} class="flex gap-1.5"><Ban size="15" />NULL</Select.Item>
38
+ </Select.Content>
39
+ </Select.Root>
40
+ </div>
41
+ {/snippet}
42
+ </FieldWrapper>
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import CodeEditor from "../../codeEditor.svelte";
4
+ import FieldWrapper from "./FieldWrapper.svelte";
5
+
6
+ interface Props {
7
+ value?: any;
8
+ field: any;
9
+ args?: any;
10
+ isDisabled?: boolean;
11
+ errorMessages?: any;
12
+ header?: any;
13
+ clearBtn?: Snippet<[]>;
14
+ }
15
+
16
+ let { value = $bindable(), field, args, isDisabled = false, errorMessages = [], header, clearBtn }: Props = $props();
17
+ </script>
18
+
19
+ <FieldWrapper span={2} {isDisabled} {errorMessages} {header}>
20
+ {#snippet children()}
21
+ <div class="relative">
22
+ {#if clearBtn}
23
+ <div class="absolute right-1 top-1 z-10">
24
+ {@render clearBtn()}
25
+ </div>
26
+ {/if}
27
+ <CodeEditor name={field.key} type={args?.type ?? 'javascript'} bind:value />
28
+ </div>
29
+ {/snippet}
30
+ </FieldWrapper>