Skip to content

Commit

Permalink
feat(input): use input primitive
Browse files Browse the repository at this point in the history
  • Loading branch information
andresz1 committed Jun 21, 2023
1 parent ccda9e3 commit f73ae2b
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 103 deletions.
106 changes: 40 additions & 66 deletions packages/components/input/src/Input.styles.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,45 @@
import { cva, VariantProps } from 'class-variance-authority'

export const inputStyles = cva(
[
'h-sz-44',
'box-border',
'outline-none',
'text-ellipsis',
'peer',
'text-body-1',
'cursor-auto',
'caret-neutral',
'appearance-none',
'autofill:shadow-surface',
'autofill:shadow-[inset_0_0_0px_1000px]',
'bg-surface',
'text-on-surface',
'disabled:bg-on-surface/dim-5',
'disabled:text-on-surface/dim-3',
'disabled:cursor-not-allowed',
'disabled:border-on-surface/dim-3',
],
{
variants: {
intent: {
none: [''],
neutral: [
'border-sm',
'border-outline',
'ring-outline-high',
'hover:border-outline-high',
'focus:ring-1',
'focus:border-outline-high',
],
success: ['border-sm', 'border-success', 'ring-success', 'focus:ring-1'],
alert: ['border-sm', 'border-alert', 'ring-alert', 'focus:ring-1'],
error: ['border-sm', 'border-error', 'ring-error', 'focus:ring-1'],
},
isLeftElementVisible: {
true: ['pl-3xl'],
false: [],
},
isRightElementVisible: {
true: ['pr-3xl'],
false: [],
},
isLeftAddonVisible: {
true: ['pl-md', 'border-l-none', 'rounded-l-none'],
false: ['rounded-l-lg'],
},
isRightAddonVisible: {
true: ['pr-md', 'border-r-none', 'rounded-r-none'],
false: ['rounded-r-lg'],
},
export const inputStyles = cva([], {
variants: {
intent: {
neutral: [],
success: [],
alert: [],
error: [],
},
isGrouped: {
true: [],
false: ['border-sm', 'focus:ring-1'],
},
},
compoundVariants: [
{
intent: 'neutral',
isGrouped: false,
class: [
'border-outline',
'ring-outline-high',
'hover:border-outline-high',
'focus:border-outline-high',
],
},
{
intent: 'success',
isGrouped: false,
class: ['border-success', 'ring-success'],
},
compoundVariants: [
{
isLeftElementVisible: false,
isLeftAddonVisible: false,
class: 'pl-lg',
},
{
isRightElementVisible: false,
isRightAddonVisible: false,
class: 'pr-lg',
},
],
}
)
{
intent: 'alert',
isGrouped: false,
class: ['border-alert', 'ring-alert'],
},
{
intent: 'error',
isGrouped: false,
class: ['border-error', 'ring-error'],
},
],
})

export type InputStylesProps = VariantProps<typeof inputStyles>
47 changes: 13 additions & 34 deletions packages/components/input/src/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,27 @@
import { useFormFieldState } from '@spark-ui/form-field'
import { ComponentPropsWithoutRef, forwardRef } from 'react'
import { forwardRef } from 'react'

import { inputStyles, InputStylesProps } from './Input.styles'
import { useInputGroup } from './InputGroupContext'
import { InputPrimitive, InputPrimitiveProps } from './InputPrimitive'

export interface InputProps
extends ComponentPropsWithoutRef<'input'>,
Omit<
InputStylesProps,
| 'isDisabled'
| 'isLeftAddonVisible'
| 'isRightAddonVisible'
| 'isLeftElementVisible'
| 'isRightElementVisible'
> {}
export interface InputProps extends InputPrimitiveProps, InputStylesProps {}

export const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, intent: intentProp = 'neutral', disabled: disabledProp, ...others }, ref) => {
(
{ className: classNameProp, intent: intentProp = 'neutral', disabled: disabledProp, ...others },
ref
) => {
const field = useFormFieldState()
const group = useInputGroup() || {}

const { isLeftAddonVisible, isRightAddonVisible, isLeftElementVisible, isRightElementVisible } =
group
const { id, name, isInvalid, isRequired, description } = field
const intent = isInvalid && intentProp !== 'none' ? 'error' : intentProp
const isDisabled = group.isDisabled ?? disabledProp
const group = useInputGroup()
const { isInvalid } = field
const isGrouped = !!group
const intent = isInvalid ? 'error' : intentProp

return (
<input
<InputPrimitive
ref={ref}
id={id}
name={name}
className={inputStyles({
className,
intent,
isLeftAddonVisible: !!isLeftAddonVisible,
isRightAddonVisible: !!isRightAddonVisible,
isLeftElementVisible: !!isLeftElementVisible,
isRightElementVisible: !!isRightElementVisible,
})}
disabled={isDisabled}
required={isRequired}
aria-describedby={description}
aria-invalid={isInvalid}
className={inputStyles({ className: classNameProp, intent, isGrouped })}
{...others}
/>
)
Expand Down
2 changes: 1 addition & 1 deletion packages/components/input/src/InputGroup.styles.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cva, VariantProps } from 'class-variance-authority'

export const inputGroupStyles = cva(['relative', 'inline-flex'])
export const inputGroupStyles = cva(['relative', 'inline-flex', 'w-full'])

export type InputGroupStylesProps = VariantProps<typeof inputGroupStyles>
11 changes: 9 additions & 2 deletions packages/components/input/src/InputGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable complexity */
import { useFormFieldState } from '@spark-ui/form-field'
import {
Children,
cloneElement,
Expand All @@ -19,8 +21,13 @@ export interface InputGroupProps extends ComponentPropsWithoutRef<'div'>, InputG
}

export const InputGroup = forwardRef<HTMLDivElement, PropsWithChildren<InputGroupProps>>(
({ className, children: childrenProp, intent = 'neutral', isDisabled, ...others }, ref) => {
(
{ className, children: childrenProp, intent: intentProp = 'neutral', isDisabled, ...others },
ref
) => {
const { isInvalid } = useFormFieldState()
const children = Children.toArray(childrenProp).filter(isValidElement)
const intent = isInvalid ? 'error' : intentProp

const getDisplayName = (element?: ReactElement) => {
return element ? (element.type as FC).displayName : ''
Expand Down Expand Up @@ -68,7 +75,7 @@ export const InputGroup = forwardRef<HTMLDivElement, PropsWithChildren<InputGrou

{isInput ? (
<>
{cloneElement(input as ReactElement, { intent: 'none' })}
{input}

<InputContainer intent={intent} />

Expand Down
58 changes: 58 additions & 0 deletions packages/components/input/src/InputPrimitive.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { cva, VariantProps } from 'class-variance-authority'

export const inputPrimitiveStyles = cva(
[
'peer',
'box-border',
'h-sz-44',
'w-full',
'outline-none',
'appearance-none',
'bg-surface',
'text-on-surface',
'text-body-1',
'text-ellipsis',
'cursor-auto',
'caret-neutral',
'autofill:shadow-surface',
'autofill:shadow-[inset_0_0_0px_1000px]',
'disabled:bg-on-surface/dim-5',
'disabled:text-on-surface/dim-3',
'disabled:cursor-not-allowed',
'disabled:border-on-surface/dim-3',
],
{
variants: {
isLeftElementVisible: {
true: ['pl-3xl'],
false: [],
},
isRightElementVisible: {
true: ['pr-3xl'],
false: [],
},
isLeftAddonVisible: {
true: ['pl-md', 'border-l-none', 'rounded-l-none'],
false: ['rounded-l-lg'],
},
isRightAddonVisible: {
true: ['pr-md', 'border-r-none', 'rounded-r-none'],
false: ['rounded-r-lg'],
},
},
compoundVariants: [
{
isLeftElementVisible: false,
isLeftAddonVisible: false,
class: 'pl-lg',
},
{
isRightElementVisible: false,
isRightAddonVisible: false,
class: 'pr-lg',
},
],
}
)

export type InputPrimitiveStylesProps = VariantProps<typeof inputPrimitiveStyles>
49 changes: 49 additions & 0 deletions packages/components/input/src/InputPrimitive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useFormFieldState } from '@spark-ui/form-field'
import { ComponentPropsWithoutRef, forwardRef } from 'react'

import { useInputGroup } from './InputGroupContext'
import { inputPrimitiveStyles, InputPrimitiveStylesProps } from './InputPrimitive.styles'

export interface InputPrimitiveProps
extends ComponentPropsWithoutRef<'input'>,
Omit<
InputPrimitiveStylesProps,
| 'isLeftAddonVisible'
| 'isRightAddonVisible'
| 'isLeftElementVisible'
| 'isRightElementVisible'
> {}

export const InputPrimitive = forwardRef<HTMLInputElement, InputPrimitiveProps>(
({ className, disabled: disabledProp, ...others }, ref) => {
const field = useFormFieldState()
const group = useInputGroup() || {}

const { isLeftAddonVisible, isRightAddonVisible, isLeftElementVisible, isRightElementVisible } =
group
const { id, name, isInvalid, isRequired, description } = field
const isDisabled = group.isDisabled ?? disabledProp

return (
<input
ref={ref}
id={id}
name={name}
className={inputPrimitiveStyles({
className,
isLeftAddonVisible: !!isLeftAddonVisible,
isRightAddonVisible: !!isRightAddonVisible,
isLeftElementVisible: !!isLeftElementVisible,
isRightElementVisible: !!isRightElementVisible,
})}
disabled={isDisabled}
required={isRequired}
aria-describedby={description}
aria-invalid={isInvalid}
{...others}
/>
)
}
)

InputPrimitive.displayName = 'InputPrimitive'
1 change: 1 addition & 0 deletions packages/components/input/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { InputRightElement, InputRightElementProps } from './InputRightElement'
export { useInputGroup } from './InputGroupContext'

export * from './Input'
export * from './InputPrimitive'
export * from './InputContainer'
export { type InputGroupProps } from './InputGroup'
export { type InputLeftAddonProps } from './InputLeftAddon'
Expand Down

0 comments on commit f73ae2b

Please sign in to comment.