Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: drop Input.Control component #64

Merged
merged 1 commit into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/react/src/components/form/Form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ export const Default: StoryFn<FormProps> = (args) => (
<Form {...args}>
<Form.Group name="email-address">
<Form.Label>Email address</Form.Label>
<Input>
<Input.Group>
<Input.Addon>$</Input.Addon>
<Input.Control placeholder="Username" type="text" />
<Input placeholder="Username" type="text" />
<Input.Addon>USD</Input.Addon>
</Input>
<Form.Caption>invalid email address provided</Form.Caption>
</Input.Group>

<Form.Caption>A unique string of characters that identifies an email account</Form.Caption>
</Form.Group>
</Form>
)
Expand Down
27 changes: 12 additions & 15 deletions packages/react/src/components/input/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import type { InputProps } from '@/components/input'
import type { InputGroupProps, InputProps } from '@/components/input'
import type { Meta, StoryFn } from '@storybook/react'

import { input } from '@giantnodes/theme'

import { Input } from '@/components/input'

const Component: Meta<typeof Input> = {
type InputComponentProps = React.ComponentProps<typeof Input>

const Component: Meta<InputComponentProps> = {
title: 'Components/Input',
component: Input,
argTypes: {
size: {
control: { type: 'select' },
options: ['xs', 'sm', 'md', 'lg', 'xl'],
},
status: {
control: { type: 'select' },
options: ['neutral', 'success', 'info', 'warning', 'danger'],
options: ['neutral', 'brand', 'success', 'info', 'warning', 'danger'],
},
},
}
Expand All @@ -23,27 +26,21 @@ const defaultProps = {
...input.defaultVariants,
}

export const Default: StoryFn<InputProps> = (args) => (
<Input {...args}>
<Input.Addon>$</Input.Addon>
<Input.Control placeholder="Username" type="text" />
<Input.Addon>USD</Input.Addon>
</Input>
)
export const Default: StoryFn<InputProps> = (args) => <Input placeholder="Username" type="text" {...args} />

Default.args = {
...defaultProps,
}

export const Disabled: StoryFn<InputProps> = (args) => (
<Input {...args}>
export const UsingGroup: StoryFn<InputGroupProps> = (args) => (
<Input.Group {...args}>
<Input.Addon>$</Input.Addon>
<Input.Control disabled placeholder="Username" type="text" />
<Input placeholder="Username" type="text" />
<Input.Addon>USD</Input.Addon>
</Input>
</Input.Group>
)

Disabled.args = {
UsingGroup.args = {
...defaultProps,
}

Expand Down
58 changes: 38 additions & 20 deletions packages/react/src/components/input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,68 @@
import type * as Polymophic from '@/utilities/polymorphic'
import type { InputVariantProps } from '@giantnodes/theme'
import type { InputProps } from 'react-aria-components'

import React from 'react'
import { Input } from 'react-aria-components'

import { useFormGroupContext } from '@/components/form/use-form-group.hook'
import InputAddon from '@/components/input/InputAddon'
import InputControl from '@/components/input/InputControl'
import { InputContext, useInput } from '@/components/input/use-input.hook'
import InputGroup from '@/components/input/InputGroup'
import { useInput, useInputContext } from '@/components/input/use-input.hook'

const __ELEMENT_TYPE__ = 'div'
const __ELEMENT_TYPE__ = 'input'

type ComponentOwnProps = InputVariantProps

type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>
type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
ComponentOwnProps
>

type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => React.ReactNode

const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
const { as, children, className, status, size, variant, transparent, ...rest } = props
<TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>,
ref: Polymophic.Ref<TElement>
) => {
const { as, children, className, status, size, variant, ...rest } = props

const Element = as ?? __ELEMENT_TYPE__
const Element = as || Input

const context = useInput({ status, size, variant, transparent })
const context = useInputContext()
const { slots } = useInput({
status: status ?? context?.status,
size: size ?? context?.size,
variant: variant ?? context?.variant,
})

const component = React.useMemo<React.ComponentPropsWithoutRef<typeof __ELEMENT_TYPE__>>(
const group = useFormGroupContext()

const component = React.useMemo<InputProps>(
() => ({
className: context.slots.input({ className }),
name: group?.name,
onChange: group?.onChange,
onBlur: group?.onBlur,
className: slots.input({ className: className?.toString() }),
...group?.fieldProps,
...rest,
}),
[className, context.slots, rest]
[className, group?.fieldProps, group?.name, group?.onBlur, group?.onChange, rest, slots]
)

return (
<InputContext.Provider value={context}>
<Element {...component} ref={ref}>
{children}
</Element>
</InputContext.Provider>
<Element {...component} ref={(group?.ref as React.RefObject<HTMLInputElement>) ?? ref}>
{children}
</Element>
)
}
)

export type { ComponentOwnProps as InputProps }
export type { ComponentOwnProps as InputOwnProps, ComponentProps as InputProps }
export default Object.assign(Component, {
Addon: InputAddon,
Control: InputControl,
Group: InputGroup,
})
18 changes: 12 additions & 6 deletions packages/react/src/components/input/InputAddon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ const __ELEMENT_TYPE__ = 'span'

type ComponentOwnProps = {}

type ComponentProps<T extends React.ElementType> = Polymophic.ComponentPropsWithRef<T, ComponentOwnProps>
type ComponentProps<TElement extends React.ElementType = typeof __ELEMENT_TYPE__> = Polymophic.ComponentPropsWithRef<
TElement,
ComponentOwnProps
>

type ComponentType = <T extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<T>
type ComponentType = <TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>
) => React.ReactNode

const Component: ComponentType = React.forwardRef(
<T extends React.ElementType = typeof __ELEMENT_TYPE__>(props: ComponentProps<T>, ref: Polymophic.Ref<T>) => {
<TElement extends React.ElementType = typeof __ELEMENT_TYPE__>(
props: ComponentProps<TElement>,
ref: Polymophic.Ref<TElement>
) => {
const { as, children, className, ...rest } = props

const Element = as ?? __ELEMENT_TYPE__
const Element = as || __ELEMENT_TYPE__

const { slots } = useInputContext()

Expand All @@ -38,5 +44,5 @@ const Component: ComponentType = React.forwardRef(
}
)

export type { ComponentOwnProps as InputAddonProps }
export type { ComponentProps as InputAddonProps, ComponentOwnProps as InputAddonOwnProps }
export default Component
50 changes: 0 additions & 50 deletions packages/react/src/components/input/InputControl.tsx

This file was deleted.

53 changes: 53 additions & 0 deletions packages/react/src/components/input/InputGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type * as Polymophic from '@/utilities/polymorphic'
import type { InputVariantProps } from '@giantnodes/theme'
import type { GroupProps } from 'react-aria-components'

import React from 'react'
import { Group } from 'react-aria-components'

import { InputContext, useInput } from '@/components/input/use-input.hook'

const __ELEMENT_TYPE__ = 'div'

type ComponentOwnProps = GroupProps & InputVariantProps

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, status, size, variant, ...rest } = props

const Element = as || Group

const context = useInput({ status, size, variant })

const component = React.useMemo<GroupProps>(
() => ({
className: context.slots.group({ className: className?.toString() }),
...rest,
}),
[className, context.slots, rest]
)

return (
<InputContext.Provider value={context}>
<Element {...component} ref={ref}>
{children}
</Element>
</InputContext.Provider>
)
}
)

