-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore(apps/web): react-hook-form 설치 * feat(packages/ui): isNill 함수 추가 * chore(packages/ui): isNill export * feat(packages/ui): TextField 컴포넌트 구현 * test(apps/web): 예시 추가 * fix(packages/ui): 디자인 요구사항 수정
- Loading branch information
1 parent
2124a91
commit 7e20960
Showing
14 changed files
with
513 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { recipe } from '@vanilla-extract/recipes'; | ||
import { vars } from '@repo/theme'; | ||
|
||
export const textFieldWrapperStyle = style({ | ||
position: 'relative', | ||
width: '100%', | ||
display: 'flex', | ||
flexDirection: 'column', | ||
gap: vars.space[8], | ||
}); | ||
|
||
export const textFieldContainerStyle = recipe({ | ||
base: { | ||
padding: vars.space[16], | ||
backgroundColor: vars.colors.grey50, | ||
borderRadius: '1.2rem', | ||
}, | ||
variants: { | ||
variant: { | ||
default: { | ||
backgroundColor: vars.colors.grey25, | ||
paddingRight: vars.space[16], | ||
}, | ||
button: { | ||
backgroundColor: vars.colors.grey50, | ||
paddingRight: '4.8rem', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export const textFieldStyle = recipe({ | ||
base: { | ||
width: '100%', | ||
border: 'none', | ||
outline: 'none', | ||
resize: 'none', | ||
color: vars.colors.grey700, | ||
fontSize: vars.typography.fontSize[18], | ||
fontWeight: vars.typography.fontWeight.medium, | ||
lineHeight: '150%', | ||
fontFamily: 'inherit', | ||
paddingRight: vars.space[4], | ||
maxHeight: `calc(${vars.typography.fontSize[18]} * 11 * 1.5)`, | ||
overflowY: 'auto', | ||
'::placeholder': { | ||
color: vars.colors.grey400, | ||
}, | ||
selectors: { | ||
'&::-webkit-scrollbar': { | ||
width: '0.6rem', | ||
}, | ||
'&::-webkit-scrollbar-thumb': { | ||
backgroundColor: vars.colors.grey200, | ||
borderRadius: '0.4rem', | ||
backgroundClip: 'padding-box', | ||
}, | ||
'&::-webkit-scrollbar-track': { | ||
backgroundColor: 'transparent', | ||
}, | ||
}, | ||
scrollbarWidth: 'thin', | ||
scrollbarColor: `${vars.colors.grey200} transparent`, | ||
}, | ||
variants: { | ||
variant: { | ||
default: { | ||
backgroundColor: vars.colors.grey25, | ||
'::placeholder': { | ||
color: vars.colors.grey400, | ||
}, | ||
}, | ||
button: { | ||
backgroundColor: vars.colors.grey50, | ||
'::placeholder': { | ||
color: vars.colors.grey400, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export const submitButtonStyle = recipe({ | ||
base: { | ||
position: 'absolute', | ||
top: '50%', | ||
transform: 'translateY(-50%)', | ||
right: '1.2rem', | ||
width: '3.2rem', | ||
height: '3.2rem', | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
border: 'none', | ||
background: 'transparent', | ||
padding: 0, | ||
cursor: 'pointer', | ||
|
||
':hover': { | ||
opacity: 0.8, | ||
}, | ||
}, | ||
variants: { | ||
isError: { | ||
true: { | ||
cursor: 'not-allowed', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export const counterStyle = recipe({ | ||
base: { | ||
fontSize: vars.typography.fontSize[16], | ||
fontWeight: vars.typography.fontWeight.medium, | ||
margin: `0 ${vars.space[8]}`, | ||
lineHeight: '1.5', | ||
textAlign: 'right', | ||
}, | ||
variants: { | ||
isError: { | ||
false: { | ||
color: vars.colors.grey500, | ||
}, | ||
true: { | ||
color: vars.colors.warning, | ||
}, | ||
}, | ||
}, | ||
defaultVariants: { | ||
isError: false, | ||
}, | ||
}); | ||
|
||
export const labelStyle = recipe({ | ||
variants: { | ||
isError: { | ||
true: { | ||
color: vars.colors.warning, | ||
}, | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { TextFieldRoot } from './TextFieldRoot'; | ||
import { TextFieldLabel } from './TextFieldLabel'; | ||
import { TextFieldInput } from './TextFieldInput'; | ||
import { TextFieldSubmit } from './TextFieldSubmit'; | ||
|
||
/** | ||
* | ||
* @example | ||
* // 1. 기본값이 있는 비제어 컴포넌트 | ||
* <TextField variant="button"> | ||
* <TextField.Label>메시지</TextField.Label> | ||
* <TextField.Input | ||
* placeholder="메시지를 입력하세요" | ||
* {...register('message', { | ||
* value: '초기값' | ||
* })} | ||
* /> | ||
* <TextField.Submit type="submit" /> | ||
* </TextField> | ||
* | ||
* // 2. onChange 이벤트가 필요한 제어 컴포넌트 | ||
* <TextField> | ||
* <TextField.Input | ||
* {...register('message')} | ||
* onChange={(e) => { | ||
* register('message').onChange(e); | ||
* setValue('message', e.target.value); | ||
* }} | ||
* /> | ||
* </TextField> | ||
* | ||
* // 3. 유효성 검사와 에러 상태를 포함한 컴포넌트 | ||
* <TextField error={!!errors.message}> | ||
* <TextField.Input | ||
* {...register('message', { | ||
* required: '메시지를 입력해주세요', | ||
* maxLength: { | ||
* value: 500, | ||
* message: '최대 500자까지 입력 가능합니다' | ||
* } | ||
* })} | ||
* /> | ||
* </TextField> | ||
*/ | ||
export const TextField = Object.assign(TextFieldRoot, { | ||
Label: TextFieldLabel, | ||
Input: TextFieldInput, | ||
Submit: TextFieldSubmit, | ||
}); | ||
|
||
export type { TextFieldProps } from './TextFieldRoot'; | ||
export type { TextFieldLabelProps } from './TextFieldLabel'; | ||
export type { TextFieldInputProps } from './TextFieldInput'; | ||
export type { TextFieldSubmitProps } from './TextFieldSubmit'; | ||
export type { TextFieldCounterProps } from './TextFieldCounter'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { counterStyle } from './TextField.css'; | ||
import { ComponentPropsWithoutRef, forwardRef, useContext } from 'react'; | ||
import { TextFieldContext } from './context'; | ||
|
||
export type TextFieldCounterProps = { | ||
current: number; | ||
max: number; | ||
} & ComponentPropsWithoutRef<'span'>; | ||
|
||
export const TextFieldCounter = forwardRef< | ||
HTMLSpanElement, | ||
TextFieldCounterProps | ||
>(({ current, max, className = '', ...props }, ref) => { | ||
const { isError } = useContext(TextFieldContext); | ||
|
||
return ( | ||
<span | ||
ref={ref} | ||
className={`${counterStyle({ isError })} ${className}`} | ||
{...props} | ||
> | ||
{current}/{max} | ||
</span> | ||
); | ||
}); | ||
|
||
TextFieldCounter.displayName = 'TextField.Counter'; |
Oops, something went wrong.