calcom/components/ui/colorpicker.tsx
Peer Richelsen 51d553559f
tailwind prettier (#1646)
* tailwind prettier

* Minor fixes

* Sorts components and pages

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2022-02-09 00:05:13 +00:00

100 lines
3.3 KiB
TypeScript

import { useCallback, useRef, useState } from "react";
import { useEffect } from "react";
import { HexColorInput, HexColorPicker } from "react-colorful";
import { isValidHexCode, fallBackHex } from "@components/CustomBranding";
import Swatch from "@components/Swatch";
type Handler = (event: MouseEvent | Event) => void;
function useEventListener<
KW extends keyof WindowEventMap,
KH extends keyof HTMLElementEventMap,
T extends HTMLElement | void = void
>(
eventName: KW | KH,
handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
element?: React.RefObject<T>
) {
// Create a ref that stores handler
const savedHandler = useRef<typeof handler>();
useEffect(() => {
// Define the listening target
const targetElement: T | Window = element?.current || window;
if (!(targetElement && targetElement.addEventListener)) {
return;
}
// Update saved handler if necessary
if (savedHandler.current !== handler) {
savedHandler.current = handler;
}
// Create event listener that calls handler function stored in ref
const eventListener: typeof handler = (event) => {
// eslint-disable-next-line no-extra-boolean-cast
if (!!savedHandler?.current) {
savedHandler.current(event);
}
};
targetElement.addEventListener(eventName, eventListener);
// Remove event listener on cleanup
return () => {
targetElement.removeEventListener(eventName, eventListener);
};
}, [eventName, element, handler]);
}
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: React.RefObject<T>,
handler: Handler,
mouseEvent: "mousedown" | "mouseup" = "mousedown"
): void {
useEventListener(mouseEvent, (event) => {
const el = ref?.current;
// Do nothing if clicking ref's element or descendent elements
if (!el || el.contains(event.target as Node)) {
return;
}
handler(event);
});
}
export type ColorPickerProps = {
defaultValue: string;
onChange: (text: string) => void;
};
const ColorPicker = (props: ColorPickerProps) => {
const init = !isValidHexCode(props.defaultValue) ? fallBackHex(props.defaultValue) : props.defaultValue;
const [color, setColor] = useState(init);
const [isOpen, toggle] = useState(false);
const popover = useRef() as React.MutableRefObject<HTMLInputElement>;
const close = useCallback(() => toggle(false), []);
useOnClickOutside(popover, close);
return (
<div className="relative mt-1 flex items-center justify-center">
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
{isOpen && (
<div className="popover" ref={popover}>
<HexColorPicker
className="!absolute !top-10 !left-0 !z-10 !h-32 !w-32"
color={color}
onChange={(val) => {
setColor(val);
props.onChange(val);
}}
/>
</div>
)}
<HexColorInput
className="ml-1 block w-full rounded-sm border border-gray-300 px-3 py-2 shadow-sm focus:border-neutral-800 focus:outline-none focus:ring-neutral-800 sm:text-sm"
color={color}
onChange={(val) => {
setColor(val);
props.onChange(val);
}}
type="text"
/>
</div>
);
};
export default ColorPicker;