Skip to content

Instantly share code, notes, and snippets.

@dennemark
Created February 21, 2023 14:12
Show Gist options
  • Save dennemark/428e0b2f612fbc842caf2797268f374f to your computer and use it in GitHub Desktop.
Save dennemark/428e0b2f612fbc842caf2797268f374f to your computer and use it in GitHub Desktop.
chakra combobox
import { Box, Input, Stack, Divider} from '@chakra-ui/react'
import * as combobox from '@zag-js/combobox'
import { normalizeProps, useMachine } from '@zag-js/react'
import { useEffect, useId, useState } from 'react'
const comboboxData = [
{ label: "Zambia", code: "ZA" },
{ label: "Benin", code: "BN" },
//...
]
export function Combobox() {
const [options, setOptions] = useState<{ label: string, code: string }[]>(comboboxData)
const [selected, setSelected] = useState<number>(-1)
const [state, send] = useMachine(
combobox.machine({
id: useId(),
onOpen() {
setOptions(comboboxData)
},
onSelect: (details) => {
/**
* state within useMachine is not updated.
* in here ´options´ always has the initial value
* and not the updated one.
* there is probably a way, but in this case i just
* set state of index of options, and get the
* most up to date option in useEffect below
*/
const index = details.value ? parseInt(details.value) : undefined;
if (index) {
setSelected(() => index);
}
},
onInputChange: async ({ value }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(value.toLowerCase()),
)
setOptions(filtered.length > 0 ? filtered : comboboxData)
},
})
)
const api = combobox.connect(state, send, normalizeProps)
const { size, ...inputProps } = api.inputProps
useEffect(() => {
if (selected > -1 && options.length > selected) {
console.log(options[selected])
}
}, [selected, options])
return (
<Box>
<Box {...api.rootProps}>
<Box {...api.controlProps}>
<Input {...inputProps} />
</Box>
</Box>
<Box {...api.positionerProps} zIndex={1} layerStyle="card">
{options && options.length > 0 && (
<Stack {...api.contentProps} spacing={0} divider={<Divider />}>
{options.map((item, index) => (
<Box
p={1}
rounded={0}
key={`${item.label}:${index}`}
{...api.getOptionProps({
label: item.label,
value: index.toString(),
index,
disabled: false,
})}
_highlighted={{
bgColor: 'gray.200',
}}
>
{item.label}
</Box>
))}
</Stack>
)}
</Box>
</Box>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment