Last active
December 12, 2023 21:16
-
-
Save AbeCole/648cf14b98bf135695ef8711fa975f2f to your computer and use it in GitHub Desktop.
Sanity - Custom input component for managing Asset metadata
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use with Sanity schema on your existing 'image' field, add a subfields option: | |
// The subfield 'options.field' attribute defines which asset field we are managing | |
// If no 'options.field' is provided, the 'title' attribute will be downcased and used (i.e. Title -> title) | |
// | |
// { | |
// title: 'Image', | |
// name: 'image', | |
// type: 'image', | |
// fields: [ | |
// { | |
// title: 'Title', | |
// type: 'string', | |
// name: 'title', | |
// inputComponent: SanityAssetCustomField, | |
// options: { | |
// isHighlighted: true, | |
// field: 'title' | |
// } | |
// } | |
// ] | |
// } | |
// @ts-nocheck | |
import React, { useEffect, useState, forwardRef } from 'react' | |
import { | |
Label, | |
TextInput, | |
Spinner, | |
Button, | |
Flex, | |
Stack, | |
Card, | |
Text | |
} from '@sanity/ui' | |
import sanityClient from 'part:@sanity/base/client' | |
import PatchEvent, { set, unset } from 'part:@sanity/form-builder/patch-event' | |
const client = sanityClient.withConfig({ apiVersion: '2021-06-07' }) | |
function SanityAssetCustomField({ type, parentValue, onFocus, onChange, ...rest }) { | |
const [value, setValue] = useState('') | |
const [error, setError] = useState(null) | |
const [currentValue, setCurrentValue] = useState(null) | |
const [loading, setLoading] = useState(false) | |
const id = parentValue?.asset?._ref | |
const handleChange = (e) => { | |
setValue(e.target.value) | |
} | |
const targetAssetField = type.options?.field || type.title.toLowerCase() | |
useEffect(() => { | |
if (id && !loading && currentValue === null) { | |
setLoading(true) | |
client | |
.fetch( | |
`*[_type in ['sanity.imageAsset', 'sanity.fileAsset'] && _id == '${id}'][0].${targetAssetField}` | |
) | |
.then((resp) => { | |
setCurrentValue(resp || '') | |
setValue(resp || '') | |
setLoading(false) | |
}) | |
} | |
}, [id, loading, currentValue, targetAssetField]) | |
const handleUpdateValue = () => { | |
if (id) { | |
setLoading(true) | |
client | |
.patch(id) | |
.set({ [targetAssetField]: value }) | |
.commit() | |
.then((resp) => { | |
setCurrentValue(resp[targetAssetField]) | |
setLoading(false) | |
onChange( | |
PatchEvent.from( | |
!resp[targetAssetField] || resp[targetAssetField] === '' ? unset() : set(resp[targetAssetField]) | |
) | |
) | |
}) | |
.catch((err) => { | |
console.error('Error updating value: ', err.message) | |
setError(`An error occured updating the value (${err.message})`) | |
setLoading(false) | |
}) | |
} | |
} | |
return ( | |
<Stack space={1}> | |
<Label size={1} style={{ marginBottom: 10 }}> | |
{type.title} | |
</Label> | |
{error && ( | |
<Card padding={2} radius={2} shadow={1} tone="critical"> | |
<Text align="center" size={1}> | |
{error} | |
</Text> | |
</Card> | |
)} | |
{!parentValue?.asset?._ref ? ( | |
<Card padding={2} radius={2} shadow={1}> | |
<Text align="center" size={1}> | |
You must select an asset first | |
</Text> | |
</Card> | |
) : loading ? ( | |
<Spinner muted /> | |
) : ( | |
<Flex align="center"> | |
<Card flex={2}> | |
<TextInput | |
type="text" | |
onChange={handleChange} | |
value={value || ''} | |
placeholder={currentValue} | |
/> | |
</Card> | |
{value !== currentValue && ( | |
<Button | |
mode="ghost" | |
type="button" | |
onClick={handleUpdateValue} | |
onFocus={onFocus} | |
text={'Save changes'} | |
/> | |
)} | |
</Flex> | |
)} | |
</Stack> | |
) | |
} | |
// eslint-disable-next-line react/display-name | |
export default forwardRef((props, ref) => ( | |
<SanityAssetCustomField {...props} forwardedRef={ref} /> | |
)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment