@lobb-js/studio 0.42.0 → 0.43.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 (32) hide show
  1. package/dist/components/confirmationDialog/confirmationDialog.svelte +1 -1
  2. package/dist/components/dataTable/dataTable.svelte +42 -16
  3. package/dist/components/dataTable/listViewChildren.svelte +60 -77
  4. package/dist/components/dataTable/listViewChildren.svelte.d.ts +1 -1
  5. package/dist/components/dataTable/table.svelte +8 -56
  6. package/dist/components/dataTable/table.svelte.d.ts +1 -2
  7. package/dist/components/detailView/changeTreeUtils.d.ts +7 -0
  8. package/dist/components/detailView/changeTreeUtils.js +47 -0
  9. package/dist/components/detailView/create/createDetailView.svelte +17 -13
  10. package/dist/components/detailView/detailView.svelte +7 -2
  11. package/dist/components/detailView/detailView.svelte.d.ts +1 -0
  12. package/dist/components/detailView/fieldInput.svelte +10 -9
  13. package/dist/components/detailView/fieldInput.svelte.d.ts +1 -0
  14. package/dist/components/detailView/update/updateDetailView.svelte +22 -18
  15. package/dist/components/drawer.svelte +15 -2
  16. package/dist/components/foreingKeyInput.svelte +163 -68
  17. package/dist/components/foreingKeyInput.svelte.d.ts +1 -1
  18. package/dist/components/polymorphicInput.svelte +112 -63
  19. package/dist/components/polymorphicInput.svelte.d.ts +1 -0
  20. package/package.json +2 -2
  21. package/src/lib/components/confirmationDialog/confirmationDialog.svelte +1 -1
  22. package/src/lib/components/dataTable/dataTable.svelte +42 -16
  23. package/src/lib/components/dataTable/listViewChildren.svelte +60 -77
  24. package/src/lib/components/dataTable/table.svelte +8 -56
  25. package/src/lib/components/detailView/changeTreeUtils.ts +39 -0
  26. package/src/lib/components/detailView/create/createDetailView.svelte +17 -13
  27. package/src/lib/components/detailView/detailView.svelte +7 -2
  28. package/src/lib/components/detailView/fieldInput.svelte +10 -9
  29. package/src/lib/components/detailView/update/updateDetailView.svelte +22 -18
  30. package/src/lib/components/drawer.svelte +15 -2
  31. package/src/lib/components/foreingKeyInput.svelte +163 -68
  32. package/src/lib/components/polymorphicInput.svelte +112 -63
@@ -24,6 +24,7 @@
24
24
  value: any;
25
25
  errorMessages?: string[];
26
26
  entry?: Record<string, any>;
27
+ changed?: boolean;
27
28
  }
28
29
 
29
30
  let {
@@ -32,6 +33,7 @@
32
33
  value = $bindable(),
33
34
  errorMessages = [],
34
35
  entry = $bindable(),
36
+ changed = false,
35
37
  }: Props = $props();
36
38
 
37
39
  const ui_input =
@@ -44,6 +46,7 @@
44
46
  const isDisabled = field.key === 'id' || Boolean(ui?.disabled)
45
47
  const disabledClasses = "pointer-events-none opacity-50";
46
48
  const destructive: boolean = $derived(!isDisabled && Boolean(errorMessages.length));
49
+ const changedClass = $derived(changed && !destructive ? '!bg-orange-500/5' : '');
47
50
 
48
51
  </script>
49
52
 
@@ -72,6 +75,7 @@
72
75
  <PolymorphicInput
73
76
  collectionField={polymorphicRelation.from.collection_field}
74
77
  idField={polymorphicRelation.from.id_field}
78
+ virtualField={polymorphicRelation.from.virtual_field ?? ''}
75
79
  targetCollections={polymorphicRelation.to}
76
80
  bind:entry
77
81
  {destructive}
@@ -87,7 +91,7 @@
87
91
  {:else if field.label === "id"}
88
92
  <Input
89
93
  placeholder="AUTO GENERATED"
90
- class="bg-muted text-xs"
94
+ class="bg-muted text-xs {changedClass}"
91
95
  bind:value
92
96
  />
93
97
  {:else if fieldRelationTarget && entry}
