client/note color picker menu item: improve
Some checks are pending
Checks / main (push) Waiting to run

This commit is contained in:
Adorian Doran 2025-11-20 01:35:44 +02:00
parent c25859cee9
commit 828a786414
3 changed files with 77 additions and 24 deletions

View File

@ -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);

View File

@ -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<FNote | null>(null);
const [currentColor, setCurrentColor] = useState<string | null>(null);
const [isCustomColor, setIsCustomColor] = useState<boolean>(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 <div className="color-picker-menu-item"
onClick={(e) => {e.stopPropagation()}}>
@ -65,7 +83,9 @@ export default function NoteColorPickerMenuItem(props: NoteColorPickerMenuItemPr
onSelect={() => onColorCellClicked(color)} />
))}
<CustomColorCell color={currentColor} isSelected={false} onSelect={onColorCellClicked} />
<CustomColorCell color={currentColor}
isSelected={isCustomColor}
onSelect={onColorCellClicked} />
</div>
}
@ -87,28 +107,50 @@ function ColorCell(props: ColorCellProps) {
}
function CustomColorCell(props: ColorCellProps) {
const [pickedColor, setPickedColor] = useState<string | null>(null);
const colorInput = useRef<HTMLInputElement>(null);
let colorInputDebouncer: Debouncer<string | null>;
const colorInputDebouncer = useRef<Debouncer<string | null> | 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 <div style={`--foreground: ${ensureContrast(props.color)};`}>
<ColorCell {...props}
className="custom-color-cell"
onSelect={() => {colorInput.current?.click()}}>
<ColorCell {...props}
color={pickedColor}
className={`custom-color-cell ${(pickedColor === null) ? "custom-color-cell-empty" : ""}`}
onSelect={onSelect}>
<input ref={colorInput}
type="color"
value={props.color ?? ""}
onChange={() => {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" />
</ColorCell>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB