Created
March 16, 2021 19:37
-
-
Save litewarp/9c1f8ce380367795d0a4dc25c56d7da6 to your computer and use it in GitHub Desktop.
Downshift Combobox Compound Component
This file contains 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
import React from 'react' | |
import { | |
useCombobox, | |
UseComboboxProps, | |
UseComboboxReturnValue, | |
} from 'downshift' | |
type Item = Record<string, any> | |
const ComboboxContext = React.createContext<UseComboboxReturnValue<Item>>(null) | |
export const useComboboxContext = (): UseComboboxReturnValue<Item> => | |
React.useContext(ComboboxContext) | |
interface IComboboxComposition { | |
Container: React.FC<ComboboxContainerProps> | |
Button: React.FC<ComboboxButtonProps> | |
Label: React.FC<ComboboxLabelProps> | |
Menu: React.FC<ComboboxMenuProps> | |
Input: React.FC<ComboboxInputProps> | |
Option: React.FC<ComboboxOptionProps> | |
} | |
const Combobox: React.FC<UseComboboxProps<Item>> & IComboboxComposition = ({ | |
children, | |
...props | |
}) => { | |
const downshiftReturnValue = useCombobox(props) | |
const context = React.useMemo(() => downshiftReturnValue, [ | |
downshiftReturnValue, | |
]) | |
return ( | |
<ComboboxContext.Provider value={context}> | |
{children} | |
</ComboboxContext.Provider> | |
) | |
} | |
type ComboboxButtonProps = { | |
[key: string]: unknown | |
} & React.HTMLProps<HTMLButtonElement> | |
const ComboboxButton: React.FC<ComboboxButtonProps> = ({ | |
children, | |
...props | |
}) => { | |
const { getToggleButtonProps } = useComboboxContext() | |
return ( | |
<button | |
{...getToggleButtonProps(props)} | |
type="button" | |
aria-label={`toggle menu`} | |
> | |
{children} | |
</button> | |
) | |
} | |
type ComboboxMenuProps = { | |
as?: 'div' | |
[key: string]: unknown | |
} & React.HTMLProps<HTMLDivElement | HTMLUListElement> | |
const ComboboxMenu: React.FC<ComboboxMenuProps> = ({ | |
as, | |
children, | |
...props | |
}) => { | |
const { getMenuProps } = useComboboxContext() | |
if (as === 'div') { | |
return <div {...getMenuProps(props)}>{children}</div> | |
} else { | |
return <ul {...getMenuProps(props)}>{children}</ul> | |
} | |
} | |
type ComboboxInputProps = { | |
[key: string]: unknown | |
} & React.HTMLProps<HTMLInputElement> | |
const ComboboxInput: React.FC<ComboboxInputProps> = ({ | |
children, | |
...props | |
}) => { | |
const { getInputProps } = useComboboxContext() | |
return <div {...getInputProps(props)}>{children}</div> | |
} | |
type ComboboxLabelProps = { | |
[key: string]: unknown | |
} & React.HTMLProps<HTMLLabelElement> | |
const ComboboxLabel: React.FC<ComboboxLabelProps> = ({ | |
children, | |
...props | |
}) => { | |
const { getLabelProps } = useComboboxContext() | |
return <label {...getLabelProps(props)}>{children}</label> | |
} | |
type ComboboxContainerProps = { | |
[key: string]: unknown | |
} & React.HTMLProps<HTMLDivElement> | |
const ComboboxContainer: React.FC<ComboboxContainerProps> = ({ | |
children, | |
...props | |
}) => { | |
const { getComboboxProps } = useComboboxContext() | |
return <div {...getComboboxProps(props)}>{children}</div> | |
} | |
type ComboboxOptionProps = { | |
[key: string]: unknown | |
as?: 'div' | |
item: Item | |
index: number | |
} & React.HTMLProps<HTMLDivElement | HTMLLIElement> | |
const ComboboxOption: React.FC<ComboboxOptionProps> = ({ | |
as, | |
children, | |
...props | |
}) => { | |
const { getItemProps } = useComboboxContext() | |
if (as === 'div') { | |
return <div {...getItemProps(props)}>{children}</div> | |
} else { | |
return <li {...getItemProps(props)}>{children}</li> | |
} | |
} | |
// ---- | |
Combobox.Container = ComboboxContainer | |
Combobox.Button = ComboboxButton | |
Combobox.Label = ComboboxLabel | |
Combobox.Menu = ComboboxMenu | |
Combobox.Input = ComboboxInput | |
Combobox.Option = ComboboxOption | |
export { Combobox } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment