-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dbaaa3e
commit e5b3366
Showing
9 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
82 changes: 82 additions & 0 deletions
82
packages/react/src/components/combo-box/ComboBox.stories.tsx
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import type { ComboBoxProps } from '@/components/combo-box' | ||
import type { Meta, StoryFn } from '@storybook/react' | ||
|
||
import { combobox } from '@giantnodes/theme' | ||
|
||
import { Typography } from '../typography' | ||
|
||
import { Avatar } from '@/components/avatar' | ||
import { ComboBox } from '@/components/combo-box' | ||
import { Input } from '@/components/input' | ||
|
||
const Component: Meta<typeof ComboBox> = { | ||
title: 'Components/ComboBox', | ||
component: ComboBox, | ||
argTypes: { | ||
size: { | ||
control: { type: 'select' }, | ||
}, | ||
status: { | ||
control: { type: 'select' }, | ||
}, | ||
variant: { | ||
control: { type: 'select' }, | ||
}, | ||
}, | ||
} | ||
|
||
const defaultProps = { | ||
...combobox.defaultVariants, | ||
} | ||
|
||
const people = [ | ||
{ id: 1, name: 'Raymond Chappell' }, | ||
{ id: 2, name: 'David Bolden' }, | ||
{ id: 3, name: 'Charles Marcano' }, | ||
{ id: 4, name: 'Jeanne Livesay' }, | ||
{ id: 5, name: 'David Bolden' }, | ||
] | ||
|
||
export const Default: StoryFn<ComboBoxProps<object>> = (args) => ( | ||
<ComboBox {...args}> | ||
<Input.Group> | ||
<Input placeholder="People" type="text" /> | ||
</Input.Group> | ||
|
||
<ComboBox.Popover> | ||
<ComboBox.List items={people}>{(item) => <ComboBox.Item key={item.id}>{item.name}</ComboBox.Item>}</ComboBox.List> | ||
</ComboBox.Popover> | ||
</ComboBox> | ||
) | ||
|
||
Default.args = { | ||
...defaultProps, | ||
} | ||
|
||
export const Custom: StoryFn<ComboBoxProps<object>> = (args) => ( | ||
<ComboBox {...args}> | ||
<Input.Group> | ||
<Input placeholder="People" type="text" /> | ||
</Input.Group> | ||
|
||
<ComboBox.Popover> | ||
<ComboBox.List items={people}> | ||
{(item) => ( | ||
<ComboBox.Item key={item.id} className="flex items-center gap-2" textValue={item.name}> | ||
<Avatar size="xs"> | ||
<Avatar.Image alt={item.name} src={`https://api.dicebear.com/8.x/personas/svg?seed=${item.name}`} /> | ||
</Avatar> | ||
|
||
<Typography.Text slot="label">{item.name}</Typography.Text> | ||
</ComboBox.Item> | ||
)} | ||
</ComboBox.List> | ||
</ComboBox.Popover> | ||
</ComboBox> | ||
) | ||
|
||
Custom.args = { | ||
...defaultProps, | ||
} | ||
|
||
export default 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type * as Polymophic from '@/utilities/polymorphic' | ||
import type { ComboBoxVariantProps } from '@giantnodes/theme' | ||
import type { ComboBoxProps } from 'react-aria-components' | ||
|
||
import React from 'react' | ||
import { ComboBox } from 'react-aria-components' | ||
|
||
import ComboBoxItem from '@/components/combo-box/ComboBoxItem' | ||
import ComboBoxList from '@/components/combo-box/ComboBoxList' | ||
import ComboBoxPopover from '@/components/combo-box/ComboBoxPopover' | ||
import { ComboBoxContext, useComboBox } from '@/components/combo-box/use-combo-box.hook' | ||
|
||
const __ELEMENT_TYPE__ = 'div' | ||
|
||
type ComponentOwnProps<TData extends object> = ComboBoxProps<TData> & ComboBoxVariantProps | ||
|
||
type ComponentProps< | ||
TData extends object, | ||
TElement extends React.ElementType = typeof __ELEMENT_TYPE__, | ||
> = Polymophic.ComponentPropsWithRef<TElement, ComponentOwnProps<TData>> | ||
|
||
type ComponentType = <TData extends object, TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TData, TElement> | ||
) => React.ReactNode | ||
|
||
const Component: ComponentType = React.forwardRef( | ||
<TData extends object, TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TData, TElement>, | ||
ref: Polymophic.Ref<TElement> | ||
) => { | ||
const { as, children, className, size, status, ...rest } = props | ||
|
||
const Element = as || ComboBox | ||
|
||
const context = useComboBox({ size, status }) | ||
|
||
const component = React.useMemo<ComboBoxProps<TData>>( | ||
() => ({ | ||
className: context.slots.combobox({ className: className?.toString() }), | ||
...rest, | ||
}), | ||
[className, context.slots, rest] | ||
) | ||
|
||
return ( | ||
<ComboBoxContext.Provider value={context}> | ||
<Element {...component} ref={ref}> | ||
{children} | ||
</Element> | ||
</ComboBoxContext.Provider> | ||
) | ||
} | ||
) | ||
|
||
export type { ComponentOwnProps as ComboBoxOwnProps, ComponentProps as ComboBoxProps } | ||
export default Object.assign(Component, { | ||
Popover: ComboBoxPopover, | ||
List: ComboBoxList, | ||
Item: ComboBoxItem, | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type * as Polymophic from '@/utilities/polymorphic' | ||
import type { ListBoxItemProps } from 'react-aria-components' | ||
|
||
import React from 'react' | ||
import { ListBoxItem } from 'react-aria-components' | ||
|
||
import { useComboBoxContext } from '@/components/combo-box/use-combo-box.hook' | ||
|
||
const __ELEMENT_TYPE__ = 'div' | ||
|
||
type ComponentOwnProps = ListBoxItemProps | ||
|
||
type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef< | ||
TElement, | ||
ComponentOwnProps | ||
> | ||
|
||
type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TElement> | ||
) => React.ReactNode | ||
|
||
const Component: ComponentType = React.forwardRef( | ||
<TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TElement>, | ||
ref: Polymophic.Ref<TElement> | ||
) => { | ||
const { as, children, className, ...rest } = props | ||
|
||
const Element = as || ListBoxItem | ||
|
||
const { slots } = useComboBoxContext() | ||
|
||
const component = React.useMemo<ListBoxItemProps>( | ||
() => ({ | ||
className: slots.item({ className: className?.toString() }), | ||
...rest, | ||
}), | ||
[className, rest, slots] | ||
) | ||
|
||
return ( | ||
<Element {...component} ref={ref}> | ||
{children} | ||
</Element> | ||
) | ||
} | ||
) | ||
|
||
export type { ComponentOwnProps as ComboBoxItemOwnProps, ComponentProps as ComboBoxItemProps } | ||
export default 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type * as Polymophic from '@/utilities/polymorphic' | ||
import type { ListBoxProps } from 'react-aria-components' | ||
|
||
import React from 'react' | ||
import { ListBox } from 'react-aria-components' | ||
|
||
import { useComboBoxContext } from '@/components/combo-box/use-combo-box.hook' | ||
|
||
const __ELEMENT_TYPE__ = 'div' | ||
|
||
type ComponentOwnProps<TData extends object> = ListBoxProps<TData> | ||
|
||
type ComponentProps< | ||
TData extends object, | ||
TElement extends React.ElementType = typeof __ELEMENT_TYPE__, | ||
> = Polymophic.ComponentPropsWithRef<TElement, ComponentOwnProps<TData>> | ||
|
||
type ComponentType = <TData extends object, TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TData, TElement> | ||
) => React.ReactNode | ||
|
||
const Component: ComponentType = React.forwardRef( | ||
<D extends object, TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<D, TElement>, | ||
ref: Polymophic.Ref<TElement> | ||
) => { | ||
const { as, children, className, ...rest } = props | ||
|
||
const Element = as || ListBox | ||
|
||
const { slots } = useComboBoxContext() | ||
|
||
const component = React.useMemo<ListBoxProps<D>>( | ||
() => ({ | ||
className: slots.list({ className: className?.toString() }), | ||
...rest, | ||
}), | ||
[className, rest, slots] | ||
) | ||
|
||
return ( | ||
<Element {...component} ref={ref}> | ||
{children} | ||
</Element> | ||
) | ||
} | ||
) | ||
|
||
export type { ComponentOwnProps as ComboBoxListOwnProps, ComponentProps as ComboBoxListProps } | ||
export default Component |
50 changes: 50 additions & 0 deletions
50
packages/react/src/components/combo-box/ComboBoxPopover.tsx
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type * as Polymophic from '@/utilities/polymorphic' | ||
import type { PopoverProps } from 'react-aria-components' | ||
|
||
import React from 'react' | ||
import { Popover } from 'react-aria-components' | ||
|
||
import { useComboBoxContext } from '@/components/combo-box/use-combo-box.hook' | ||
|
||
const __ELEMENT_TYPE__ = 'div' | ||
|
||
type ComponentOwnProps = PopoverProps | ||
|
||
type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef< | ||
TElement, | ||
ComponentOwnProps | ||
> | ||
|
||
type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TElement> | ||
) => React.ReactNode | ||
|
||
const Component: ComponentType = React.forwardRef( | ||
<TElement extends React.ElementType = typeof __ELEMENT_TYPE__>( | ||
props: ComponentProps<TElement>, | ||
ref: Polymophic.Ref<TElement> | ||
) => { | ||
const { as, children, className, ...rest } = props | ||
|
||
const Element = as || Popover | ||
|
||
const { slots } = useComboBoxContext() | ||
|
||
const component = React.useMemo<PopoverProps>( | ||
() => ({ | ||
className: slots.popover({ className: className?.toString() }), | ||
...rest, | ||
}), | ||
[className, rest, slots] | ||
) | ||
|
||
return ( | ||
<Element {...component} ref={ref}> | ||
{children} | ||
</Element> | ||
) | ||
} | ||
) | ||
|
||
export type { ComponentOwnProps as ComboBoxPopoverOwnProps, ComponentProps as ComboBoxPopoverProps } | ||
export default 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export type * from '@/components/combo-box/ComboBox' | ||
export type * from '@/components/combo-box/ComboBoxItem' | ||
export type * from '@/components/combo-box/ComboBoxList' | ||
export type * from '@/components/combo-box/ComboBoxPopover' | ||
|
||
export { default as ComboBox } from '@/components/combo-box/ComboBox' |
26 changes: 26 additions & 0 deletions
26
packages/react/src/components/combo-box/use-combo-box.hook.ts
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import type { ComboBoxVariantProps } from '@giantnodes/theme' | ||
|
||
import { combobox } from '@giantnodes/theme' | ||
import React from 'react' | ||
|
||
import { createContext } from '@/utilities/context' | ||
|
||
type UseComboBoxProps = ComboBoxVariantProps | ||
|
||
type UseComboBoxReturn = ReturnType<typeof useComboBox> | ||
|
||
export const useComboBox = (props: UseComboBoxProps) => { | ||
const { size, status } = props | ||
|
||
const slots = React.useMemo(() => combobox({ size, status }), [size, status]) | ||
|
||
return { | ||
slots, | ||
} | ||
} | ||
|
||
export const [ComboBoxContext, useComboBoxContext] = createContext<UseComboBoxReturn>({ | ||
name: 'ComboBoxContext', | ||
strict: true, | ||
errorMessage: 'useComboBox: `context` is undefined. Seems you forgot to wrap component within <ComboBox />', | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import type { VariantProps } from 'tailwind-variants' | ||
|
||
import { tv } from 'tailwind-variants' | ||
|
||
export const combobox = tv({ | ||
slots: { | ||
combobox: ['group flex flex-col gap-1'], | ||
popover: ['bg-foreground', 'border border-solid border-partition', 'rounded-md', 'w-[--trigger-width]'], | ||
list: ['flex flex-col gap-1', 'p-1', 'outline-none'], | ||
item: [ | ||
'flex tems-center', | ||
'px-2 py-1', | ||
'rounded-md', | ||
'cursor-pointer', | ||
'outline-none', | ||
'text-content', | ||
'overflow-hidden', | ||
'disabled:opacity-50 disabled:cursor-default', | ||
], | ||
}, | ||
variants: { | ||
size: { | ||
xs: { | ||
item: ['text-xs'], | ||
}, | ||
sm: { | ||
item: ['text-sm'], | ||
}, | ||
md: { | ||
item: ['text-base'], | ||
}, | ||
lg: { | ||
item: ['text-lg'], | ||
}, | ||
}, | ||
status: { | ||
neutral: { | ||
item: ['hover:bg-middleground'], | ||
}, | ||
brand: { | ||
item: ['hover:bg-brand/20'], | ||
}, | ||
success: { | ||
item: ['hover:bg-success/20'], | ||
}, | ||
info: { | ||
item: ['hover:bg-info/20'], | ||
}, | ||
warning: { | ||
item: ['hover:bg-warning/20'], | ||
}, | ||
danger: { | ||
item: ['hover:bg-danger/20'], | ||
}, | ||
}, | ||
}, | ||
defaultVariants: { | ||
size: 'md', | ||
status: 'neutral', | ||
}, | ||
}) | ||
|
||
export type ComboBoxVariantProps = VariantProps<typeof combobox> |
Oops, something went wrong.