Flower Delivery Website 구현: (5)UI 컴포넌트 구현 /InputText

김 주현·2023년 8월 10일
0

개인적으로 가장 만들고 싶었던 UI컴포넌트~ InputBox는 기본적으로 대응해야 할 케이스가 아주 다양하기 때문에 그걸 한번 유연하게 처리해보고 싶었다. 특히나 이렇게 state가 존재하는 inputbox는 또 분기 처리하는 게 까탈스럽기 때문에...! 잘 해보고 싶은 욕심이 들었다.

InputText

type InputTextProp = {
  label?: string;
  textHelper?: string;
  placeholder?: string;
  disabled?: boolean;
  state?: "default" | "error" | "success";
  value: string;
  onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
};

const InputText = ({
  label,
  textHelper,
  placeholder,
  disabled,
  state = "default",
  value,
  onChange,
}: InputTextProp) => {
  const inputShape =
    "caption-bold border-m1 apperance-none outline-none text-black " +
    "tablet:border-t1 desktop:border-d1 " +
    (state === "default" && value === ""
      ? "border-lightgray "
      : "border-black ") +
    (state === "error"
      ? "border-error text-error placeholder:text-error "
      : "") +
    (state === "success"
      ? "border-success text-success placeholder:text-success "
      : "");

  const inputLayout =
    "w-full px-m16 py-m12 mb-m8 " +
    "tablet:p-t16 tablet:mb-t8 desktop:p-d16 desktop:mb-d8 ";

  const inputState =
    "disabled:bg-extralight disabled:placeholder:text-lightgray " +
    (value === "" && state === "default"
      ? "pointerhover:hover:border-gray disabled:pointerhover:hover:border-lightgray "
      : "");
  // + "focus-within:shadow-[0px_0px_2px_2px_#0D69D4] ";
  const inputStyle = inputShape + inputLayout + inputState;

  return (
    <label className="w-full">
      <p className="heading6 mb-m12 tablet:mb-t12 desktop:mb-d12">{label}</p>
      <input
        className={inputStyle}
        disabled={disabled}
        type="text"
        placeholder={placeholder}
        value={value}
        onChange={onChange}
      />
      {state === "default" && (
        <p className="caption-small text-gray">{textHelper}</p>
      )}

      {state === "error" && (
        <p className="caption-small flex items-center text-error">
          <ErrorIcon className="inline-block h-m16 w-m16 fill-error tablet:h-t16 tablet:w-t16 desktop:h-d16 desktop:w-d16" />
          {textHelper}
        </p>
      )}

      {state === "success" && (
        <p className="caption-small text-success">
          <CorrectIcon className="fill-succeess inline-block h-m16 w-m16 tablet:h-t16 tablet:w-t16 desktop:h-d16 desktop:w-d16" />
          {textHelper}
        </p>
      )}
    </label>
  );
};

아~ tailwind 이번에 프로젝트 끝나면 진짜 안 써야지😇 한 번 거슬리니까 계속 거슬리네

신경쓴 점

Pseudo-class를 최대한 활용하기

...를 하고 싶어서 input 태그의 pseudo-class를 알아보았지만 별 소득은 없었다. (사실은 내가 잘 활용을 못한 것 같다ㅋㅋ)

공백을 체크해서 스타일 먹이기

  • valid, invalid

    • 요 의사 클래스는 input 속성에 validation을 지정해주게 되면 입력된 값이 요기에 유효한 값인지 확인해서 스타일을 먹여주는 의사 클래스이다.
      • 그러므로, 속성에 validation을 설정해주어야 작동한다. (이걸 몰랐다ㅋㅋ)
    • 그래서 이걸 이용해서 값이 들어가 있지 않으면 border-lightgray를, 채워져 있으면 border-black을 먹이는 게 목표.
    • 공백 체크는 보통 두 가지 방법이 있다.
      1. required 속성을 넣어서 invalid 체크하기
        • required 속성은 필수로 값을 지정해줘야 하는 속성이니까, 값이 비어있으면 invalid가 적용된다.
      2. pattern 속성에 정규식을 넣어서 invalid 체크하기
        • pattern에 정규식 표현을 넣으면 입력된 값이 정규식 표현에 맞는지 아닌지 확인할 수 있다. 맞으면 valid가, 맞지 않으면 invalid가 적용된다.
        • 그런데 이 방식은 내가 정규식을 잘못 썼는지,, 잘 안 됐다. 쓴 표현식은 다음과 같았다. .*, .+
    • 그럼 다음과 같이 작성할 수 있다.
const inputShape = "border "
const inputState = 
      	"valid:border-blue-500 " +
      	"invalid:border-red-500 "
const inputStyle = inputShape + inputState
<input type="text" className={inputStyle} required />
  • blank, empty

    • 난 처음에 이걸 보고 공백체크를 해야겠다! 싶었던 건데, 방향을 잘못 잡았다.
    • blank는 '자식 노드'가 없을 때 작동하는 의사 클래스다. 그러니까, input에서는 쓰이지 않는 클래스이다.
    • empty는 값이 비어있을 때 작동하는 의사 클래스이긴 한데.. 실험적인 클래스라고 한다. 그 말인즉슨?

      ㅋㅋ
  • placeholder-shown

    • 내가 생각했을 땐, 이 방법이 제일인 것 같았다.
    • 말 그대로 placeholder가 보여질 때 작동하는 의사 클래스인데, placeholder가 보여질 때는 값이 비어있을 때 밖에 없으니, 결국 값이 비어있을 때 작동하는 의사 클래스나 마찬가지!
    • 요것과 :not을 이용하면 공백일 때와 아닐 때를 나눌 수 있다.
import plugin from "tailwindcss/plugin";

/** @type {import('tailwindcss').Config} */
export default {
  plugins: [
    plugin(function ({ addVariant }) {
      addVariant("not-placeholder-shown", "&:not(:placeholder-shown)");
    }),
  ],
}

아참, 여담으로 tailwind plugin이 안 먹히는 이유를 알아냈다. plugin을 import할 때 tailwindcss가 아닌 tailwindcss/plugin으로 했어야 했다😇

const inputShape = "border "
const inputState = 
      	"not-placeholder-shown:border-blue-500 " +
      	"placeholder-shown:border-red-500 "
const inputStyle = inputShape + inputState
<input type="text" className={inputStyle} required />

focus-within 활용하기

  • 여러 사이트에서 UI Component를 만드는 걸 보니, 웹 접근성을 생각해서 focus-within도 표시해주던데, 그렇게 되면 설정해놓은 border가 가리게 돼서 일단은 빼놨다.
  • 원래 border 스타일이 묻히는 게 웹 접근성에 맞는 건가 싶기도 하고? 포커스가 됐다는 걸 알려주는 게 맞으니까...? 모르겠다.
profile
FE개발자 가보자고🥳

0개의 댓글