모바일 반응형 적용을 위한 React Suite 라이브러리 DateRangePicker 변형

JulyK9·2022년 12월 20일
1

문제 이슈

  • 기본적으로 rsuite 라이브러리에서 제공하는 DateRangePicker 는 <FROM 날짜 시간> <To 날짜 시간> 의 선택을 위해 2개의 캘린더를 가로 형태로 보여줌
  • 문제는 아래와 같이 모바일 모드로 가로폭이 좁아졌을 때는 펼쳐지는 캘린더 크기를 조정하는 옵션이 없어서 어떻게 개선할 것인지 고민

접근방법

  • rsuite docs에서 확인한 부분 : 해당 컴포넌트에서 제공되는 옵션에는 펼쳐지는 달력의 위치를 조절하는 속성은 있지만 달력을 세로로 조정한다던지 크기 자체를 조절하는 속성이 없음(size 속성도 입력한 날짜의 display 부분의 크기 조절임)
  • rsuite 가 material ui 나 다른 ui 라이브러리 같이 그렇게 많이 쓰는게 아니라서 그런지 생각보다 자료가 많지 않음
    => 시간까지 피킹할 수 있는 라이브러리가 잘 안보여서 선택하긴 했는데 나중엔 이런 부분까지 고려해보고 사용해야 할 듯..(계속 찾다보니 중국계쪽에서 만든 것 같았음)
  • mui 라이브러리를 쓰면서 색상을 변경할 때, 라이브러리 자체에서 제공하는 속성을 사용하는 게 아니라 root 속성에 접근해서 직접 바꾸는 방식이 떠오름
  • 개발자 도구에서 라이브러리를 활용한 해당 컴포넌트, 태그의 클래스네임이 어떻게 되는지 확인하여 css를 적용해주면(개발자 도구상에서 직접 css를 적용해볼 수 있음) 기본적으로 제공되는 ui의 속성을 바꿀 수 있었음

적용과정

  • 개발자 도구를 통해 펼쳐진 캘린더의 마크업과 디테일한 클레스네임을 확인할 수 있음
  • 문제는 mui 처럼 컴포넌트 자체에 sx 속성 처럼 인라인 스타일로 적용할 수 있는 옵션이 없어서, 이모션을 통해 css 선택자를 클레스네임을 이리저리 잡아가며 적용해봤지만 적용이 되지 않아 시간이 소비됨
  • 없는 자료를 재검색, 재확인하다가 일반적인 css 적용 방법을 권장하는 것을 확인(아래 스택오버플로우 글)
  • 이유는 rs-picker-menu가 진입점인 root div 범위의 바깥쪽에서 렌더링되기 때문에 styled-component 방식으로 접근해서 수정하기보다 css 파일을 생성해서 적용하는 일반적인 방법을 사용하라고 함
  • 이 부분에서 react potarls의 개념을 언급하고 있는데 공식문서를 기반으로 해서 설명하고 계신 이 분의 블로그를 보니 css 상속 구조와 관련이 있다는 게 조금 이해가 되는 것 같다.
  • 개발자 도구에서 확인되는 마크업 내용을 다시 보니, id가 root 인 div 메인 돔을 벗어나서 별도의 div로 엘리먼트를 그리고 있었다.

적용결과

  • css 파일을 생성하여 반응형으로 모바일 모드에서는 달력을 세로로 보여주도록 css 파일 작성
  • 적용할 파일에 해당 css 파일을 임포트 시켜서 적용
// pickerStyles.css

