trptk-sanity/components/TrackDisplayTitleInput.tsx
2026-02-24 12:22:46 +01:00

125 lines
3.4 KiB
TypeScript

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<string>('')
useEffect(() => {
let cancelled = false
async function run() {
if (!workRef) {
setWorkTitle('')
return
}
const doc = await client.fetch<WorkDoc | null>(`*[_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 (
<Card padding={3} radius={2} border>
<Stack space={3}>
<Flex direction="column" gap={2}>
<Text size={1} weight="semibold">
Display title (stored)
</Text>
<Text size={0} muted>
Generated once from Work/Movement and saved. It will not change automatically if the
Work changes.
</Text>
</Flex>
<TextInput
{...elementProps}
value={(value as string) || ''}
readOnly={true}
placeholder={computed || 'Select a Work to generate a title'}
/>
<Flex gap={2} wrap="wrap">
<Button
text={hasStored ? 'Overwrite from Work/Movement' : 'Generate from Work/Movement'}
mode="default"
disabled={readOnly || !computed}
onClick={() => onChange(set(computed))}
/>
<Button
text="Clear"
mode="ghost"
disabled={readOnly || !hasStored}
onClick={() => onChange(unset())}
/>
</Flex>
{!computed && (
<Text size={0} muted>
No computable title yet. Select a Work (and optionally add Movement).
</Text>
)}
</Stack>
</Card>
)
}
return (
<TextInput
{...elementProps}
value={(value as string) || ''}
readOnly={readOnly}
onChange={(e) => {
const next = e.currentTarget.value
onChange(next && next.trim() ? set(next) : unset())
}}
/>
)
}