@@ -126,10 +130,7 @@
126
130
  }
127
131
  >
128
132
  <Select.Trigger
129
- class="
130
- h-9 w-full bg-muted pr-8
131
- {destructive ? 'border-destructive bg-destructive/10' : ''}
132
- "
133
+ class="h-9 w-full bg-muted pr-8 {changedClass} {destructive ? 'border-destructive !bg-destructive/10' : ''}"
133
134
  >
134
135
  {#if value != null && enumOptions}
135
136
  <EnumBadge value={String(value)} enum={enumOptions} />
@@ -159,7 +160,7 @@
159
160
  placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
160
161
  type="text"
161
162
  class="
162
- bg-muted text-xs
163
+ bg-muted text-xs {changedClass}
163
164
  {destructive ? 'border-destructive bg-destructive/10' : ''}
164
165
  "
165
166
  bind:value
@@ -169,7 +170,7 @@
169
170
  placeholder={ui?.placeholder ? ui.placeholder : value === "" ? "EMPTY STRING" : "NULL"}
170
171
  rows={5}
171
172
  class="
172
- bg-muted text-xs
173
+ bg-muted text-xs {changedClass}
173
174
  {destructive ? 'border-destructive bg-destructive/10' : ''}
174
175
  "
175
176
  bind:value
@@ -265,7 +266,7 @@
265
266
  scale={isFloat ? 20 : 0}
266
267
  groupDigits={ui?.groupDigits ?? false}
267
268
  class="
268
- bg-muted text-xs
269
+ bg-muted text-xs {changedClass}
269
270
  {destructive ? 'border-destructive bg-destructive/10' : ''}
270
271
  "
271
272
  bind:value
@@ -275,7 +276,7 @@
275
276
  placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
276
277
  type="text"
277
278
  class="
278
- bg-muted text-xs
279
+ bg-muted text-xs {changedClass}
279
280
  {destructive ? 'border-destructive bg-destructive/10' : ''}
280
281
  "
281
282
  bind:value
@@ -4,6 +4,7 @@ interface Props {
4
4
  value: any;
5
5
  errorMessages?: string[];
6
6
  entry?: Record<string, any>;
7
+ changed?: boolean;
7
8
  }
8
9
  declare const FieldInput: import("svelte").Component<Props, {}, "value" | "entry">;
9
10
  type FieldInput = ReturnType<typeof FieldInput>;
@@ -29,6 +29,7 @@
29
29
  import { untrack } from "svelte";
30
30
  import { showDialog } from "../../../actions";
31
31
 
32
+
32
33
  const { lobb, ctx } = getStudioContext();
33
34
  import { getChangedProperties } from "../../../utils";
34
35
  import UpdateDetailViewChildren from "./updateDetailViewChildren.svelte";
@@ -81,22 +82,15 @@
81
82
  const hasChildChanges = $derived(
82
83
  Object.values(localChanges.children).some((ch: ChildrenChanges) =>
83
84
  ch.created.length || ch.updated.length || ch.deleted.length || ch.linked.length || ch.unlinked.length
85
+ ) ||
86
+ Object.values(localChanges.data).some((val: any) =>
87
+ val && typeof val === 'object' && !Array.isArray(val) && (val.create || val.update || val.delete)
84
88
  )
85
89
  );
86
90
 
87
- const changeSummaryLines = $derived.by(() => {
88
- const lines: string[] = [];
89
- const fieldCount = Object.keys(localChanges.data).length;
90
- if (fieldCount > 0) lines.push(`${fieldCount} field${fieldCount > 1 ? 's' : ''} changed`);
91
- for (const [col, ch] of Object.entries(localChanges.children) as [string, ChildrenChanges][]) {
92
- if (ch.created.length) lines.push(`${ch.created.length} created in ${col}`);
93
- if (ch.linked.length) lines.push(`${ch.linked.length} linked in ${col}`);
94
- if (ch.updated.length) lines.push(`${ch.updated.length} edited in ${col}`);
95
- if (ch.deleted.length) lines.push(`${ch.deleted.length} deleted from ${col}`);
96
- if (ch.unlinked.length) lines.push(`${ch.unlinked.length} unlinked from ${col}`);
97
- }
98
- return lines;
99
- });
91
+ import { buildChangeTree, renderTree } from "../changeTreeUtils";
92
+
93
+ const changeSummaryLines = $derived(renderTree(buildChangeTree(localChanges)));
100
94
 
101
95
  $effect(() => {
102
96
  const currentEntrySnap = $state.snapshot(values);
@@ -106,8 +100,18 @@
106
100
  });
107
101
  });
