diff --git a/components/CountryInput.tsx b/components/CountryInput.tsx new file mode 100644 index 0000000..b1a18f9 --- /dev/null +++ b/components/CountryInput.tsx @@ -0,0 +1,54 @@ +import React, {useCallback, useMemo, useState} from 'react' +import {Autocomplete, Card, Text} from '@sanity/ui' +import {set, unset, StringInputProps} from 'sanity' +import {countryList} from '../schemaTypes/country-list' + +const options = countryList.map((c) => ({value: c.value, payload: c})) + +export function CountryInput(props: StringInputProps) { + const {value, onChange, readOnly} = props + const [query, setQuery] = useState('') + + const filtered = useMemo(() => { + if (!query) return options + const q = query.toLowerCase() + return options.filter((o) => o.payload.title.toLowerCase().includes(q)) + }, [query]) + + const handleSelect = useCallback( + (val: string) => { + onChange(val ? set(val) : unset()) + }, + [onChange], + ) + + const renderOption = useCallback( + (option: (typeof options)[number]) => ( + + {option.payload.title} + + ), + [], + ) + + const selectedTitle = useMemo(() => { + if (!value) return undefined + return countryList.find((c) => c.value === value)?.title + }, [value]) + + return ( + setQuery(q ?? '')} + placeholder="Search for a country…" + renderOption={renderOption} + value={value || ''} + readOnly={readOnly} + openButton + filterOption={() => true} + renderValue={() => selectedTitle || value || ''} + /> + ) +} diff --git a/components/TrackDisplayTitleInput.tsx b/components/TrackDisplayTitleInput.tsx new file mode 100644 index 0000000..04d73f2 --- /dev/null +++ b/components/TrackDisplayTitleInput.tsx @@ -0,0 +1,125 @@ +import React, {useEffect, useMemo, useRef, useState} from 'react' +import {Button, Card, Flex, Stack, Text, TextInput} from '@sanity/ui' +import {set, unset, StringInputProps, useClient, useFormValue} from 'sanity' + +type WorkDoc = { + _id: string + title?: string +} + +function computeTitle(workTitle: string | undefined, movement: string | undefined) { + const wt = (workTitle || '').trim() + const mv = (movement || '').trim() + + if (wt && mv) return `${wt}: ${mv}` + if (wt) return wt + if (mv) return mv + return '' +} + +export function TrackDisplayTitleInput(props: StringInputProps) { + const {value, onChange, readOnly, elementProps} = props + + const workRef = useFormValue(['work', '_ref']) as string | undefined + const movement = useFormValue(['movement']) as string | undefined + + const client = useClient({apiVersion: '2025-01-01'}) + const [workTitle, setWorkTitle] = useState('') + + useEffect(() => { + let cancelled = false + + async function run() { + if (!workRef) { + setWorkTitle('') + return + } + + const doc = await client.fetch(`*[_type == "work" && _id == $id][0]{title}`, { + id: workRef, + }) + + if (!cancelled) setWorkTitle((doc?.title || '').trim()) + } + + run() + return () => { + cancelled = true + } + }, [client, workRef]) + + const computed = useMemo(() => computeTitle(workTitle, movement), [workTitle, movement]) + + const didAutofill = useRef(false) + useEffect(() => { + if (didAutofill.current) return + if (!workRef) return + if (value && String(value).trim()) return + if (!computed) return + if (readOnly) return + + didAutofill.current = true + onChange(set(computed)) + }, [workRef, value, computed, readOnly, onChange]) + + const hasStored = Boolean(value && String(value).trim()) + const inWorkMode = Boolean(workRef) + + if (inWorkMode) { + return ( + + + + + Display title (stored) + + + Generated once from Work/Movement and saved. It will not change automatically if the + Work changes. + + + + + + +