@media screen and (max-width: 479px) {
  .rs-picker-daterange-menu {
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .rs-picker-daterange-panel {
    text-align: center;
  }
  .rs-stack-item {
    width: fit-content;
  }
  .rs-picker-daterange-content {
    width: 100%;
  }
  .rs-picker-daterange-header {
    width: 100%;
  }
  .rs-calendar-header-title {
    font-weight: bold;
  }

  .rs-picker-daterange-calendar-group {
    height: 100%;
    display: flex;
    flex-direction: column;
    min-width: auto;
  }
}
// StudyInputField.js

/** @jsxImportSource @emotion/react */
import 'rsuite/dist/rsuite.min.css';
import React, { useState } from 'react';
import { TextField, Box, MenuItem } from '@mui/material';
import { css, ThemeProvider } from '@emotion/react';
import { DateRangePicker } from 'rsuite';
import KewordAddressModal from './KewordAddressModal';
import { theme } from '../../Styles/theme';
import '../../Styles/pickerStyles.css';

// ...
// 생략
// ...

  return (
    <div css={Container}>
      <ThemeProvider theme={theme}>
        <Box
          sx={{
            '& > :not(style)': {
              m: 1,
              width: {
                mobile: 360,
                tablet: 290,
                laptop: 290,
                desktop: 290,
              },
            },
          }}
          noValidate
          autoComplete="off"
        >
          <div css={InputContainer}>
            <TextField
              id="study-name"
              label="스터디 이름"
              variant="standard"
              name="title"
              placeholder="스터디 이름을 지어주세요"
              value={title || ''}
              onChange={handleTitle}
              sx={{
                width: {
                  mobile: 360,
                  tablet: 290,
                  laptop: 290,
                  desktop: 290,
                },
                maxWidth: '100%',
                '& .MuiInputLabel-root.Mui-focused': { color: 'black' }, // 기본 라벨, 포커스시 라벨 색상
                '& .MuiInput-underline:after': {
                  borderBottomColor: 'black',
                },
              }}
            />
            <TextField
              id="study-category"
              select
              label="카테고리"
              variant="standard"
              name="category"
              value={category || ''}
              onChange={handleCategory}
              sx={{
                width: {
                  mobile: 360,
                  tablet: 290,
                  laptop: 290,
                  desktop: 290,
                },
                maxWidth: '100%',
                '& .MuiInputLabel-root.Mui-focused': { color: 'black' },
                '& .MuiInput-underline:after': {
                  borderBottomColor: 'black',
                },
              }}
            >
              {categories.map(option => (
                <MenuItem key={option.value} value={option.value || ''}>
                  {option.label}
                </MenuItem>
              ))}
            </TextField>
            <TextField
              id="study-location"
              type="button"
              onClick={handleOpen}
              label="스터디 위치"
              variant="standard"
              value={locationText || ''}
              onChange={handleLocationText}
              placeholder="클릭하면 주소 검색창이 나와요"
              sx={{
                width: {
                  mobile: 360,
                  tablet: 290,
                  laptop: 290,
                  desktop: 290,
                },
                maxWidth: '100%',
                '& .MuiInputLabel-root.Mui-focused': { color: 'black' },
                '& .MuiInput-underline:after': {
                  borderBottomColor: 'black',
                },
              }}
            />
            {open && (
              <KewordAddressModal
                open={open}
                handleClose={handleClose}
                setOpen={setOpen}
                locationText={locationText}
                setLocationText={setLocationText}
                setLatitude={setLatitude}
                setLongitude={setLongitude}
                setAddress={setAddress}
              />
            )}
            <TextField
              id="study-peopleNumber"
              label="모집인원"
              type="number"
              variant="standard"
              name="numberOfPeople"
              InputProps={{ inputProps: { min: 0, max: 100 } }}
              placeholder="숫자를 입력해주세요"
              value={capacity || ''}
              onChange={handleCapacity}
              sx={{
                width: {
                  mobile: 360,
                  tablet: 290,
                  laptop: 290,
                  desktop: 290,
                },
                maxWidth: '100%',
                '& .MuiInputLabel-root.Mui-focused': { color: 'black' },
                '& .MuiInput-underline:after': {
                  borderBottomColor: 'black',
                },
              }}
            />
            <div css={PeriodContainer}>
              <span>모집기간</span>
              <DateRangePicker
                format="yyyy-MM-dd hh:mm aa"
                placement="bottomEnd"
                preventOverflow
                showMeridian
                name="period"
                value={period}
                onChange={handlePeriodChange}
              />
            </div>
          </div>
        </Box>
      </ThemeProvider>
    </div>
  );
};

export default StudyInputField;

참고자료

profile
느리지만 꾸준하게. 부족하거나 잘못된 부분은 알려주시면 감사하겠습니다.

0개의 댓글