108
102
 
103
+ function cleanFkField(val: any): any {
104
+ if (!val || typeof val !== 'object' || Array.isArray(val)) return val;
105
+ // strip internal id from pending edit (server reads from DB)
106
+ if (val.id && val.update) return { ...(val.collection ? { collection: val.collection } : {}), update: val.update };
107
+ // { delete: true } — pass through as-is (no id needed)
108
+ return val;
109
+ }
110
+
109
111
  function buildPayload(changes: Changes): { data: Record<string, any>; children?: Record<string, any> } {
110
- const { id: _id, ...data } = changes.data;
112
+ const { id: _id, ...rawData } = changes.data;
113
+ const data: Record<string, any> = {};
114
+ for (const [key, val] of Object.entries(rawData)) data[key] = cleanFkField(val);
111
115
  const children = buildChildren(changes.children);
112
116
  return { data, ...(children ? { children } : {}) };
113
117
  }
@@ -140,7 +144,7 @@
140
144
  if (!isRecordingMode && hasChildChanges && changeSummaryLines.length > 0) {
141
145
  const confirmed = await showDialog(
142
146
  "Confirm changes",
143
- changeSummaryLines.map(l => `• ${l}`).join('\n')
147
+ changeSummaryLines.join('\n')
144
148
  );
145
149
  if (!confirmed) return;
146
150
  }
@@ -151,7 +155,7 @@
151
155
  if (response.status === 204) {
152
156
  onChanges?.(snap);
153
157
  if (onSuccessfullSave) await onSuccessfullSave(snap);
154
- toast.success(`The record was successfully updated`);
158
+ if (!isRecordingMode) toast.success(`The record was successfully updated`);
155
159
  onCancel?.();
156
160
  return;
157
161
  }
@@ -171,7 +175,7 @@
171
175
 
172
176
  onChanges?.(snap);
173
177
  if (onSuccessfullSave) await onSuccessfullSave(snap);
174
- toast.success(`The record was successfully updated`);
178
+ if (!isRecordingMode) toast.success(`The record was successfully updated`);
175
179
  onCancel?.();
176
180
  }
177
181
  </script>
@@ -196,7 +200,7 @@
196
200
  </div>
197
201
  </div>
198
202
  <div class="flex-1 overflow-y-auto">
