[가계Boo] mui-color-input 라이브러리로 Color Picker 만들기
캘린더와 일별 가계부 목록에서 포인트 컬러 커스터마이징 기능을 추가하기 위해 Color Picker 컴포넌트를 만들어야 했다.
최대한 컴포넌트도 직접 만들어서 적용해 보자 생각했으나, 도저히 깔끔하게 만들 자신이 없어 이 부분은 잘 만들어진 라이브러리를 가져다 쓰기로 결정.
몇몇 대안이 있었으나 mui 공식문서에 올라가 있는 라이브러리인 mui-color-input을 사용하기로 결정했다.
https://viclafouch.github.io/mui-color-input/docs/api-reference/
API Reference | MUI color input
This article discusses the API and props of MuiColorInput. Props are defined within MuiColorInputProps.
viclafouch.github.io
문서에 올라와 있는 예제를 확인해보니, 기본적인 사용 방식은 PC 브라우저에서 인풋박스에 RGB 값을 텍스트롱 입력받거나, Color picker 팝업을 띄워 선택할 수 있도록하는 것이었다.
그렇지만 지금 만들고 있는 커스터마이징 기능에서는 정확한 세부 RGB 값이 필요하지 않았고, 만들고 있는 가계부가 모바일 웹 전용이기 때문에 텍스트 인풋박스는 사용하지 않고 Color Picker 팝업만 사용할 수 있도록 아래 두 가지 처리가 필요했다.
1. CSS로 Input text field를 숨김
2. Popup Close event를 잡아 선택된 컬러를 저장하는 API 호출
첫 번째 CSS 적용은 styled를 이용해 input text field 부분에 display: none을 주어 해결했다.
두 번째 작업을 위해 라이브러리 문서를 살펴봤으나 onPopupClose와 같은 이벤트 리스너 관련 설명이 없었다,,,
그런데 다행히 github issue에 나와 같은 상황에 놓인 사람이 먼저 올려둔 질문을 발견했다.
올라와 있는 답변으로는 popup close시에 자동으로 text field에 focus되도록 처리되어 있다는 것에 착안해 onFocus 이벤트 리스너를 대신 사용하는 것으로 문제를 해결했다는 답변을 발견. 바로 적용해보았다.
그런데,,, input text field에 focus 되었다는 이벤트가 발생하질 않는다. 몇 가지 가능성을 두고 체크를 해봤는데, display: none 으로 숨겨두었던게 문제였던 것 같아 해당 부분을 opacity: 0으로 처리 방식을 바꾸었다.
import { useState } from "react";
import { MuiColorInput } from "mui-color-input";
import "components/style/ColorPicker.css";
import styled from "@emotion/styled";
import { useDispatch } from "react-redux";
import { openSuccessAlert } from "store/slice/clientInfo";
const MuiColorInputStyled = styled(MuiColorInput)`
& .MuiInputBase-input {
opacity: 0;
}
& .MuiButtonBase-root {
width: 40px;
height: 40px;
border-radius: 20px 20px 20px 20px;
position: relative;
}
& .MuiInputBase-root fieldset {
display: none;
}
`;
export default function ColorPicker({
value = "#ffffff",
setValue,
selectColor,
}) {
const handleChange = (newValue) => {
setValue(newValue);
};
const [initialValue, setInitialValue] = useState(value);
const dispatch = useDispatch();
return (
<MuiColorInputStyled
isAlphaHidden
format="hex"
onFocus={() => {
dispatch(openSuccessAlert("컬러가 설정되었습니다."));
}}
value={value}
onChange={handleChange}
size="large"
variant="outlined"
/>
);
}
의도했던대로 Popup이 닫히는 시점을 잡아 API를 호출할 수는 있겠으나,,, 모바일 환경에서 input text field에 focus가 가면 터치 키보드가 올라온다는 사실을 까먹스 해버린 것
그렇다면 API 호출하는 부분에서 focus된 element에 blur를 주면 어떨까?
import { useState } from "react";
import { MuiColorInput } from "mui-color-input";
import "components/style/ColorPicker.css";
import styled from "@emotion/styled";
import { useDispatch } from "react-redux";
import { openSuccessAlert } from "store/slice/clientInfo";
const MuiColorInputStyled = styled(MuiColorInput)`
& .MuiInputBase-input {
opacity: 0;
}
& .MuiButtonBase-root {
width: 40px;
height: 40px;
border-radius: 20px 20px 20px 20px;
position: relative;
}
& .MuiInputBase-root fieldset {
display: none;
}
`;
export default function ColorPicker({
value = "#ffffff",
setValue,
selectColor,
}) {
const handleChange = (newValue) => {
setValue(newValue);
};
const [initialValue, setInitialValue] = useState(value);
const dispatch = useDispatch();
return (
<MuiColorInputStyled
isAlphaHidden
format="hex"
onFocus={(e) => {
e.target.blur();
dispatch(openSuccessAlert("컬러가 설정되었습니다."));
}}
value={value}
onChange={handleChange}
size="large"
variant="outlined"
/>
);
}
키보드가 올라와 놓고 멀뚱 해버리는 상황보다는 낫지만 주소 표시줄이 내려갔다 올라오면서 깜빡 거린다는 느낌을 준다.
영 별로라 직접 라이브러리 컴포넌트 코드를 볼 수는 없을까 고민하다 VS Code에서 Mac의 옵션키를 누른상태로 import한 라이브러리 모듈이름을 누르니 정의된 부분으로 넘어갈 수 있었다.
그 중 눈에 띈 PopoverProps라는 녀석
한 번 더 들어가보자.
onClose,,, 너무 반갑다.
코드로 정의되어 있는 스펙을 따라 적용해보자.
onFocus를 사용하지 않을테니 opacity: 0으로 숨겨뒀던 input text field도 display: none으로 다시 바꿔준다.
import { useEffect, useRef, useState } from "react";
import { MuiColorInput } from "mui-color-input";
import "components/style/ColorPicker.css";
import styled from "@emotion/styled";
const MuiColorInputStyled = styled(MuiColorInput)`
& .MuiInputBase-input {
display: none;
}
& .MuiButtonBase-root {
width: 40px;
height: 40px;
border-radius: 20px 20px 20px 20px;
position: relative;
}
& .MuiInputBase-root fieldset {
display: none;
}
`;
export default function ColorPicker({
value = "#ffffff",
setValue,
selectColor,
}) {
const handleChange = (newValue) => {
setValue(newValue);
};
const [initialValue, setInitialValue] = useState(value);
return (
<MuiColorInputStyled
isAlphaHidden
format="hex"
PopoverProps={{
onClose: () => {
selectColor(value, initialValue);
},
}}
value={value}
onChange={handleChange}
size="large"
variant="outlined"
/>
);
}
원했던대로 깔끔하게 Color Picker 컴포넌트를 구성했다.
성공적으로 처리하고 나니 Github issue글이 생각이 났다.
질문이 올라온지는 몇 달 정도 지났지만 onFocus를 사용해서 해결했다는 답변은 바로 지난달에 올라왔던 것으로 기억해 이 해결책이 도움이 되었으면 좋겠다는 생각이 들었고 소소한 영어 실력으로 해당 issue에 여차저차 코멘트를 달았다,,,,
다음날 메일로 github에서 알림이 한 통 왔는데
오잉 Issue가 close되었다?
프로젝트 오너가 내가 단 코멘트를 확인하고 따봉과 함께 이슈를 직접 클로즈해줬다,,
onFocus 해결책을 코멘트했던 사람도 함께 따봉을 줬다!
혼자 포스팅이나 조금씩 올렸지 다른 프로젝트에 코멘트를 달고 코멘트에 대한 피드백을 받은 것은 처음이라 얼떨떨하기도 하고 내가 올린 해결책이 답이었구나 하는 생각에 기분도 좋고 다른 사람에게 영향을 주었단 사실이 뭔가 뿌듯하고 설렜다.
그래서 Backend 위주로 포스팅을 하려 했으나, Frontend 작업임에도 불구하고 이렇게 포스팅을 남긴다.