diff --git a/src/components/InputField/InputField.styles.ts b/src/components/InputField/InputField.styles.ts new file mode 100644 index 0000000..6fc0815 --- /dev/null +++ b/src/components/InputField/InputField.styles.ts @@ -0,0 +1,97 @@ +import { css } from '@emotion/react'; + +import type { InputFieldProps } from '@components/InputField/InputField'; + +import { Theme } from '../../styles/Theme'; + +export const inputContainerStyling = css({ + display: 'flex', + flexDirection: 'column', + gap: Theme.spacer.spacing2, +}); + +export const inputWrapperStyling = (isError: Required['isError']) => { + return css({ + display: 'flex', + gap: '12px', + alignItems: 'center', + + paddingTop: 0, + paddingBottom: 0, + borderRadius: Theme.borderRadius.small, + + backgroundColor: isError ? `${Theme.color.red100} !important` : 'transparent', + + transition: 'all .2s ease-in', + + '&:focus-within': { + backgroundColor: isError ? Theme.color.red100 : Theme.color.white, + boxShadow: isError + ? `inset 0 0 0 1px ${Theme.color.red200}` + : `inset 0 0 0 1px ${Theme.color.gray300}`, + }, + + '& svg': { + width: '16px', + height: '16px', + }, + }); +}; + +export const getVariantStyling = (variant: Required['variant']) => { + const style = { + default: css({ + backgroundColor: Theme.color.gray100, + }), + + text: css({ + backgroundColor: 'transparent', + }), + focus: css({ + backgroundColor: 'transparent', + }), + error: css({ + backgroundColor: 'transparent', + }), + }; + + return style[variant]; +}; + +export const getSizeStyling = (size: Required['size']) => { + const style = { + large: css({ + padding: '14px 16px', + + fontSize: Theme.text.medium.fontSize, + lineHeight: Theme.text.medium.lineHeight, + }), + + medium: css({ + padding: '12px 16px', + + fontSize: Theme.text.medium.fontSize, + lineHeight: Theme.text.medium.lineHeight, + }), + + small: css({ + padding: '8px 12px', + + fontSize: Theme.text.small.fontSize, + lineHeight: Theme.text.small.lineHeight, + }), + }; + + return style[size]; +}; + +export const getInputStyling = css({ + width: '100%', + paddingLeft: 0, + paddingRight: 0, + border: 'none', + borderRadius: Theme.borderRadius.small, + outline: 0, + + backgroundColor: 'transparent', +}); \ No newline at end of file diff --git a/src/components/InputField/InputField.tsx b/src/components/InputField/InputField.tsx new file mode 100644 index 0000000..0d83e00 --- /dev/null +++ b/src/components/InputField/InputField.tsx @@ -0,0 +1,48 @@ +import type { Size } from '@type/index'; +import type { ComponentPropsWithRef, ForwardedRef, ReactElement } from 'react'; +import { forwardRef } from 'react'; + +import { + getInputStyling, + getSizeStyling, + getVariantStyling, + inputContainerStyling, + inputWrapperStyling, +} from '@components/InputField/InputField.styles'; +import Label from '@components/TextLabel/TextLabel'; + +export interface InputFieldProps extends Omit, 'size'> { + label?: string; + variant?: 'default' | 'text' | 'focus' | 'error'; + size?: Extract; + isError?: boolean; + icon?: ReactElement; +} + +const InputField = ( + { + label, + variant = 'default', + size = 'medium', + isError = false, + icon, + ...attributes + }: InputFieldProps, + ref: ForwardedRef +) => { + return ( +
+ {label && ( + + )} +
+ {icon} + +
+
+ ); +}; + +export default forwardRef(InputField); \ No newline at end of file diff --git a/src/components/TextLabel/TextLabel.style.ts b/src/components/TextLabel/TextLabel.style.ts new file mode 100644 index 0000000..5323a70 --- /dev/null +++ b/src/components/TextLabel/TextLabel.style.ts @@ -0,0 +1,10 @@ +import { css } from '@emotion/react'; + + +import { Theme } from '../../styles/Theme'; + +export const labelStyling = css({ + fontSize: Theme.text.small.fontSize, + lineHeight: Theme.text.small.lineHeight, + fontWeight: 600, +}); diff --git a/src/components/TextLabel/TextLabel.tsx b/src/components/TextLabel/TextLabel.tsx new file mode 100644 index 0000000..4b37762 --- /dev/null +++ b/src/components/TextLabel/TextLabel.tsx @@ -0,0 +1,15 @@ +import type { ComponentPropsWithoutRef } from 'react'; + +import { labelStyling } from '@components/TextLabel/TextLabel.style'; + +export interface LabelProps extends ComponentPropsWithoutRef<'label'> {} + +const TextLabel = ({ id, children, ...attributes }: LabelProps) => { + return ( + + ); +}; + +export default TextLabel; diff --git a/src/stories/InputField.stories.tsx b/src/stories/InputField.stories.tsx new file mode 100644 index 0000000..9edddf1 --- /dev/null +++ b/src/stories/InputField.stories.tsx @@ -0,0 +1,115 @@ +import { containerStyle, informationStyle } from './style'; +import type { Meta, StoryObj } from '@storybook/react'; + +import InputField from '@components/InputField/InputField'; + +const meta = { + title: 'InputField', + component: InputField, + argTypes: { + label: { + control: { type: 'text' }, + }, + variant: { + control: { type: 'radio' }, + options: ['default', 'text'], + }, + size: { + control: { type: 'radio' }, + options: ['small', 'medium', 'large'], + }, + isError: { + control: { type: 'boolean' }, + }, + }, + args: { + variant: 'default', + size: 'medium', + placeholder: 'placeholder', + isError: false, + id: 'input', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; + +export const Variants: Story = { + render: ({ size, isError, placeholder }) => { + return ( +
    +
  • +
    Default
    + +
  • +
  • +
    Text
    + +
  • +
+ ); + }, + argTypes: { + variant: { + control: false, + }, + }, +}; + +export const Sizes: Story = { + render: ({ variant, isError, placeholder }) => { + return ( +
    +
  • +
    Small
    + +
  • +
  • +
    Medium
    + +
  • +
  • +
    Large
    + +
  • +
+ ); + }, + argTypes: { + size: { + control: false, + }, + }, +}; + +export const Default: Story = { + args: { + variant: 'default', + }, + argTypes: { + variant: { + control: false, + }, + }, +}; + +export const Text: Story = { + args: { + variant: 'text', + }, + argTypes: { + variant: { + control: false, + }, + }, +}; + + +export const WithLabel: Story = { + args: { + label: 'Label', + }, + name: 'Input with Label', +}; diff --git a/src/stories/Textlabel.stories.tsx b/src/stories/Textlabel.stories.tsx new file mode 100644 index 0000000..3d9d0f4 --- /dev/null +++ b/src/stories/Textlabel.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import Label from '@components/TextLabel/TextLabel'; + +const meta: Meta = { + title: 'TextLabel', + component: Label, + argTypes: { + children: { + control: { type: 'text' }, + }, + }, + args: { + children: 'Label', + }, +}; + +export default meta; + +export const Default: StoryObj = {}; diff --git a/tsconfig.json b/tsconfig.json index cebef64..52681ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,6 +27,6 @@ }, "include": [ "src/**/*.ts", - "src/**/*.tsx" + "src/**/*.tsx" ] }