199
- <DetailView {collectionName} bind:entry={values} {fieldsErrors} />
203
+ <DetailView {collectionName} bind:entry={values} {fieldsErrors} changedFields={Object.keys(localChanges.data)} />
200
204
  {#if showRelatedRecords}
201
205
  <UpdateDetailViewChildren {collectionName} entry={values} changes={localChanges} onChanges={(children) => { localChanges.children = children; }} />
202
206
  {/if}
@@ -4,6 +4,7 @@
4
4
  import { fade } from "svelte/transition";
5
5
  import { cubicOut } from "svelte/easing";
6
6
  import Portal from "svelte-portal";
7
+ import { getContext, setContext, onDestroy } from "svelte";
7
8
 
8
9
  interface Props {
9
10
  children?: Snippet<[]>;
@@ -13,6 +14,16 @@
13
14
 
14
15
  let { onHide, children, position = "side" }: Props = $props();
15
16
 
17
+ // Track nesting depth for stacking effect
18
+ const DEPTH_KEY = 'drawer-depth';
19
+ const parentDepth: number = getContext(DEPTH_KEY) ?? 0;
20
+ const depth = parentDepth + 1;
21
+ setContext(DEPTH_KEY, depth);
22
+
23
+ // Side drawers get narrower, bottom drawers get shorter — both offset by 48px per level
24
+ const sideWidth = $derived(calculateDrawerWidth() - (depth - 1) * 48);
25
+ const bottomOffset = $derived((depth - 1) * 48);
26
+
16
27
  function slide(_node: Element, { duration = 250, axis }: { duration?: number; axis: "x" | "y" }) {
17
28
  return {
18
29
  duration,
@@ -39,9 +50,11 @@
39
50
  role="dialog"
40
51
  transition:slide={{ axis: position === "bottom" ? "y" : "x" }}
41
52
  class={position === "bottom"
42
- ? "fixed bottom-0 left-0 z-40 flex h-[60vh] w-full flex-col border-t bg-card"
53
+ ? "fixed bottom-0 left-0 z-40 flex w-full flex-col border-t bg-card"
43
54
  : "fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-card"}
44
- style={position === "side" ? `max-width: ${calculateDrawerWidth()}px;` : ""}
55
+ style={position === "side"
56
+ ? `max-width: ${sideWidth}px;`
57
+ : `height: calc(60vh - ${bottomOffset}px);`}
45
58
  >
46
59
  {@render children?.()}
47
60
  </div>
@@ -2,11 +2,11 @@
2
2
  import { onMount } from "svelte";
3
3
  import Input from "./ui/input/input.svelte";
4
4
  import SelectRecord from "./selectRecord.svelte";
5
- import UpdateDetailViewButton from "./detailView/update/updateDetailViewButton.svelte";
6
5
  import CreateDetailView from "./detailView/create/createDetailView.svelte";
6
+ import UpdateDetailView from "./detailView/update/updateDetailView.svelte";
7
7
  import { getCollectionPrimaryField } from "./dataTable/utils";
8
8
  import { getStudioContext } from "../context";
9
- import { ExternalLink, Plus } from "lucide-svelte";
9
+ import { Plus, Link, Pencil, Unlink, Trash, RotateCcw, RefreshCw } from "lucide-svelte";
10
10
  import Button from "./ui/button/button.svelte";
11
11
 
12
12
  const { lobb, ctx } = getStudioContext();
@@ -15,7 +15,7 @@
15
15
  parentCollectionName: string;
16
16
  collectionName: string;
17
17
  fieldName: string;
18
- value?: number | null;
18
+ value?: any;
19
19
  destructive?: boolean;
20
20
  entry: Record<string, any>;
21
21
  }
@@ -29,96 +29,180 @@
29
29
  entry,
30
30
  }: LocalProps = $props();
31
31
 
32
- let displayName = $state<string | null>(null);
33
32
  let createDrawerOpen = $state(false);
33
+ let editDrawerOpen = $state(false);
34
+ let editValues: Record<string, any> | undefined = $state(undefined);
35
+ let originalId = $state<number | null>(null);
36
+ let initialValue = $state<any>(undefined);
37
+ let unlinked = $state(false);
34
38
 
35
- onMount(async () => {
36
- if (value == null) return;
37
- try {
38
- const res = await lobb.findOne(collectionName, value);
39
- const record = (await res.json()).data;
40
- const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
41
- displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
42
- } catch {
43
- displayName = null;
39
+ onMount(() => { initialValue = value; });
40
+
41
+ // Derived state from value
42
+ const isPendingCreate = $derived(value && typeof value === 'object' && value.create);
43
+ const isPendingEdit = $derived(value && typeof value === 'object' && value.id && value.update);
44
+ const isStagedDelete = $derived(value && typeof value === 'object' && value.delete === true);
45
+ const isStagedUnlink = $derived(unlinked);
46
+ const hasRealId = $derived(value != null && typeof value === 'number');
47
+ const isNewLink = $derived(hasRealId && initialValue !== undefined && initialValue == null && value !== initialValue);
48
+ const isReplaced = $derived(hasRealId && initialValue !== undefined && initialValue != null && value !== initialValue);
49
+ const isEmpty = $derived((value == null || value === 0) && !unlinked);
50
+ const isZeroPlaceholder = $derived(value === 0);
51
+
52
+ const bgClass = $derived(
53
+ isPendingCreate ? '!bg-green-500/5 border-green-500/40' :
54
+ isNewLink ? '!bg-blue-500/5 border-blue-500/40' :
55
+ isReplaced ? '!bg-orange-500/5 border-orange-500/40' :
56
+ isPendingEdit ? '!bg-orange-500/5 border-orange-500/40' :
57
+ isStagedUnlink ? '!bg-slate-500/5 border-slate-500/40' :
58
+ isStagedDelete ? '!bg-red-500/5 border-red-500/40' :
59
+ ''
60
+ );
61
+
62
+ const displayId = $derived(
63
+ isPendingEdit ? (value as any).id :
64
+ isStagedUnlink ? originalId :
65
+ isStagedDelete ? originalId :
66
+ hasRealId ? value :
67
+ null
68
+ );
69
+
70
+ async function handleCreated(record: any) {
71
+ // dry-run result has no id — store as pending create
72
+ if (!record.id) {
73
+ value = { create: record };
74
+ } else {
75
+ value = record.id;
44
76
  }
45
- });
77
+ }
46
78
 
47
- $effect(() => {
48
- if (value == null) displayName = null;
49
- });
79
+ async function openEdit() {
80
+ const res = await lobb.findAll(collectionName, { filter: { id: value }, limit: 1 });
81
+ const result = await res.json();
82
+ editValues = result.data[0];
83
+ editDrawerOpen = true;
84
+ }
85
+
86
+ function handleEditChanges(changes: import('./detailView/utils').Changes) {
87
+ if (Object.keys(changes.data).length === 0) {
88
+ value = (editValues as any)?.id ?? value;
89
+ } else {
90
+ value = { id: (editValues as any)?.id ?? value, update: changes.data };
91
+ }
92
+ }
50
93
 
51
94
  function handleSelect(selectedEntry: any) {
52
- const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
53
95
  value = selectedEntry.id;
54
- displayName = primaryFieldName ? String(selectedEntry[primaryFieldName]) : null;
55
96
  }
56
97
 
57
- async function handleCreated(record: any) {
58
- const primaryFieldName = getCollectionPrimaryField(ctx, collectionName);
59
- value = record.id;
60
- displayName = primaryFieldName ? String(record[primaryFieldName]) : null;
98
+ function handleUnlink() {
99
+ if (hasRealId) {
100
+ originalId = value as number;
101
+ unlinked = true;
102
+ value = null;
103
+ }
104
+ }
105
+
106
+ async function handleDelete() {
107
+ if (hasRealId) {
108
+ originalId = value as number;
109
+ value = { delete: true };
110
+ }
61
111
  }
62
112
 
63
- const idIsZero = $derived(value === 0);
113
+ function handleRevert() {
114
+ if (originalId != null) {
115
+ value = originalId;
116
+ originalId = null;
117
+ } else {
118
+ value = null;
119
+ }
120
+ unlinked = false;
121
+ }
64
122
  </script>
65
123
 
66
- {#if !idIsZero}
124
+ {#if !isZeroPlaceholder}
67
125
  <div class="relative">
68
- <div class="flex gap-2 absolute right-0 top-0 mr-9 h-full items-center text-xs">
69
- {#if value != null}
70
- <UpdateDetailViewButton
71
- collectionName={collectionName}
72
- recordId={value}
126
+ <!-- Action buttons overlay on the right -->
127
+ <div class="flex gap-1 absolute right-0 top-0 mr-9 h-full items-center text-xs">
128
+ {#if isStagedUnlink || isStagedDelete || isPendingCreate || isPendingEdit}
129
+ <Button
130
+ class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
73
131
  variant="ghost"
74
- class="h-5 w-5 px-0 py-0 text-muted-foreground hover:bg-transparent"
75
- Icon={ExternalLink}
76
- ></UpdateDetailViewButton>
77
- {/if}
78
- {#if displayName}
79
- <div class="flex items-center bg-background rounded-full border h-6 px-3 shadow-sm">
80
- {displayName}
81
- </div>
132
+ Icon={RotateCcw}
133
+ onclick={handleRevert}
134
+ title="Revert"
135
+ ></Button>
136
+ {:else if hasRealId}
137
+ <Button
138
+ class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
139
+ variant="ghost"
140
+ Icon={Trash}
141
+ onclick={handleDelete}
142
+ title="Delete record"
143
+ ></Button>
144
+ <Button
145
+ class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
146
+ variant="ghost"
147
+ Icon={Unlink}
148
+ onclick={handleUnlink}
149
+ title="Unlink"
150
+ ></Button>
151
+ <Button
152
+ class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
153
+ variant="ghost"
154
+ Icon={Pencil}
155
+ onclick={openEdit}
156
+ title="Edit record"
157
+ ></Button>
158
+ <SelectRecord
159
+ class="h-5 w-5 px-0 py-0 hover:bg-transparent text-muted-foreground"
160
+ variant="ghost"
161
+ {collectionName}
162
+ onSelect={handleSelect}
163
+ additionalFilter={{ id: { $ne: value } }}
164
+ title="Replace"
165
+ {entry}
166
+ >
167
+ {#snippet children()}<RefreshCw size="13" />{/snippet}
168
+ </SelectRecord>
169
+ {:else if isEmpty}
170
+ <Button
171
+ class="h-6 px-2 font-normal text-xs"
172
+ variant="outline"
173
+ Icon={Plus}
174
+ onclick={() => (createDrawerOpen = true)}
175
+ >
176
+ Create
177
+ </Button>
178
+ <SelectRecord
179
+ class="h-6 px-2 font-normal text-xs"
180
+ variant="outline"
181
+ {parentCollectionName}
182
+ {collectionName}
183
+ {fieldName}
184
+ onSelect={handleSelect}
185
+ text="Link"
186
+ {entry}
187
+ />
82
188
  {/if}
83
- <Button
84
- class="h-6 px-2 font-normal text-xs"
85
- variant="outline"
86
- Icon={Plus}
87
- onclick={() => (createDrawerOpen = true)}
88
- >
89
- Create
90
- </Button>
91
- <SelectRecord
92
- class="h-6 px-2 font-normal text-xs"
93
- variant="outline"
94
- {parentCollectionName}
95
- {collectionName}
96
- {fieldName}
97
- onSelect={handleSelect}
98
- text="Select"
99
- {entry}
100
- />
101
189
  </div>
190
+
191
+ <!-- Input field colored by state -->
102
192
  <Input
103
- placeholder={"NULL"}
193
+ placeholder={isEmpty ? "NULL" : ""}
104
194
  type="number"
105
- class="
106
- bg-muted text-xs
107
- {destructive ? 'border-destructive bg-destructive/10' : ''}
108
- "
195
+ class="bg-muted text-xs {bgClass} {destructive ? '!bg-destructive/10 border-destructive' : ''}"
196
+ disabled={isPendingCreate || isPendingEdit}
109
197
  bind:value={
110
- () => value ?? "",
111
- (v) => (value = (v === "" || v == null) ? null : Number(v))
198
+ () => displayId ?? "",
199
+ (v) => { if (hasRealId || isEmpty) value = (v === "" || v == null) ? null : Number(v); }
112
200
  }
113
201
  />
114
202
  </div>
115
203
  {:else}
116
204
  <div class="relative z-10">
117
- <Input
118
- placeholder={"PARENT ID"}
119
- class="bg-muted text-xs"
120
- disabled={true}
121
- />
205
+ <Input placeholder="PARENT ID" class="bg-muted text-xs" disabled={true} />
122
206
  </div>
123
207
  {/if}
124
208
 
@@ -126,6 +210,17 @@
126
210
  <CreateDetailView
127
211
  collectionName={collectionName}
128
212
  onCreated={handleCreated}
213
+ onChanges={() => {}}
129
214
  onCancel={async () => { createDrawerOpen = false; }}
130
215
  />
131
216
  {/if}
217
+
218
+ {#if editDrawerOpen && editValues}
219
+ <UpdateDetailView
220
+ collectionName={collectionName}
221
+ recordId={String(editValues.id)}
222
+ values={editValues}
223
+ onChanges={handleEditChanges}
224
+ onCancel={async () => { editDrawerOpen = false; editValues = undefined; }}
225
+ />
226
+ {/if}
@@ -2,7 +2,7 @@ interface LocalProps {
2
2
  parentCollectionName: string;
3
3
  collectionName: string;
4
4
  fieldName: string;
5
- value?: number | null;
5
+ value?: any;
6
6
  destructive?: boolean;
7
7
  entry: Record<string, any>;
8
8
  }