본문으로 건너뛰기

Form

Input

크기, 비밀번호 표시, Enter 동작을 지원하는 텍스트 입력입니다.

Usage

기본 input 속성을 그대로 전달하며, 프로젝트 전용 sizeonEnter를 추가로 지원합니다.

Examples

Password

Enter

Enter를 눌러보세요.

Props

  • size?: 'small' | 'medium' | 'large' — 입력 높이, 기본값 medium
  • onEnter?: (event, value) => void — Enter 입력 시 호출
  • wrapperClass?: string — 바깥 wrapper class
  • 그 밖의 표준 input 속성

Source

input
'use client'

import React, { forwardRef, PropsWithChildren, Ref, useCallback, useEffect, useState } from 'react'
import { iconSizeClasses, IInputProps, passwordClasses, sizeClasses } from '@/shared/ui/Input/types'

export default forwardRef<HTMLInputElement, IInputProps>(function DInput(
  {
    wrapperClass = '',
    className = '',
    size = 'medium',
    type = 'text',
    onEnter,
    ...props
  }: PropsWithChildren<IInputProps>,
  ref: Ref<HTMLInputElement>,
): React.JSX.Element {
  const wrapperClasses = ['relative w-full', wrapperClass].filter((w) => !!w).join(' ')
  const inputClasses = [
    'flex w-full rounded-md border border-neutral-400 dark:border-neutral-500 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-neutral-800',
    sizeClasses[size],
    type === 'password' && passwordClasses[size],
    className,
  ]
    .filter((w) => !!w)
    .join(' ')

  const [isShowPassword, setIsShowPassword] = useState<boolean>(false)
  const handleClickViewPassword = useCallback(() => {
    setIsShowPassword((prev) => !prev)
  }, [])

  const handleKeydown = useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) => {
      if (e.key === 'Enter') {
        if (onEnter && typeof onEnter === 'function') {
          onEnter(e, e.currentTarget.value)
        }
      }
    },
    [onEnter],
  )

  useEffect(() => {
    setIsShowPassword(false)
  }, [])

  return (
    <div className={`${wrapperClasses}`}>
      <input
        {...props}
        type={isShowPassword ? 'text' : type}
        className={`${inputClasses}`}
        ref={ref}
        onKeyDown={handleKeydown}
      />
      {type === 'password' && (
        <button
          className={'absolute top-1/2 right-3 translate-y-[-50%]'}
          onClick={handleClickViewPassword}
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={1.5}
            stroke="currentColor"
            className={iconSizeClasses[size]}
          >
            {isShowPassword ? (
              <>
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"
                />
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
                />
              </>
            ) : (
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"
              />
            )}
          </svg>
        </button>
      )}
    </div>
  )
})
input-examples
'use client'

import { useState } from 'react'
import Input from '@/shared/ui/Input'

export function InputBasicExample() {
  return (
    <div className="w-full max-w-sm space-y-3">
      <Input aria-label="이름" placeholder="이름을 입력하세요" />
      <Input aria-label="작은 입력" size="small" placeholder="Small" />
      <Input aria-label="비활성 입력" disabled placeholder="Disabled" />
    </div>
  )
}

export function InputPasswordExample() {
  return (
    <div className="w-full max-w-sm">
      <Input aria-label="비밀번호" type="password" placeholder="비밀번호" />
    </div>
  )
}

export function InputEnterExample() {
  const [message, setMessage] = useState('Enter를 눌러보세요.')
  return (
    <div className="w-full max-w-sm space-y-2">
      <Input aria-label="Enter 예제" onEnter={(_, value) => setMessage(`입력값: ${value}`)} />
      <p aria-live="polite" className="text-sm">
        {message}
      </p>
    </div>
  )
}

Known limitations

현재 비밀번호 표시 버튼은 접근 가능한 이름과 명시적 type="button"이 없습니다. 현행 UI 보존 범위에서는 수정하지 않았으며, 접근성 개선 작업에서 보완해야 합니다.