[UIUX] 컴포넌트 레이어

Bora Im·2024년 3월 4일

이야기는 Select 컴포넌트에서 출발한다.
HTML Select가 아닌 일반 Tags와 Button 컴포넌트를 이용해 커스텀하게 만든 녀석이고,
UI 관련 아래 variant, size, placement 정도의 props를 가지고 있다.
*각각 형태(appearance), 크기, 방향

<div class="root">
  <Button class="select">선택</Button>
  <div class="dropdown">
    <ul class="list">
      <li class="item">
        <Button class="option">항목 1</Button>
      </li>
      ...
    </ul>
  </div>
</div>

마크업은 대략 위와 같고,
root 기준으로(relative) dropdown 영역의 위치를 잡아주었다(absolute).

size prop에 따라 select와 각 option의 높이값을 md/lg/xl 40/48/60으로 잡아주고,
placement prop은 드롭다운이 펼쳐지는 방향으로
"bottom"일 경우 top값, "top"일 경우 bottom값으로 사이 간격(8px)을 더한 calc(100% + 8px)을 주어 구현했다.

🚨근데 이제? 이슈 발생..

Select 컴포넌트는(*내가 만든) 다양한 환경과 위치에 자리하게 되는데,
overflow 안에서 절대 자유로울 수 없다..
드롭다운 레이어가 absolute임에도 불구하고, 부모 영역에 스크롤이 생기거나, 댕강 허리가 잘려버림 🥲

사실 이건 Select 컴포넌트에만 국한된 이슈가 아니고,
컴포넌트 내 최상위(root) 영역을 기준으로 하위 레이어의 위치를 잡는 모든 것이 그러하다.
Date(Range), Tooltip, Context, Popover 등등..

자, 해결 방법은? ⛏️

간단하다. 웹뷰 전체에서 자유로우면 된다.!

  • 1) 마크업이 body 바로 하위의 가장 바깥에 위치하거나
  • 2) 마크업이 어느 곳에 있든 위치값이 어느 요소에 종속되지 않거나
    • 고정 위치(fixed position) : 뷰포트(viewport)를 기준으로 위치를 설정하는 방식

남들은 어케 했는지 레퍼런스를 살펴보자.

MUI

Select, Popover, Modal 등의 컴포넌트를 살펴보면, 레이어 마크업(MuiPaper)이
버튼(MuiBox)과는 별개로 body가 닫히기 직전 위치에 추가/제거 되는 것을 확인할 수 있다.

Select

  • Select active 시,
    • html overflow: hidden, 레이어를 감싼 영역에 position: fixed; inset: 0
      👉 스크롤, 클릭 차단
    • 웹뷰 resize 시 레이어 위치값(top,left) 업데이트 (뷰포트 기준)
    • 본체(버튼)의 위치가 변경되었을 때 레이어에 영향 없음. (유기적 동작 X)

Popper

  • Popper active 시,
    • 웹뷰 resize 시 위치값(translate x,y) 업데이트 (웹문서 기준)
    • 스크롤 시 호출한 요소(버튼)와 잘 붙어있음.

Ant

Select 컴포넌트를 살펴봤을 때, MUI와 마찬가지로 컴포넌트 레이어 마크업이
body가 닫히기 직전 위치에 추가 되는 것을 확인할 수 있다.
*다만, hidden 클래스를 가진 채로 추가된 후, toggle class 해주는 방식.
.ant-select-dropdown-hidden {display: none}

Select

  • Select가 focus를 잃으면 닫힘(inactive)
  • 웹뷰 resize 시 레이어 위치값(top,left) 업데이트 (웹문서 기준)
  • 스크롤 시 본체(버튼)와 잘 붙어있음.

일반적으로 방법 1) 을 사용하는 경향.

레퍼런스는 충분히 봤지만,
하나의 컴포넌트의 마크업이 웹문서 내 여러 곳에 흩어져있는 걸 선호하지 않는 상태(?).
*고집불통

초기에 컴포넌트를 설계할 때, 컴포넌트 간의 조합은 최소화하고 컴포넌트와 모듈 CSS를 1:1로 두어 해당 컴포넌트의 모든 스타일링을 한곳(Select.module.scss)에서 관리하고 있기 때문에
레이어 마크업을 외부에 만드는 개념이 아직 생소하다. *관리(control)가 제대로 될까?😟

2) 를 밀고 나가 보자! 고정 위치(fixed position) 📌

  • 레이어에 position: fixed를 준다. 👉 뷰포트 기준
  • 인라인 스타일로 x,y축 좌표를 준다.
    • 요소의 크기/위치 정보를 알려주는 getBoundingClientRect() 메소드를 통해
      부모(컴포넌트)의 크기/위치값을 가져와 각각 top, left, minWidth 적용.
    • 부모(컴포넌트) 기준 0,0 좌표로부터 size에 따른 높이 + 간격(8px) 고정값 transform 적용.

추가 UX 정책(rules)이 필요하다.

  • 스크롤 가능 여부: ⭕
  • 스크롤에 따른 좌표값 업데이트 여부: ❌
  • 웹뷰 resize 시 레이어 좌표값 업데이트 여부: ❌
  • 웹뷰 resize, 스크롤 시 레이어 비활성화(inactive) 여부: ⭕

🎉 ta-da!

멋지게 자기 주장을 하는 요녀석

기특하게도 잘 빠져나온다..🤧

0개의 댓글