Skip to content

Instantly share code, notes, and snippets.

@rosnovsky
Last active March 14, 2022 09:01
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rosnovsky/fdfb7874d6020bf7a61af2f0f11ad144 to your computer and use it in GitHub Desktop.
Save rosnovsky/fdfb7874d6020bf7a61af2f0f11ad144 to your computer and use it in GitHub Desktop.
/*
Tag Schema:
export default {
type: "object",
name: "tag",
fields: [
{
name: "value",
type: "string"
},
{
name: "label",
type: "string"
}
]
}
Include in your document like this:
{
name: 'tags',
title: 'Tags',
type: 'array',
description: 'Add tags that describe this photo.',
inputComponent: sanityTagAutocomplete,
of:[{type: "tag"}],
options: {
layout: "tags",
isHighlighted: true,
}
}
*/
import React, {useState, useEffect} from 'react'
import CreatableSelect from 'react-select/creatable';
const sanityClient = require('@sanity/client')
import PatchEvent, {set, unset} from 'part:@sanity/form-builder/patch-event'
// We need access to document._id. Here's how:
import {withDocument} from 'part:@sanity/form-builder'
// @ts-ignore
const client = sanityClient({
projectId: 'process.env.SANITY_PROJECT_ID',
dataset: 'production',
token: process.env.SANITY_TOKEN,
useCdn: false // important to set to `false`: we need our tags fresh
})
const createPatchFrom = value => PatchEvent.from(value === '' ? unset() : set(value))
const CreatableMulti = (props) => {
const [uniqueImageTags, setUniqueImageTags] = useState([])
const [isLoading, setIsLoading] = useState(false)
const [selected, setSelected] = useState([]);
// On component load, let's fetch all tags from all images and only keep unique ones
useEffect(() => {
// Component is loading! Hands off!
setIsLoading(true)
const query = '*[_type == "photo"] {photo}'
const fetchTags = async () => {
const allTags = []
const result = client.fetch(query)
.then(photos => {
const fillTags = photos.forEach(photo => {
allTags.push(photo.photo.tags)
})
// At this point, we have an array of arrays. Let's flatten this sucker!
// @ts-ignore
const flatTags = allTags.flat();
// Now, let's create a new array that only includes unique tags
const uniqueTags = Array.from(new Set(flatTags.map(tag => tag.value)))
.map(tagValue => {
return {
value: tagValue,
label: tagValue
}
})
setUniqueImageTags(uniqueTags)
return fillTags;
})
return result
}
// Ok, now let's populate the dropdown with tags already assigned.
const setSelectedTags = async () => {
setSelected(props.document.photo.tags)
}
fetchTags();
setSelectedTags();
// Component no longer loading
setIsLoading(false)
}, [])
// Here we handle change to the tags when this change does not involve creating a new tag
const handleChange = (newValue, actionMeta) => {
setSelected(newValue)
props.onChange(createPatchFrom(newValue))
};
// Ok, here's some fun: here we handle changes that involve creating new tags and
// populating these new options into selected tags and all tags
const createOption = (newValue) => {
const newSelected = selected
newSelected.push({value: newValue, label: newValue})
setSelected(newSelected)
// New tags need to be commited to Sanity so that we can reuse them elsewhere
client
.patch(props.document._id)
.setIfMissing({tags: []}) // shouldn't be a factor, but who knows? 🤷
.append('tags',
[{value: newValue, label: newValue}]
)
.commit()
}
return (
<>
<h4>Tags</h4>
<CreatableSelect
disabled={isLoading}
isLoading={isLoading}
value={"" || selected}
isMulti
onChange={handleChange}
onCreateOption={createOption}
options={"" || uniqueImageTags}
/>
</>
);
}
export default withDocument(CreatableMulti)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment