@lobb-js/studio 0.37.0 → 0.38.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.
- package/dist/components/dataTable/fieldPicker.svelte +61 -0
- package/dist/components/dataTable/fieldPicker.svelte.d.ts +9 -0
- package/dist/components/dataTable/filterButton.svelte +3 -2
- package/dist/components/dataTable/sort.svelte +169 -104
- package/dist/components/dataTable/sortButton.svelte +33 -7
- package/dist/components/dataTable/table.svelte +3 -2
- package/dist/components/dataTable/table.svelte.d.ts +1 -0
- package/dist/components/detailView/create/createManyView.svelte +1 -3
- package/dist/components/importButton.svelte +154 -31
- package/package.json +4 -3
- package/src/lib/components/dataTable/fieldPicker.svelte +61 -0
- package/src/lib/components/dataTable/filterButton.svelte +3 -2
- package/src/lib/components/dataTable/sort.svelte +169 -104
- package/src/lib/components/dataTable/sortButton.svelte +33 -7
- package/src/lib/components/dataTable/table.svelte +3 -2
- package/src/lib/components/detailView/create/createManyView.svelte +1 -3
- package/src/lib/components/importButton.svelte +154 -31
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Typeahead field picker. Renders inside a parent-owned <Popover.Content>
|
|
3
|
+
// so the parent controls open/close. Used by SortButton's fast-path
|
|
4
|
+
// picker and by Sort's "add a rule" popover.
|
|
5
|
+
import { getStudioContext } from "../../context";
|
|
6
|
+
import { getFieldIcon } from "./utils";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
collectionName: string;
|
|
10
|
+
excludeFields?: string[];
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
onPick: (fieldName: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
collectionName,
|
|
17
|
+
excludeFields = [],
|
|
18
|
+
placeholder = "Pick a field…",
|
|
19
|
+
onPick,
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
const { ctx } = getStudioContext();
|
|
23
|
+
|
|
24
|
+
let search = $state("");
|
|
25
|
+
|
|
26
|
+
const allFields = $derived(
|
|
27
|
+
Object.keys(ctx.meta.collections[collectionName].fields).filter(
|
|
28
|
+
(f) => !excludeFields.includes(f),
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
const filteredFields = $derived(
|
|
32
|
+
search
|
|
33
|
+
? allFields.filter((f) =>
|
|
34
|
+
f.toLowerCase().includes(search.toLowerCase()),
|
|
35
|
+
)
|
|
36
|
+
: allFields,
|
|
37
|
+
);
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<input
|
|
41
|
+
{placeholder}
|
|
42
|
+
bind:value={search}
|
|
43
|
+
class="w-full rounded-md border bg-muted px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-ring"
|
|
44
|
+
/>
|
|
45
|
+
<div class="mt-2 max-h-60 overflow-auto">
|
|
46
|
+
{#each filteredFields as fieldName}
|
|
47
|
+
{@const FieldIcon = getFieldIcon(ctx, fieldName, collectionName)}
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
onclick={() => onPick(fieldName)}
|
|
51
|
+
class="flex w-full items-center gap-2 rounded-md p-2 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
52
|
+
>
|
|
53
|
+
<FieldIcon size="14" />
|
|
54
|
+
<span>{fieldName}</span>
|
|
55
|
+
</button>
|
|
56
|
+
{:else}
|
|
57
|
+
<div class="p-2 text-center text-xs text-muted-foreground">
|
|
58
|
+
No matching fields
|
|
59
|
+
</div>
|
|
60
|
+
{/each}
|
|
61
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
collectionName: string;
|
|
3
|
+
excludeFields?: string[];
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
onPick: (fieldName: string) => void;
|
|
6
|
+
}
|
|
7
|
+
declare const FieldPicker: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type FieldPicker = ReturnType<typeof FieldPicker>;
|
|
9
|
+
export default FieldPicker;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as Popover from "../ui/popover/index.js";
|
|
3
|
-
import {
|
|
3
|
+
import { Settings2 } from "lucide-svelte";
|
|
4
4
|
import { buttonVariants } from "../ui/button";
|
|
5
5
|
import Filter from "./filter.svelte";
|
|
6
6
|
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
class={buttonVariants({
|
|
23
23
|
variant: "ghost",
|
|
24
24
|
size: "sm",
|
|
25
|
+
class: "text-muted-foreground",
|
|
25
26
|
})}
|
|
26
27
|
>
|
|
27
|
-
<
|
|
28
|
+
<Settings2 />
|
|
28
29
|
{#if showText}
|
|
29
30
|
{#if Object.keys(filter).length}
|
|
30
31
|
Filtered by {Object.keys(filter).length} rules
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import * as _ from "lodash-es";
|
|
3
3
|
|
|
4
4
|
import * as Popover from "../ui/popover/index.js";
|
|
5
|
-
import
|
|
5
|
+
import * as Select from "../ui/select/index.js";
|
|
6
|
+
import { GripVertical, Plus, X } from "lucide-svelte";
|
|
6
7
|
import Button, { buttonVariants } from "../ui/button/button.svelte";
|
|
7
|
-
import Label from "../ui/label/label.svelte";
|
|
8
|
-
import Switch from "../ui/switch/switch.svelte";
|
|
9
8
|
import { getStudioContext } from "../../context";
|
|
10
9
|
import { getFieldIcon } from "./utils";
|
|
10
|
+
import { dndzone } from "svelte-dnd-action";
|
|
11
|
+
import { flip } from "svelte/animate";
|
|
12
|
+
import FieldPicker from "./fieldPicker.svelte";
|
|
11
13
|
|
|
12
14
|
const { ctx } = getStudioContext();
|
|
13
15
|
|
|
@@ -21,108 +23,174 @@
|
|
|
21
23
|
|
|
22
24
|
let popoverOpen = $state(false);
|
|
23
25
|
|
|
26
|
+
// Fields not currently used by any rule. Used to decide whether the
|
|
27
|
+
// "Add a sort rule" trigger is shown at all.
|
|
24
28
|
function getFieldNames() {
|
|
25
29
|
const options = Object.keys(
|
|
26
30
|
ctx.meta.collections[collectionName].fields,
|
|
27
31
|
);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
return filteredOptions;
|
|
32
|
+
const used = items.map((r) => r.id);
|
|
33
|
+
return _.difference(options, used);
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`Property '${String(propertyToMove)}' not found in the object.`,
|
|
45
|
-
);
|
|
46
|
-
return { ...obj };
|
|
47
|
-
}
|
|
36
|
+
// The field-name <Select> for an existing rule needs to list every
|
|
37
|
+
// field that isn't already used by *another* rule (plus its own
|
|
38
|
+
// current field). That way the user can swap a rule's field to any
|
|
39
|
+
// unused one without colliding with the others.
|
|
40
|
+
function getFieldOptionsForRule(currentField: string): string[] {
|
|
41
|
+
const all = Object.keys(ctx.meta.collections[collectionName].fields);
|
|
42
|
+
const usedByOthers = new Set(
|
|
43
|
+
items.map((r) => r.id).filter((id) => id !== currentField),
|
|
44
|
+
);
|
|
45
|
+
return all.filter((f) => !usedByOthers.has(f));
|
|
46
|
+
}
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
// Push items → sort. Strips any in-flight shadow rows whose ids
|
|
49
|
+
// don't correspond to a real schema field. Called explicitly from
|
|
50
|
+
// each mutator (and from finalize) instead of via $effect — that
|
|
51
|
+
// way a drag's `consider` events don't spam the parent's sort prop
|
|
52
|
+
// and trigger a refetch on every frame. We reassign the whole
|
|
53
|
+
// binding (instead of mutating in place) because the parent's
|
|
54
|
+
// refetch watcher only fires on identity changes.
|
|
55
|
+
function commit() {
|
|
56
|
+
const validIds = ctx.meta.collections[collectionName].fields;
|
|
57
|
+
const next: Record<string, "asc" | "desc"> = {};
|
|
58
|
+
for (const r of items) {
|
|
59
|
+
if (r.id in validIds) next[r.id] = r.direction;
|
|
61
60
|
}
|
|
61
|
+
sort = next;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renameField(oldName: string, newName: string) {
|
|
65
|
+
if (oldName === newName) return;
|
|
66
|
+
items = items.map((r) => (r.id === oldName ? { ...r, id: newName } : r));
|
|
67
|
+
commit();
|
|
68
|
+
}
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
function setDirection(fieldId: string, dir: "asc" | "desc") {
|
|
71
|
+
items = items.map((r) => (r.id === fieldId ? { ...r, direction: dir } : r));
|
|
72
|
+
commit();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function removeRule(fieldId: string) {
|
|
76
|
+
items = items.filter((r) => r.id !== fieldId);
|
|
77
|
+
commit();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function addRule(fieldId: string) {
|
|
81
|
+
items = [...items, { id: fieldId, direction: "asc" }];
|
|
82
|
+
commit();
|
|
83
|
+
}
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
// svelte-dnd-action needs to own an array of {id} objects so it can
|
|
86
|
+
// stamp placeholder shadows during drag. We keep that array as local
|
|
87
|
+
// state and push into `sort` on every change — never the other way
|
|
88
|
+
// around, so the shadow item never leaks into the parent's filter.
|
|
89
|
+
type Rule = { id: string; direction: "asc" | "desc" };
|
|
90
|
+
let items = $state<Rule[]>(
|
|
91
|
+
Object.entries(sort).map(([id, direction]) => ({
|
|
92
|
+
id,
|
|
93
|
+
direction: direction as "asc" | "desc",
|
|
94
|
+
})),
|
|
95
|
+
);
|
|
69
96
|
|
|
70
|
-
|
|
97
|
+
function handleDndConsider(e: CustomEvent<{ items: Rule[] }>) {
|
|
98
|
+
// Mid-drag: update local items so the row tracks the cursor, but
|
|
99
|
+
// do NOT touch `sort` — that would refetch the table every frame.
|
|
100
|
+
items = e.detail.items;
|
|
71
101
|
}
|
|
102
|
+
function handleDndFinalize(e: CustomEvent<{ items: Rule[] }>) {
|
|
103
|
+
items = e.detail.items;
|
|
104
|
+
commit();
|
|
105
|
+
}
|
|
106
|
+
|
|
72
107
|
</script>
|
|
73
108
|
|
|
74
109
|
<div class="flex flex-col gap-2 p-2 text-muted-foreground">
|
|
75
|
-
{#if
|
|
76
|
-
<div
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
110
|
+
{#if items.length}
|
|
111
|
+
<div
|
|
112
|
+
class="flex flex-col gap-2"
|
|
113
|
+
use:dndzone={{
|
|
114
|
+
items,
|
|
115
|
+
dragDisabled: false,
|
|
116
|
+
dropTargetStyle: {},
|
|
117
|
+
flipDurationMs: 150,
|
|
118
|
+
}}
|
|
119
|
+
onconsider={handleDndConsider}
|
|
120
|
+
onfinalize={handleDndFinalize}
|
|
121
|
+
>
|
|
122
|
+
{#each items as rule (rule.id)}
|
|
123
|
+
{@const field = ctx.meta.collections[collectionName].fields[rule.id]}
|
|
124
|
+
<!-- Single wrapper so the `animate:flip` directive sits
|
|
125
|
+
directly under the keyed each. The conditional inside
|
|
126
|
+
swaps between the dnd shadow placeholder and the real
|
|
127
|
+
row. -->
|
|
128
|
+
<div animate:flip={{ duration: 150 }}>
|
|
129
|
+
{#if !field}
|
|
130
|
+
<div class="h-7"></div>
|
|
131
|
+
{:else}
|
|
132
|
+
{@const FieldIcon = getFieldIcon(ctx, rule.id, collectionName)}
|
|
133
|
+
<div class="flex items-center gap-2 text-xs">
|
|
134
|
+
<!-- Drag handle. The whole rule row is draggable via
|
|
135
|
+
dndzone, but cursor-grab on the handle telegraphs
|
|
136
|
+
the intent. -->
|
|
137
|
+
<GripVertical
|
|
138
|
+
size="15"
|
|
139
|
+
class="cursor-grab text-muted-foreground hover:text-foreground"
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<!-- Field-name picker — only fields not used by other
|
|
143
|
+
rules are selectable; the current one always is. -->
|
|
144
|
+
<Select.Root
|
|
145
|
+
type="single"
|
|
146
|
+
value={rule.id}
|
|
147
|
+
onValueChange={(v) => v && renameField(rule.id, v)}
|
|
148
|
+
>
|
|
149
|
+
<Select.Trigger class="bg-muted h-7 flex-1 text-xs">
|
|
150
|
+
<div class="inline-flex items-center gap-1.5">
|
|
151
|
+
<FieldIcon size="13" />
|
|
152
|
+
{rule.id}
|
|
153
|
+
</div>
|
|
154
|
+
</Select.Trigger>
|
|
155
|
+
<Select.Content>
|
|
156
|
+
{#each getFieldOptionsForRule(rule.id) as optionName}
|
|
157
|
+
{@const OptionIcon = getFieldIcon(ctx, optionName, collectionName)}
|
|
158
|
+
<Select.Item value={optionName}>
|
|
159
|
+
<div class="inline-flex items-center gap-1.5">
|
|
160
|
+
<OptionIcon size="13" />
|
|
161
|
+
{optionName}
|
|
162
|
+
</div>
|
|
163
|
+
</Select.Item>
|
|
164
|
+
{/each}
|
|
165
|
+
</Select.Content>
|
|
166
|
+
</Select.Root>
|
|
167
|
+
|
|
168
|
+
<!-- Direction picker -->
|
|
169
|
+
<Select.Root
|
|
170
|
+
type="single"
|
|
171
|
+
value={rule.direction}
|
|
172
|
+
onValueChange={(v) => {
|
|
173
|
+
if (v === "asc" || v === "desc") setDirection(rule.id, v);
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<Select.Trigger class="bg-muted h-7 w-32 text-xs">
|
|
177
|
+
{rule.direction === "asc" ? "Ascending" : "Descending"}
|
|
178
|
+
</Select.Trigger>
|
|
179
|
+
<Select.Content>
|
|
180
|
+
<Select.Item value="asc">Ascending</Select.Item>
|
|
181
|
+
<Select.Item value="desc">Descending</Select.Item>
|
|
182
|
+
</Select.Content>
|
|
183
|
+
</Select.Root>
|
|
184
|
+
|
|
185
|
+
<Button
|
|
186
|
+
onclick={() => removeRule(rule.id)}
|
|
187
|
+
class="h-7 w-7 text-muted-foreground hover:bg-transparent"
|
|
188
|
+
variant="ghost"
|
|
189
|
+
size="icon"
|
|
190
|
+
Icon={X}
|
|
191
|
+
></Button>
|
|
192
|
+
</div>
|
|
193
|
+
{/if}
|
|
126
194
|
</div>
|
|
127
195
|
{/each}
|
|
128
196
|
</div>
|
|
@@ -144,25 +212,22 @@
|
|
|
144
212
|
class={buttonVariants({
|
|
145
213
|
variant: "ghost",
|
|
146
214
|
size: "sm",
|
|
215
|
+
class: "text-muted-foreground",
|
|
147
216
|
})}
|
|
148
217
|
>
|
|
149
218
|
<Plus />
|
|
150
219
|
Add a sort rule
|
|
151
220
|
</Popover.Trigger>
|
|
152
|
-
<Popover.Content class="
|
|
153
|
-
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<FieldIcon size="15" />
|
|
163
|
-
<div>{fieldName}</div>
|
|
164
|
-
</button>
|
|
165
|
-
{/each}
|
|
221
|
+
<Popover.Content class="w-64 p-2">
|
|
222
|
+
<FieldPicker
|
|
223
|
+
{collectionName}
|
|
224
|
+
excludeFields={items.map((r) => r.id)}
|
|
225
|
+
placeholder="Pick a field to sort by…"
|
|
226
|
+
onPick={(fieldName: string) => {
|
|
227
|
+
addRule(fieldName);
|
|
228
|
+
popoverOpen = false;
|
|
229
|
+
}}
|
|
230
|
+
/>
|
|
166
231
|
</Popover.Content>
|
|
167
232
|
</Popover.Root>
|
|
168
233
|
{:else}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
// Sort button. Two-stage flow:
|
|
3
|
+
// • No sorts yet → typeahead field picker (fast path: just pick a
|
|
4
|
+
// field, it lands as asc, popover closes)
|
|
5
|
+
// • At least one sort → full multi-rule editor (Sort.svelte) so the
|
|
6
|
+
// user can reorder, toggle direction, add more, etc.
|
|
2
7
|
import * as Popover from "../ui/popover/index.js";
|
|
3
|
-
import {
|
|
8
|
+
import { ArrowUpDown } from "lucide-svelte";
|
|
4
9
|
import { buttonVariants } from "../ui/button";
|
|
5
10
|
import Sort from "./sort.svelte";
|
|
11
|
+
import FieldPicker from "./fieldPicker.svelte";
|
|
6
12
|
|
|
7
13
|
interface Props {
|
|
8
14
|
collectionName: string;
|
|
@@ -11,26 +17,46 @@
|
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
let { collectionName, sort = $bindable({}), showText }: Props = $props();
|
|
20
|
+
|
|
21
|
+
let popoverOpen = $state(false);
|
|
22
|
+
|
|
23
|
+
function pick(fieldName: string) {
|
|
24
|
+
sort = { ...sort, [fieldName]: "asc" };
|
|
25
|
+
popoverOpen = false;
|
|
26
|
+
}
|
|
14
27
|
</script>
|
|
15
28
|
|
|
16
|
-
<Popover.Root>
|
|
29
|
+
<Popover.Root bind:open={popoverOpen}>
|
|
17
30
|
<Popover.Trigger
|
|
18
31
|
class={buttonVariants({
|
|
19
32
|
variant: "ghost",
|
|
20
33
|
size: "sm",
|
|
34
|
+
class: "text-muted-foreground",
|
|
21
35
|
})}
|
|
22
36
|
>
|
|
23
|
-
<
|
|
37
|
+
<ArrowUpDown />
|
|
24
38
|
{#if showText}
|
|
25
39
|
{@const sortRules = Object.keys(sort).length}
|
|
26
40
|
{#if sortRules}
|
|
27
|
-
Sorted by {sortRules}
|
|
41
|
+
Sorted by {sortRules} {sortRules === 1 ? "field" : "fields"}
|
|
28
42
|
{:else}
|
|
29
43
|
Sort
|
|
30
44
|
{/if}
|
|
31
45
|
{/if}
|
|
32
46
|
</Popover.Trigger>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
{#if Object.keys(sort).length === 0}
|
|
48
|
+
<!-- Fast path: typeahead picker. The first selected field starts
|
|
49
|
+
the sort; subsequent edits go through the full editor below. -->
|
|
50
|
+
<Popover.Content class="w-64 p-2">
|
|
51
|
+
<FieldPicker
|
|
52
|
+
{collectionName}
|
|
53
|
+
placeholder="Pick a field to sort by…"
|
|
54
|
+
onPick={pick}
|
|
55
|
+
/>
|
|
56
|
+
</Popover.Content>
|
|
57
|
+
{:else}
|
|
58
|
+
<Popover.Content class="w-screen max-w-[24rem] p-0">
|
|
59
|
+
<Sort {collectionName} {showText} bind:sort />
|
|
60
|
+
</Popover.Content>
|
|
61
|
+
{/if}
|
|
36
62
|
</Popover.Root>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
interface Column {
|
|
3
3
|
id: string;
|
|
4
|
+
label?: string;
|
|
4
5
|
icon?: any;
|
|
5
6
|
subtext?: any;
|
|
6
7
|
}
|
|
@@ -223,8 +224,8 @@
|
|
|
223
224
|
{:else}
|
|
224
225
|
<ColumnIcon size="12.5" class="text-muted-foreground" />
|
|
225
226
|
{/if}
|
|
226
|
-
<div class="font-bold">{column.id}</div>
|
|
227
|
-
<div class="text-muted-foreground text-[0.7rem]">
|
|
227
|
+
<div class="font-bold whitespace-nowrap">{column.label ?? column.id}</div>
|
|
228
|
+
<div class="text-muted-foreground text-[0.7rem] whitespace-nowrap">
|
|
228
229
|
{column.subtext}
|
|
229
230
|
</div>
|
|
230
231
|
</button>
|
|
@@ -60,9 +60,7 @@
|
|
|
60
60
|
relation.to.collection === parentRecord?.collectionName,
|
|
61
61
|
)?.from.field;
|
|
62
62
|
const createValues = {
|
|
63
|
-
[refrenceFieldName]:
|
|
64
|
-
id: 0,
|
|
65
|
-
},
|
|
63
|
+
[refrenceFieldName]: 0,
|
|
66
64
|
};
|
|
67
65
|
let selectedRecordsIds: string[] = $derived(
|
|
68
66
|
entries.filter((entry) => entry.id).map((entry) => entry.id),
|