Skip to content

Instantly share code, notes, and snippets.

@aziis98
Created March 31, 2023 16:57
Show Gist options
  • Save aziis98/a7f4b402222a9d01d6827e81f55f0743 to your computer and use it in GitHub Desktop.
Save aziis98/a7f4b402222a9d01d6827e81f55f0743 to your computer and use it in GitHub Desktop.
JSX Tags Input Widget
const normalizeTag = tag => {
return tag
.toLowerCase()
.replace(/\s+/g, ' ')
.replace(/ /g, '-')
.replace(/[^\p{L}0-9\/\-]/gu, '')
}
const InputTags = ({ tags, setTags, availableTags }) => {
availableTags ??= []
const [id] = useState('tags-' + randomHex())
const nextRef = useRef()
const [nextText, setNextText] = useState('')
const onFocus = e => {
if (!e.target.closest('.tags .tag .remove')) {
nextRef.current?.focus()
}
}
const removeTag = tag => {
setTags(tags => tags.filter(t => t !== tag))
}
const addTag = tag => {
setTags(tags => [...tags, tag])
}
const onKeyDown = e => {
if (e.key === 'Backspace' && nextText.length === 0) {
removeTag(tags.at(-1))
}
const trimmed = nextText.trim()
if (e.key === 'Enter' && trimmed.length > 0) {
addTag(normalizeTag(trimmed))
setNextText('')
}
}
return (
<div class="input-tags" onClick={onFocus}>
{tags.map(tag => (
<div class="tag">
<span>{tag}</span>
<span class="remove" onClick={() => removeTag(tag)}>
<i class="fa-solid fa-remove"></i>
</span>
</div>
))}
<input
type="text"
ref={nextRef}
list={id}
value={nextText}
onInput={e => setNextText(e.target.value)}
onKeyDown={onKeyDown}
/>
<datalist id={id}>
{availableTags.map(({ id, label }) => (
<option value={id} label={label} />
))}
</datalist>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment