feat: implement textField component

This commit is contained in:
jinhojang6 2023-02-18 05:15:49 +09:00
parent 5a3928fd7f
commit 2b53c86608
No known key found for this signature in database
GPG Key ID: 0E7AA62CB0D9E6F3
7 changed files with 230 additions and 15 deletions

View File

@ -12,6 +12,7 @@ import { BreadcrumbStyles } from '../Breadcrumb/Breadcrumb.styles'
import { BreadcrumbItemStyles } from '../BreadcrumbItem/BreadcrumbItem.styles'
import { defaultThemes, Theme, withTheme } from '../Theme'
import { TypographyStyles } from '../Typography/Typography.styles'
import { TextFieldStyles } from '../TextField/TextField.styles'
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
[
@ -26,6 +27,7 @@ const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
IconTagStyles,
BreadcrumbStyles,
BreadcrumbItemStyles,
TextFieldStyles,
]
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({

View File

@ -1,6 +1,7 @@
import { LsdIcon } from '../LsdIcon'
export const CheckIcon = LsdIcon((props) => (
export const CheckIcon = LsdIcon(
(props) => (
<svg
width="14"
height="14"
@ -10,8 +11,12 @@ export const CheckIcon = LsdIcon((props) => (
{...props}
>
<path
d="M11.0833 2.91667V11.0833H2.91667V2.91667H11.0833ZM11.0833 1.75H2.91667C2.275 1.75 1.75 2.275 1.75 2.91667V11.0833C1.75 11.725 2.275 12.25 2.91667 12.25H11.0833C11.725 12.25 12.25 11.725 12.25 11.0833V2.91667C12.25 2.275 11.725 1.75 11.0833 1.75Z"
d="M5.25009 9.43247L2.81759 6.99997L1.98926 7.82247L5.25009 11.0833L12.2501 4.0833L11.4276 3.2608L5.25009 9.43247Z"
fill="black"
/>
</svg>
))
),
{
filled: true,
},
)

View File

@ -0,0 +1,16 @@
export const textFieldClasses = {
root: `lsd-textField`,
input: `lsd-textField__input`,
filled: `lsd-textField__icon`,
supportingText: 'lsd-textField__supporting-text',
supportingTextLarge: 'lsd-textField__supporting-text--large',
supportingTextMedium: 'lsd-textField__supporting-text--medium',
disabled: `lsd-textField--disabled`,
error: 'lsd-textField--error',
large: `lsd-textField--large`,
medium: `lsd-textField--medium`,
withIcon: `lsd-textField--with-icon`,
}

View File

@ -0,0 +1,28 @@
import { Meta, Story } from '@storybook/react'
import { TextField, TextFieldProps } from './TextField'
export default {
title: 'TextField',
component: TextField,
argTypes: {
size: {
type: {
name: 'enum',
value: ['medium', 'large'],
},
defaultValue: 'large',
},
},
} as Meta
export const Root: Story<TextFieldProps> = (args) => (
<TextField {...args}>TextField</TextField>
)
Root.args = {
size: 'large',
supportingText: 'Supporting text',
disabled: false,
withIcon: false,
error: false,
placeholder: 'Placeholder',
}

View File

@ -0,0 +1,76 @@
import { css } from '@emotion/react'
import { textFieldClasses } from './TextField.classes'
export const TextFieldStyles = css`
.${textFieldClasses.root} {
width: auto;
border-bottom: 1px solid rgb(var(--lsd-border-primary));
box-sizing: border-box;
display: flex;
align-items: center;
}
.${textFieldClasses.input} {
border: none;
outline: none;
font-size: 14px;
color: rgb(var(--lsd-text-primary));
background: none;
width: inherit;
}
.${textFieldClasses.input}:hover {
outline: none;
}
.${textFieldClasses.input}::placeholder {
color: rgb(var(--lsd-text-primary));
opacity: 0.3;
}
.${textFieldClasses.disabled} {
cursor: default;
opacity: 0.34;
}
.${textFieldClasses.error} {
text-decoration: line-through;
}
.${textFieldClasses.supportingText} {
width: fit-content;
font-size: 12px;
line-height: 16px;
}
.${textFieldClasses.large} {
width: 208px;
height: 40px;
padding: 10px 14px;
}
.${textFieldClasses.medium} {
width: 188px;
height: 32px;
padding: 6px 12px;
}
.${textFieldClasses.supportingTextLarge} {
margin: 8px 14px;
}
.${textFieldClasses.supportingTextMedium} {
margin: 8px 12px;
}
.${textFieldClasses.withIcon} {
}
.${textFieldClasses.filled} {
cursor: pointer;
display: flex;
svg path {
--lsd-icon-primary: var(--lsd-icon-primary);
}
}
`

View File

@ -0,0 +1,87 @@
import clsx from 'clsx'
import React, { useState } from 'react'
import { CheckIcon, CloseIcon, ErrorIcon } from '../Icons'
import { Typography } from '../Typography'
import { textFieldClasses } from './TextField.classes'
export type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> & {
size?: 'large' | 'medium'
withIcon?: boolean
error?: boolean
supportingText?: string
}
export const TextField: React.FC<TextFieldProps> & {
classes: typeof textFieldClasses
} = ({
size = 'large',
withIcon = 'false',
supportingText = 'Supporting text',
error = false,
children,
...props
}) => {
const [inputValue, setInputValue] = useState<string>('')
const onChange = (e: React.FormEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.value
setInputValue(newValue)
}
const onCancel = () => {
setInputValue('')
}
return (
<>
<div
className={clsx(
props.className,
textFieldClasses.root,
textFieldClasses[size],
props.disabled && textFieldClasses.disabled,
withIcon && textFieldClasses.withIcon,
)}
>
<input
onChange={onChange}
className={clsx(
textFieldClasses.input,
error && textFieldClasses.error,
)}
value={inputValue}
{...props}
/>
{withIcon && error ? (
<span className={textFieldClasses.filled} onClick={onCancel}>
<ErrorIcon color="primary" className={textFieldClasses.filled} />
</span>
) : withIcon && !inputValue.length ? (
<span className={textFieldClasses.filled}>
<CheckIcon color="primary" />
</span>
) : withIcon && inputValue.length ? (
<span className={textFieldClasses.filled} onClick={onCancel}>
<CloseIcon color="primary" />
</span>
) : null}
</div>
{supportingText && (
<Typography
variant={size === 'large' ? 'label1' : 'label2'}
component="p"
className={clsx(
textFieldClasses.supportingText,
size && size === 'large'
? textFieldClasses.supportingTextLarge
: textFieldClasses.supportingTextMedium,
)}
>
{supportingText}
</Typography>
)}
</>
)
}
TextField.classes = textFieldClasses

View File

@ -0,0 +1 @@
export * from './TextField'