table형태로 정보를 보여주고 한 행을 선택할 수 있는 select를 만들어보기로 했다.

popper를 사용함

 

1. import

import Box from "@mui/material/Box";
import Popper from "@mui/material/Popper";

 

2. return에서 사용

<div onClick={(e: React.MouseEvent<HTMLElement>) => {
	setOpenTable((prev) => !prev);
	setAnchorEl(e.currentTarget);
}}>
	<Popper open={openTable} anchorEl={anchorEl} placement="bottom-end" sx={{ zIndex: "1500", position: "absolute" }}>
		<Box sx={{ bgcolor: "background.paper", overflowY: "auto", maxHeight: "250px", width: "fit-content" }}>
			<table>원하는 테이블 작성</table>
		</Box>
	</Popper>
</div>

div에 anchorEl를 걸고, 클릭할때 table을 open할 state를 따로 설정해서 open에 적어줍니다(openTable)

MUI의 Tooltip은 hover시에만 유지되고 요구사항에 맞지않는 점이 있어서 popper로 변경해보기로 하였다.

 

내가 원하는 모습 : 클릭으로  toggle하고, 유지되었다가 닫기 상호작용이 가능한 tooltip


1. import

import Popper from "@mui/material/Popper";
import CloseIcon from "@mui/icons-material/Close";

 

2. 사용하기

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const tooltipOpen = Boolean(anchorEl);
...
<div onClick={(e) => setAnchorEl(anchorEl ? null : e.currentTarget)}>
	<Popper open={tooltipOpen} anchorEl={anchorEl} sx={{ zIndex: "1500", position: "absolute" }} placement="bottom-start">
		<div className="flex flex-row text-xs w-[200px] bg-white rounded-md shadow-md p-2 border border-solid mt-2">
			<div className="flex items-center justify-center p-1">{코멘트코멘트코멘트}</div>
				<div className="flex pl-2 text-gray-500 cursor-pointer hover:text-gray-700" onClick={() => setAnchorEl(null)}>
				<CloseIcon fontSize="small"/>
			</div>
		</div>
	</Popper>
</div>

 

주의할점은 popper가 나타날 주체인 아이콘이던 div던에 anchorEl을 걸어줘야 작동합니다.

 

저는 zIndex가 높은 div에서 작업했기때문에 별도로 z index를 보다 높게 지정했습니다.

MUI Tooltip을 커스텀없이 사용했을때의 모습

 

내가 필요한것 : 흰바탕에 검정글씨 툴팁

 

1. import

import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
import { styled } from "@mui/material/styles";

 

2. lightTooltip 별도 선언

- 함수 컴포넌트 외부에 선언해도 괜찮습니다

const LightTooltip = styled(({ className, ...props }: TooltipProps) => <Tooltip {...props} classes={{ popper : className }} />)(({ theme }) => ({
	[`& .${tooltipClasses.tooltip}`]: {
    	backgroundColor : theme.palette.common.white,
        color: "rgba(0,0,0,0.87)",
        boxShadow: theme.shadows[1],
        fontSize: 11,
        width: 150,
        lineHeight: 1.5,
       },
 }));

 

3. return 절에서 사용

<LightTooltip title ={툴팁에 들어갈 내용}></LightTooltip>

 

리액트 쿼리에서 특정 조건일때는 data를 불러오지 않도록 처리할 수 있다.

 

export const useData = (id: number) => {
  const query = useQuery(["data"], () => axios.get<DataType[]>(`{api주소}`).then((res) => res.data), {
    enabled: id === 0 ? false : true,
  });
  return query;
};

 

id를 넣어서 데이터를 불러오는데, 사용처에서 useEffect안에서 use훅으로 조건에 맞춰 데이터를 불러오는 동작을 할 수 없다보니 에러가 생긴다.

 

이럴땐 기본값이거나 데이터를 불러오는 동작을 하지 않고 싶을땐 의도적으로 id값을 0으로 설정해주고,

enabled를 추가하여서 id가 0이 아닐때만 동작하게 하도록 한다.

import { t } from "i18next";
import React from "react";
import Chart from "react-apexcharts";

function TempertureGraph() {
  const series: ApexAxisChartSeries | ApexNonAxisChartSeries | undefined = [
    {
      name: "",
      data: [10, 20, 30, 40, 50, 60, 70, 80, 90],
    },
    {
      name: "",
      data: [20, 30, 40, 50, 60, 70, 80, 90, 100],
    },
    {
      name: "",
      data: [5, 15, 25, 35, 45, 55, 65, 75, 85],
    },
    {
      name: "",
      data: [90, 80, 70, 60, 50, 40, 30, 20, 10],
    },
    {
      name: "",
      data: [70, 60, 50, 40, 30, 20, 10, 5, 15],
    },
    {
      name: "",
      data: [40, 50, 60, 70, 80, 90, 100, 90, 80],
    },
    {
      name: "",
      data: [15, 25, 35, 45, 55, 65, 75, 85, 95],
    },
    {
      name: "",
      data: [50, 40, 30, 20, 10, 5, 15, 25, 35],
    },
  ];
  const options: ApexCharts.ApexOptions = {
    colors: ["#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00", "#FFD92F", "#A65628", "#F781BF"],
    chart: {
      // 구간확대 비활성화
      zoom: {
        enabled: false,
      },
      // download 버튼 비활성화
      toolbar: {
        show: false,
      },
    },
    dataLabels: {
      enabled: false,
    },
    stroke: {
      curve: "straight",
      width: 3,
    },
    xaxis: {
      categories: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"],
    },
    legend: {
      show: false,
    },
    grid: {
      show: series.length !== 0,
    },
    noData: {
      text: "정보없음",
      style: {
        fontSize: "18px",
      },
    },
    states: {
      hover: {
        filter: {
          type: "lighten",
          value: 0.15,
        },
      },
    },
    tooltip: {
      shared: false,
    },
  };
  return (
    <div>
      <Chart type="line" width={"100%"} height={400} series={series} options={options} />
    </div>
  );
}

