From 828a786414b4d5b744d1d68dfe7e286b6944ae56 Mon Sep 17 00:00:00 2001 From: Adorian Doran Date: Thu, 20 Nov 2025 01:35:44 +0200 Subject: [PATCH] client/note color picker menu item: improve --- .../custom-items/NoteColorPickerMenuItem.css | 23 ++++-- .../custom-items/NoteColorPickerMenuItem.tsx | 78 ++++++++++++++---- .../src/menus/custom-items/custom-culor.png | Bin 0 -> 1259 bytes 3 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 apps/client/src/menus/custom-items/custom-culor.png diff --git a/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.css b/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.css index 35029699f..d062ea577 100644 --- a/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.css +++ b/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.css @@ -4,18 +4,22 @@ justify-content: space-between; } -.color-picker-menu-item .color-cell { +.color-picker-menu-item .color-cell { width: 14px; height: 14px; border-radius: 4px; - background: var(--color); + background-color: var(--color); } -.color-picker-menu-item .color-cell.disabled-color-cell { +.color-picker-menu-item .color-cell:not(.selected):hover { + transform: scale(1.2); +} + +.color-picker-menu-item .color-cell.disabled-color-cell { cursor: not-allowed; } -.color-picker-menu-item .color-cell.selected { +.color-picker-menu-item .color-cell.selected { outline: 2px solid var(--color); outline-offset: 2px; } @@ -28,7 +32,7 @@ left: 0; right: 0; bottom: 0; - font-size: 14px; + font-size: 18px; justify-content: center; align-items: center; font-family: boxicons; @@ -50,9 +54,16 @@ position: relative; display: flex; justify-content: center; - background: var(--color); + } +.custom-color-cell.custom-color-cell-empty { + background-image: url(./custom-culor.png); + background-size: cover; + --foreground: transparent; +} + + .custom-color-cell::before { content: "\ed35"; color: var(--foreground); diff --git a/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.tsx b/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.tsx index 3db626013..96f7eba54 100644 --- a/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.tsx +++ b/apps/client/src/menus/custom-items/NoteColorPickerMenuItem.tsx @@ -1,5 +1,5 @@ import "./NoteColorPickerMenuItem.css"; -import { useEffect, useRef, useState} from "preact/hooks"; +import { useCallback, useEffect, useRef, useState} from "preact/hooks"; import {ComponentChildren} from "preact"; import attributes from "../../services/attributes"; import Color, { ColorInstance } from "color"; @@ -22,12 +22,13 @@ export default function NoteColorPickerMenuItem(props: NoteColorPickerMenuItemPr const [note, setNote] = useState(null); const [currentColor, setCurrentColor] = useState(null); + const [isCustomColor, setIsCustomColor] = useState(false); useEffect(() => { const retrieveNote = async (noteId: string) => { - const result = await froca.getNote(noteId, true); - if (result) { - setNote(result); + const noteInstance = await froca.getNote(noteId, true); + if (noteInstance) { + setNote(noteInstance); } } @@ -39,10 +40,27 @@ export default function NoteColorPickerMenuItem(props: NoteColorPickerMenuItemPr }, []); useEffect(() => { - setCurrentColor(note?.getLabel("color")?.value ?? null); + const colorLabel = note?.getLabel("color")?.value ?? null; + if (colorLabel) { + let color: ColorInstance | null = null; + + try { + color = new Color(colorLabel); + } catch(ex) { + console.error(ex); + } + + if (color) { + setCurrentColor(color.hex().toLowerCase()); + } + } }, [note]); - const onColorCellClicked = (color: string | null) => { + useEffect(() => { + setIsCustomColor(COLORS.indexOf(currentColor) === -1); + }, [currentColor]) + + const onColorCellClicked = useCallback((color: string | null) => { if (note) { if (color !== null) { attributes.setLabel(note.noteId, "color", color); @@ -52,7 +70,7 @@ export default function NoteColorPickerMenuItem(props: NoteColorPickerMenuItemPr setCurrentColor(color); } - } + }, [note, currentColor]); return
{e.stopPropagation()}}> @@ -65,7 +83,9 @@ export default function NoteColorPickerMenuItem(props: NoteColorPickerMenuItemPr onSelect={() => onColorCellClicked(color)} /> ))} - +
} @@ -87,28 +107,50 @@ function ColorCell(props: ColorCellProps) { } function CustomColorCell(props: ColorCellProps) { + const [pickedColor, setPickedColor] = useState(null); const colorInput = useRef(null); - let colorInputDebouncer: Debouncer; + const colorInputDebouncer = useRef | null>(null); + const callbackRef = useRef(props.onSelect); useEffect(() => { - colorInputDebouncer = new Debouncer(500, (color) => { - props.onSelect?.(color); + colorInputDebouncer.current = new Debouncer(500, (color) => { + callbackRef.current?.(color); + setPickedColor(color); }); return () => { - colorInputDebouncer.destroy(); + colorInputDebouncer.current?.destroy(); } - }); + }, []); + + useEffect(() => { + if (props.isSelected && pickedColor === null) { + setPickedColor(props.color); + } + }, [props.isSelected]) + + useEffect(() => { + callbackRef.current = props.onSelect; + }, [props.onSelect]); + + const onSelect = useCallback(() => { + if (pickedColor !== null) { + callbackRef.current?.(pickedColor); + } + + colorInput.current?.click(); + }, [pickedColor]); return
- {colorInput.current?.click()}}> + {colorInputDebouncer.updateValue(colorInput.current?.value ?? null)}} + value={pickedColor ?? props.color ?? "#40bfbf"} + onChange={() => {colorInputDebouncer.current?.updateValue(colorInput.current?.value ?? null)}} style="width: 0; height: 0; opacity: 0" />
diff --git a/apps/client/src/menus/custom-items/custom-culor.png b/apps/client/src/menus/custom-items/custom-culor.png new file mode 100644 index 0000000000000000000000000000000000000000..4275c56d2e804b1ad6d1057a9b4ef28534491031 GIT binary patch literal 1259 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1SJ3FdmIK*jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBufiR<}hF1enP_o1|q9iy!t)x7$D3u`~F*C13&(AeP!Bo#s z&(N@oL+l(-(T>yz&ooa@Ed~xChm}E!k(GfF$npYWX($`y91TWhusFy;hKx)Mf?v*lQd?d7fo&prj{uQbo{ikU4Ww!nAkig0G5O#-Ip`Skl- z^cqVpHf+DByGdYezTdS=nHPe!lBW1MXZ4<1p8hTC^3vTq_G~hd+jBC@?#NuDTL*or zg0!x$shB(WRhIP@FYnot&ptKX?knaq)2rz1qbZl7rvQ~N^UGw#+h^ z`fRCN$f~8~T(#9|Mb=x+IzM3%?vpmUI5T?D`6AWYT^DkB_+NzIS+^&2wY8XEVe+v( ziEB&^6Ha)kdT+QQAz_rDaNg_a$CZ%}r+jT&tYynD`O-Xrdi}aH zF)0D6kzQx+Ww>wYN%<1Hf5{vZhu}WRk1@YKT>d%#`_D%c*UDEGSpR){?#^$X)lK2y zb6?fG{Pwr3@@pyU-N&r`t@pQg&9~Q&yZ4xHdhy+sgQt^z@*jU(V4!(wullKk`BT}J z-k!hpbhOmQ^Yu1U|J?Pi+ZnxQ&uQIt1)asgH$}ckyc4^5IHi8xlgiRfnj3FDzx1~M zHn-}(jZfCkK6<%d#QaA?cPNkU`7LTsRJObj4=byjxM!aB``oai%Wr4r{{C~?I5B;7 zMbYsmR?}}ju*qI?Y|AXK#iqrRR_*$tK5hPK_lFsACtN+ZZ@*Qh>d#h|C+dFvW=eDM znoUt(7uoHNnGsqpd01t_#ngg`E%Rrdne=kb&M&hsc$cjcT`ZmRQSSAcuuh4>zcU`} zRI&Q7q-&Ri$Hu}hFWz(iS8-SO{Pp#R@X@EI3ezJ>HD6sgsT!QcK2M|mqUgrvFWlbM zOtYuHSIP_6nDnwYP;ODt)Ku=&(gkbmUe8INd_YQ@Hzrm-Idk3Y%%JjpyY%<>#wGS1 z*zj=2uG%ZPwnEM}nJw4;uFne!G!ANOX?r~D?Yei{mmf)+Te*C1X*kFGLs#Q@F0R=X jHO0U1%b9Xt7x!QPAGsO_tNzdJ1!Y%HS3j3^P6