diff --git a/src/components/ActivityButtons.tsx b/src/components/ActivityButtons.tsx index 6e6b0ddf..f51c1673 100644 --- a/src/components/ActivityButtons.tsx +++ b/src/components/ActivityButtons.tsx @@ -1,22 +1,24 @@ import { Button, ButtonGroup, + ColorPicker, Flex, Group, HStack, Heading, Input, + Portal, + Select, Stack, Text, createListCollection, parseColor, - ColorPicker, - Select, - Portal, } from "@chakra-ui/react"; import type { ComponentPropsWithoutRef, FormEvent } from "react"; import { useContext, useLayoutEffect, useState } from "react"; +import { ColorPickerInput } from "./ui/colorpicker-input"; + import { LuCheck as CheckIcon, LuX as CloseIcon } from "react-icons/lu"; import { Checkbox } from "./ui/checkbox"; import { Field } from "./ui/field"; @@ -213,7 +215,7 @@ function ActivityColor(props: { activity: Activity; onHide: () => void }) { > - + diff --git a/src/components/ui/colorpicker-input.tsx b/src/components/ui/colorpicker-input.tsx new file mode 100644 index 00000000..a9ef209d --- /dev/null +++ b/src/components/ui/colorpicker-input.tsx @@ -0,0 +1,53 @@ +import { + ColorPicker, + parseColor, + useColorPickerContext, +} from "@chakra-ui/react"; +import { forwardRef } from "react"; + +export const ColorPickerInput = forwardRef< + HTMLInputElement, + Omit +>(function ColorHexInput(props, ref) { + const { setValue } = useColorPickerContext(); + + return ( + { + const input = e.target.value; + if ( + input.length === 6 || + (input.length === 7 && input.startsWith("#")) + ) { + // parseColor will throw if the value is not a valid hex color + try { + let caretPositionBefore = e.target.selectionStart; + let colorToParse = input; + if (!colorToParse.startsWith("#")) { + colorToParse = `#${colorToParse}`; + caretPositionBefore = caretPositionBefore + ? caretPositionBefore + 1 + : caretPositionBefore; + } + setValue(parseColor(colorToParse)); + setTimeout(() => { + try { + e.target.setSelectionRange( + caretPositionBefore, + caretPositionBefore, + ); + } catch (error) { + console.error("Error setting selection range:", error); + } + }, 0); + } catch { + return; + } + } + }} + channel="hex" + ref={ref} + {...props} + /> + ); +});