From 2f9f00383ecb0fc34e725fcc7187186a93e8ab6d Mon Sep 17 00:00:00 2001 From: "Snow@Moana" Date: Tue, 31 Mar 2026 07:39:08 +1100 Subject: [PATCH 1/3] Restore v1 creatable behaviour. --- fields/core/select/edit-component.tsx | 66 +++++++++++++++++++++++++-- fields/core/select/index.tsx | 11 +++-- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/fields/core/select/edit-component.tsx b/fields/core/select/edit-component.tsx index c0f4c432a..d819d088c 100644 --- a/fields/core/select/edit-component.tsx +++ b/fields/core/select/edit-component.tsx @@ -1,6 +1,7 @@ "use client"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; +import { PlusIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { Combobox, @@ -37,11 +38,14 @@ const EditComponent = (props: any) => { const { value, field, onChange } = props; const isReadonly = Boolean(field?.readonly); const multiple = Boolean(field.options?.multiple); + const creatable = Boolean(field.options?.creatable); const storeAsObject = field?.type === "reference" && field.options?.store === "object"; const anchor = useComboboxAnchor(); + const [inputValue, setInputValue] = useState(""); - const options = useMemo( + // The static options defined in the field schema + const baseOptions = useMemo( () => Array.isArray(field.options?.values) ? field.options.values.map(normalizeOption) @@ -49,6 +53,41 @@ const EditComponent = (props: any) => { [field.options?.values], ); + // When creatable, also include any already-selected free-form values that + // aren't in the static list, so they display correctly when the form loads. + const options = useMemo(() => { + if (!creatable) return baseOptions; + + const existingValues = new Set(baseOptions.map((o) => o.value)); + const selectedItems = multiple + ? (Array.isArray(value) ? value : []) + : (value != null && value !== "" ? [value] : []); + + const extra: Option[] = selectedItems + .map((v: any) => + typeof v === "object" && v !== null ? normalizeOption(v) : { value: String(v), label: String(v) } + ) + .filter((o: Option) => o.value !== "" && !existingValues.has(o.value)); + + return extra.length > 0 ? [...baseOptions, ...extra] : baseOptions; + }, [baseOptions, creatable, value, multiple]); + + // Synthetic "Create …" option shown when the typed value doesn't match anything + const createOption = useMemo