export type { ComponentProps as InputGroupProps, ComponentOwnProps as InputGroupOwnProps }
export default Component
6 changes: 3 additions & 3 deletions packages/react/src/components/input/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type { InputProps } from '@/components/input/Input'
export type { InputAddonProps } from '@/components/input/InputAddon'
export type { InputControlProps } from '@/components/input/InputControl'
export type * from '@/components/input/Input'
export type * from '@/components/input/InputAddon'
export type * from '@/components/input/InputGroup'

export { default as Input } from '@/components/input/Input'
9 changes: 6 additions & 3 deletions packages/react/src/components/input/use-input.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ type UseInputProps = InputVariantProps
type UseInputReturn = ReturnType<typeof useInput>

export const useInput = (props: UseInputProps) => {
const { status, size, transparent, variant } = props
const { status, size, variant } = props

const slots = React.useMemo(() => input({ status, size, transparent, variant }), [status, size, transparent, variant])
const slots = React.useMemo(() => input({ status, size, variant }), [status, size, variant])

return {
slots,
status,
size,
variant,
}
}

export const [InputContext, useInputContext] = createContext<UseInputReturn>({
name: 'InputContext',
strict: true,
strict: false,
errorMessage: 'useInputContext: `context` is undefined. Seems you forgot to wrap component within <Input />',
})
Loading