-
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.
- Loading branch information
1 parent
3f96bbc
commit 1d695c7
Showing
10 changed files
with
258 additions
and
24 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,17 @@ | ||
const HomePage = () => { | ||
return <>Hello World</>; | ||
import type { NextPage } from 'next'; | ||
|
||
import WriteInput from '@/src/components/Input/WriteInput'; | ||
import { useInput } from '@/src/hooks/useInput'; | ||
|
||
const HomePage: NextPage = () => { | ||
const testInputProps = useInput({ | ||
id: 'test', | ||
defaultValue: '', | ||
}); | ||
|
||
return ( | ||
<WriteInput inputProps={testInputProps} placeholder="메모를 끄적여보세요" /> | ||
); | ||
}; | ||
|
||
export default HomePage; |
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,115 @@ | ||
import type { ChangeEvent, HTMLAttributes, KeyboardEvent } from 'react'; | ||
import { useMemo, useRef, useState } from 'react'; | ||
import { assignInlineVars } from '@vanilla-extract/dynamic'; | ||
|
||
import { MAIN_INPUT_MAX_LENGTH } from '@/src/constants/config'; | ||
import type { UseInputReturn } from '@/src/hooks/useInput'; | ||
import { COLORS } from '@/src/styles/tokens'; | ||
|
||
import Icon from '../../SvgIcon'; | ||
import * as style from './style.css'; | ||
|
||
interface WriteInputProps extends HTMLAttributes<HTMLTextAreaElement> { | ||
inputProps: UseInputReturn; | ||
placeholder?: string; | ||
maxLength?: number; | ||
} | ||
|
||
const WriteInput = ({ | ||
inputProps, | ||
placeholder, | ||
maxLength = MAIN_INPUT_MAX_LENGTH, | ||
}: WriteInputProps) => { | ||
const { id, value } = inputProps; | ||
const inputRef = useRef<HTMLTextAreaElement | null>(null); | ||
const [textareaHeight, setTextareaHeight] = useState({ | ||
row: 1, | ||
lineBreak: {}, | ||
}); | ||
|
||
const handleResize = (e: ChangeEvent<HTMLTextAreaElement>) => { | ||
const { scrollHeight, clientHeight, value } = e.target; | ||
|
||
if (value.length === 0) { | ||
setTextareaHeight((prev) => ({ | ||
row: 1, | ||
lineBreak: { ...prev.lineBreak, [e.target.value.length]: false }, | ||
})); | ||
} | ||
|
||
if (scrollHeight > clientHeight) { | ||
setTextareaHeight((prev) => ({ | ||
row: prev.row + 1, | ||
lineBreak: { ...prev.lineBreak, [value.length - 1]: true }, | ||
})); | ||
} | ||
|
||
if (textareaHeight.lineBreak[value.length]) { | ||
setTextareaHeight((prev) => ({ | ||
row: prev.row - 1, | ||
lineBreak: { ...prev.lineBreak, [value.length]: false }, | ||
})); | ||
} | ||
}; | ||
|
||
const handleKeydownEnter = (e: KeyboardEvent<HTMLTextAreaElement>) => { | ||
if (e.code === 'Enter') { | ||
setTextareaHeight((prev) => ({ | ||
row: prev.row + 1, | ||
lineBreak: { ...prev.lineBreak, [value.length]: true }, | ||
})); | ||
} | ||
}; | ||
|
||
const isValid = useMemo(() => value.length > 0, [value.length]); | ||
|
||
return ( | ||
<div className={style.conatiner}> | ||
<div | ||
className={style.contentWrapper} | ||
style={assignInlineVars({ | ||
[style.inputHeight]: `${textareaHeight.row * 27}px`, | ||
})} | ||
> | ||
<label htmlFor={id} className={style.label}> | ||
<textarea | ||
{...inputProps} | ||
ref={inputRef} | ||
autoComplete="off" | ||
rows={textareaHeight.row} | ||
className={style.input} | ||
placeholder={placeholder} | ||
maxLength={maxLength} | ||
onInput={handleResize} | ||
onKeyDown={handleKeydownEnter} | ||
/> | ||
</label> | ||
|
||
<div | ||
className={style.submitWrapper({ | ||
multirow: textareaHeight.row > 1, | ||
})} | ||
> | ||
<div className={style.submit}> | ||
{value.length > 0 && ( | ||
<span className={style.textCount}> | ||
<span className={style.currentTextCount}>{value.length}</span> | ||
/ 500자 | ||
</span> | ||
)} | ||
<button disabled={!isValid}> | ||
<Icon | ||
icon="submit" | ||
width={48} | ||
height={48} | ||
color={isValid ? COLORS['Blue/Default'] : undefined} | ||
/> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default WriteInput; |
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,90 @@ | ||
import { createVar, style } from '@vanilla-extract/css'; | ||
import { recipe } from '@vanilla-extract/recipes'; | ||
|
||
import { COLORS } from '@/src/styles/tokens'; | ||
|
||
export const conatiner = style({ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
borderRadius: '16px', | ||
width: '100%', | ||
padding: '22px 12px 22px 24px', | ||
border: `2px solid ${COLORS['Blue/Gradient']}`, | ||
}); | ||
|
||
export const inputHeight = createVar(); | ||
export const contentWrapper = style({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
width: '100%', | ||
height: inputHeight, | ||
minHeight: '27px', | ||
maxHeight: '260px', | ||
}); | ||
|
||
export const label = style({ | ||
width: '100%', | ||
}); | ||
|
||
export const input = style({ | ||
padding: '0', | ||
width: '100%', | ||
maxHeight: '216px', | ||
resize: 'none', | ||
color: COLORS['Grey/900'], | ||
fontSize: '17px', | ||
lineHeight: '27px', | ||
overflowWrap: 'break-word', | ||
'::placeholder': { | ||
color: COLORS['Grey/250'], | ||
}, | ||
}); | ||
|
||
export const submitWrapper = recipe({ | ||
base: { | ||
display: 'flex', | ||
alignItems: 'flex-end', | ||
paddingLeft: '20px', | ||
}, | ||
variants: { | ||
multirow: { | ||
true: { | ||
height: '100%', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
export const submit = style({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
gap: '16px', | ||
height: '48px', | ||
}); | ||
|
||
export const textCount = style({ | ||
color: COLORS['Grey/400'], | ||
fontSize: '14px', | ||
fontWeight: '400', | ||
whiteSpace: 'nowrap', | ||
}); | ||
|
||
export const currentTextCount = style({ | ||
color: COLORS['Blue/Default'], | ||
fontSize: '14px', | ||
fontWeight: '700', | ||
}); | ||
|
||
export const alert = style({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
marginTop: '12px', | ||
}); | ||
|
||
export const alertMsg = style({ | ||
marginLeft: '6px', | ||
color: COLORS['Grey/600'], | ||
fontSize: '13px', | ||
fontWeight: '600', | ||
lineHeight: '17px', | ||
}); |
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 |
---|---|---|
@@ -1,23 +1,16 @@ | ||
import { Icon as icon } from '../constants/icon'; | ||
import { iconFactory, type Icons } from '../constants/icon'; | ||
|
||
interface IconProps { | ||
icon: keyof typeof icon; | ||
fill?: string; | ||
stroke?: string; | ||
icon: Icons; | ||
color?: string; | ||
width?: number; | ||
height?: number; | ||
} | ||
|
||
const Icon = ({ | ||
icon: iconKey, | ||
fill, | ||
stroke, | ||
width = 24, | ||
height = 24, | ||
}: IconProps) => { | ||
const SvgIcon = icon[iconKey]; | ||
const Icon = ({ icon, color, width = 24, height = 24 }: IconProps) => { | ||
const SvgIcon = iconFactory[icon]; | ||
|
||
return <SvgIcon fill={fill} stroke={stroke} width={width} height={height} />; | ||
return <SvgIcon color={color} width={width} height={height} />; | ||
}; | ||
|
||
export default Icon; |
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,4 @@ | ||
/** | ||
* "끄적끄적" 최대 입력 글자 수 | ||
*/ | ||
export const MAIN_INPUT_MAX_LENGTH = 500; |
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 |
---|---|---|
@@ -1,11 +1,9 @@ | ||
import type { FC, SVGProps } from 'react'; | ||
|
||
import Profle from '@/src/assets/icons/profile.svg'; | ||
import Submit from '@/src/assets/icons/submit.svg'; | ||
|
||
export type IconFactory = { | ||
[key: string]: FC<SVGProps<SVGSVGElement>>; | ||
}; | ||
|
||
export const Icon: IconFactory = { | ||
export const iconFactory = { | ||
profile: Profle, | ||
submit: Submit, | ||
}; | ||
|
||
export type Icons = keyof typeof iconFactory; |
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,19 @@ | ||
import type { ChangeEvent } from 'react'; | ||
import { useState } from 'react'; | ||
|
||
interface UseInputArgs { | ||
id: string; | ||
defaultValue?: string; | ||
} | ||
|
||
export const useInput = ({ id, defaultValue = '' }: UseInputArgs) => { | ||
const [value, setValue] = useState(defaultValue); | ||
|
||
const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | ||
setValue(e.currentTarget.value); | ||
}; | ||
|
||
return { id, value, onChange }; | ||
}; | ||
|
||
export type UseInputReturn = ReturnType<typeof useInput>; |
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