export default TempertureGraph;

 

암튼 타입이 문제여..

스크롤바를 커스텀 해보자...

 

css로 가능한데, 리액트에서 테일윈드를 사용하기에, 적용할 방법이 없었으나

emotion을 이용해 적용가능!

 

import { css } from "@emotion/react";
.
.
.
<div
            css={css`
              ::-webkit-scrollbar {
                width: 8px;
              }
              ::-webkit-scrollbar-track {
                background-color: transparent;
              }
              ::-webkit-scrollbar-thumb {
                background-color: rgba(0, 0, 0, 0.3);
                border-radius: 6px;
              }
              ::-webkit-scrollbar-thumb:hover {
                background-color: rgba(0, 0, 0, 0.5);
              }
            `}
            className="w-full overflow-y-auto h-full p-3"
         >

 

다른 css는 className에서 tailwind로도 가능하지만 스크롤바 커스텀은 안되어서 어쩔수없이 emotion과 혼용사용 하였다.

datepicker는 활용도가 정말 높은데 그때그때 import 하는것보다 고정적인 기준을 두고 공용화 하고 싶어서 진행해보았다.

 

1. import

- 상단의 두개는 datepicker 설치시 기본으로 불러오고,

- 하단의 언어는 달력에도 다국어를 지원해주기 위해 불러왔다.

import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";

import ko from "date-fns/locale/ko";
import enUS from "date-fns/locale/en-US";
import es from "date-fns/locale/es";
import ja from "date-fns/locale/ja";

 

2. 타입과 함수

type Props = {
  // 시작날짜, 종료날짜, 혹은 단일 선택인지 타입 지정
  dateType: "start" | "end" | "select";
  // selected로 설정할 date 정보값
  date: Date | null;
  // onchange시에 작동할 함수 정보
  onChangeFn: (date: Date) => void;
  // 종료날짜의 달력일때 dateForMin에 시작날짜 state를 넣으면 시작날짜의 앞날은 종료날짜로 선택이 불가하게 설정함.
  dateForMin?: Date | null;
  // 선택이 불가 하게 할 옵션 설정
  readOnlyOpt?: boolean;
  // time 설정까지 가능한 datepicker 인지 구분
  isTime?: boolean;
};

// 데이트피커 로케일별로 언어 변경 제공
export function datePickerLocale() {
  const locale = localStorage.getItem("i18nextLng");
  switch (locale) {
    case "ko":
      return ko;
    case "ja":
      return ja;
    case "es":
      return es;
    default:
      return enUS;
  }
}

 

3. datepicker return 하기

export default function EdgeDatePicker(props: Props) {
  const { dateType, date, onChangeFn, dateForMin, errors, readOnlyOpt, isTime = false } = props;

  return isTime ? (
    <DatePicker
      locale={datePickerLocale()}
      minDate={dateType === "end" ? dateForMin : null}
      maxDate={new Date()}
      readOnly={readOnlyOpt}
      placeholderText="YYYY-MM-DD HH:MM"
      onChange={onChangeFn}
      selected={date}
      dateFormat="yyyy-MM-dd HH:mm"
      timeFormat="p"
      //   키보드 입력 방지
      onKeyDown={(e: any) => {
        e.preventDefault();
      }}
      showTimeInput
    />
  ) : (
    <DatePicker
      locale={datePickerLocale()}
      minDate={dateType === "end" ? dateForMin : null}
      maxDate={new Date()}
      readOnly={readOnlyOpt}
      placeholderText="YYYY-MM-DD"
      onChange={onChangeFn}
      selected={date}
      dateFormat="yyyy-MM-dd"
      //   키보드 입력 방지
      onKeyDown={(e: any) => {
        e.preventDefault();
      }}
    />
  );
}

개인적으로 시간까지 입력되는 datpicker유형도 필요해서, 컴포넌트를 하나 더 파기보단 그냥 옵션을 받아서 따로 제공하기로 하였다

 

https://reactdatepicker.com/

 

React Datepicker crafted by HackerOne

 

reactdatepicker.com

이 페이지에 들어가면 눌러보면서 기능이 어떤것인지 얼추 감이 온다.

MUI의 Modal 컴포넌트는 해당 div 외부 클릭시에 닫히는 기능이 지원되는데, 해당 기능을 컨트롤 하려고 한다.

 

일단, Modal

<Modal open={isToggle} onClose={onClose}>

open은 열고 닫기를 조절하는 state이고,(bool)

onClose는 모달이 닫힐때 실행하는 함수이다.

보통은 내용 초기화 등을 진행하였다.

 

그러나 외부 클릭을 조작하려면 일단, onClose에 매개변수를 하나 더 받아야 한다.

<Modal open={isPiggeryToggle} onClose={(_, reason) => onClose(reason)}>

이렇게 넘겨주고 함수 작성하기.

 

type CloseReason = "backdropClick" | "escapeKeyDown" | "closeButtonClick";

const onClose = (value: CloseReason) => {
    // 뒷배경 클릭이나 esc 키로는 닫기가 작동하지 않도록
    if (value === "backdropClick" || value === "escapeKeyDown") {
      return;
    } else {
      setIsToggle(false)
    }
  };

이렇게 작성해서 예외처리 해주면 끝!