mirror of https://github.com/acid-info/lsd.git
feat: implement textField component
This commit is contained in:
parent
5a3928fd7f
commit
2b53c86608
|
@ -12,6 +12,7 @@ import { BreadcrumbStyles } from '../Breadcrumb/Breadcrumb.styles'
|
||||||
import { BreadcrumbItemStyles } from '../BreadcrumbItem/BreadcrumbItem.styles'
|
import { BreadcrumbItemStyles } from '../BreadcrumbItem/BreadcrumbItem.styles'
|
||||||
import { defaultThemes, Theme, withTheme } from '../Theme'
|
import { defaultThemes, Theme, withTheme } from '../Theme'
|
||||||
import { TypographyStyles } from '../Typography/Typography.styles'
|
import { TypographyStyles } from '../Typography/Typography.styles'
|
||||||
|
import { TextFieldStyles } from '../TextField/TextField.styles'
|
||||||
|
|
||||||
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
|
const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
|
||||||
[
|
[
|
||||||
|
@ -26,6 +27,7 @@ const componentStyles: Array<ReturnType<typeof withTheme> | SerializedStyles> =
|
||||||
IconTagStyles,
|
IconTagStyles,
|
||||||
BreadcrumbStyles,
|
BreadcrumbStyles,
|
||||||
BreadcrumbItemStyles,
|
BreadcrumbItemStyles,
|
||||||
|
TextFieldStyles,
|
||||||
]
|
]
|
||||||
|
|
||||||
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({
|
export const CSSBaseline: React.FC<{ theme?: Theme }> = ({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { LsdIcon } from '../LsdIcon'
|
import { LsdIcon } from '../LsdIcon'
|
||||||
|
|
||||||
export const CheckIcon = LsdIcon((props) => (
|
export const CheckIcon = LsdIcon(
|
||||||
|
(props) => (
|
||||||
<svg
|
<svg
|
||||||
width="14"
|
width="14"
|
||||||
height="14"
|
height="14"
|
||||||
|
@ -10,8 +11,12 @@ export const CheckIcon = LsdIcon((props) => (
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<path
|
<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"
|
fill="black"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
))
|
),
|
||||||
|
{
|
||||||
|
filled: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -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`,
|
||||||
|
}
|
|
@ -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',
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './TextField'
|
Loading…
Reference in New Issue