Skip to content
File

Blob: src/client/components/editor/controllers/color-picker-panel.tsx

typescript60 lines
1import { FloatingPortal } from "@floating-ui/react";
2import { preserveEditorSelectionOnMouseDown, useEditorRectPopover } from "./menu/popover";
3import { type ColorEntry } from "./colors";
4import "../styles/color-picker.css";
5 
6interface ColorPickerPanelProps {
7 colors: ColorEntry[];
8 activeColor: string | null;
9 nullFallback: string;
10 onSelect: (color: string | null) => void;
11 triggerRef: React.RefObject<HTMLButtonElement | null>;
12 onClose: () => void;
13}
14 
15export function ColorPickerPanel({
16 colors,
17 activeColor,
18 nullFallback,
19 onSelect,
20 triggerRef,
21 onClose,
22}: ColorPickerPanelProps) {
23 const { floatingStyles, setFloating } = useEditorRectPopover({
24 open: true,
25 onClose,
26 contextElement: () => triggerRef.current,
27 getAnchorRect: () => triggerRef.current?.getBoundingClientRect() ?? null,
28 offset: 6,
29 });
30 
31 return (
32 <FloatingPortal>
33 <div
34 ref={setFloating}
35 className="tiptap-menu-surface tiptap-color-panel"
36 style={{ ...floatingStyles, zIndex: 60 }}
37 onMouseDownCapture={(e) => preserveEditorSelectionOnMouseDown(e)}
38 >
39 {colors.map((c) => (
40 <button
41 key={c.label}
42 type="button"
43 title={c.label}
44 aria-label={c.label}
45 className={`tiptap-color-swatch${(activeColor ?? null) === c.value ? " is-active" : ""}`}
46 style={{
47 backgroundColor: c.value ?? nullFallback,
48 ...(c.value === null ? { backgroundImage: "linear-gradient(135deg, #747178 25%, transparent 25%)" } : {}),
49 }}
50 onMouseDown={(e) => {
51 e.preventDefault();
52 onSelect(c.value);
53 }}
54 />
55 ))}
56 </div>
57 </FloatingPortal>
58 );
59}