Skip to content

Commit

Permalink
refactor: drop Input.Control component (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
PHILLIPS71 authored May 3, 2024
1 parent 0f5eb50 commit dbaaa3e
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 136 deletions.
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

0 comments on commit dbaaa3e

Please sign in to comment.