From c14bb953c5f01b2f1fcbc716f3ba14187629c152 Mon Sep 17 00:00:00 2001 From: Alastair Cottier Date: Fri, 8 Mar 2024 20:10:09 -0700 Subject: [PATCH 1/2] Color Picker Component --- src/components/ColorPicker.tsx | 190 +++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/components/ColorPicker.tsx diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx new file mode 100644 index 0000000..fd002d8 --- /dev/null +++ b/src/components/ColorPicker.tsx @@ -0,0 +1,190 @@ +import clsx from "clsx"; +import { useState, useEffect, useRef, useCallback } from "react"; + +interface ColorCircleProps { + /** The color of the circle */ + color: string; +} +function ColorCircle({ color }: ColorCircleProps) { + // Circle style + const circle = "h-6 w-6 rounded-full"; + + return
; +} + +interface ColorPickerOptionProps { + /** The color option */ + color: string; + + /** The function to call when the option is clicked */ + handleClick: (color: string) => void; +} +function ColorPickerOption({ color, handleClick }: ColorPickerOptionProps) { + // Body style + const body = + "p-1 bg-inherit rounded-lg outline outline-1 outline-transparent"; + + // Body style (on hover) + const bodyOnHover = + "hover:bg-neutrals-light-400 hover:outline-neutrals-dark-100 transition transition-all duration-200 ease-in-out"; + + return ( + + ); +} + +interface ColorPickerDropdownProps { + /** The ref to the dropdown */ + dropdownRef: React.RefObject; + + /** The function to call when the option is clicked */ + handleClick: (color: string) => void; +} +function ColorPickerDropdown({ + dropdownRef, + handleClick, +}: ColorPickerDropdownProps) { + // Body style + const body = + "flex flex-col gap-1 p-2 bg-neutrals-light-100 rounded-lg outline outline-1 outline-neutrals-dark-100 absolute -left-4 z-10 transition transition-all duration-200 ease-in-out"; + + // Row style + const row = "flex flex-row gap-1"; + + return ( +
+
+ + + + +
+
+ + + + +
+
+ ); +} + +interface ColorPickerButtonProps { + /** The color currently selected */ + selectedColor: string; + + /** The ref to the button */ + buttonRef: React.RefObject; + + /** The function to call when the button is clicked */ + handleClick: () => void; +} +function ColorPickerButton({ + selectedColor, + buttonRef, + handleClick, +}: ColorPickerButtonProps) { + // Body style + const body = + "flex flex-row p-2 mb-[1px] gap-2.5 bg-neutrals-light-100 hover:bg-neutrals-light-400 rounded-lg outline outline-1 outline-neutrals-dark-100"; + + // Text style + const text = "text-neutrals-dark-300 text-body-sm leading-6"; + + return ( + + ); +} + +function ColorPicker() { + // State for the color picker + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [selectedColor, setSelectedColor] = useState("bg-profile-air"); + + // Refs for the button and the dropdown + const buttonRef = useRef(null); + const dropdownRef = useRef(null); + + // Memoize the handleClickOutside function + const handleClickOutside = useCallback((event: MouseEvent) => { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) && + buttonRef.current && + !buttonRef.current.contains(event.target as Node) + ) { + setIsDropdownOpen(false); + } + }, []); + + // Use useEffect to add an event listener that closes the dropdown when clicking outside + useEffect(() => { + if (isDropdownOpen) { + document.addEventListener("click", handleClickOutside); + } + + return () => document.removeEventListener("click", handleClickOutside); + }, [isDropdownOpen, handleClickOutside]); + + // Body style + const body = "relative"; + + return ( +
+ setIsDropdownOpen(!isDropdownOpen)} + /> + {isDropdownOpen && ( + + )} +
+ ); +} + +export default ColorPicker; From b5ea1c97fb7c2c24b1cb70d0aac8312db9853cb6 Mon Sep 17 00:00:00 2001 From: Alastair Cottier Date: Fri, 8 Mar 2024 20:10:32 -0700 Subject: [PATCH 2/2] Color Picker Test Route --- src/App.tsx | 5 +++++ src/routes/ColorPickerTestRoute.tsx | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/routes/ColorPickerTestRoute.tsx diff --git a/src/App.tsx b/src/App.tsx index 3c65eac..b62f9fe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import NavigationTest from "@/routes/NavigationTest"; import InputCodeTest from "@/routes/InputCodeTest"; import ToastTestRoute from "@/routes/ToastTestRoute"; import MediaUploadZoneTestRoute from "@/routes/MediaUploadZoneTestRoute"; +import ColorPickerTestRoute from "@/routes/ColorPickerTestRoute"; const router = createBrowserRouter([ { @@ -50,6 +51,10 @@ const router = createBrowserRouter([ path: "/toast-test", element: , }, + { + path: "/color-picker-test", + element: , + }, ]); function App() { diff --git a/src/routes/ColorPickerTestRoute.tsx b/src/routes/ColorPickerTestRoute.tsx new file mode 100644 index 0000000..3fcdb21 --- /dev/null +++ b/src/routes/ColorPickerTestRoute.tsx @@ -0,0 +1,11 @@ +import ColorPicker from "@/components/ColorPicker"; + +function ColorPickerTestRoute() { + return ( +
+ +
+ ); +} + +export default ColorPickerTestRoute;