| 1 | "use client"; |
| 2 | |
| 3 | import { Baseline, PaintBucket, RotateCcw } from "lucide-react"; |
| 4 | import { KEYS } from "platejs"; |
| 5 | |
| 6 | import { |
| 7 | BUTTON_ELEMENT, |
| 8 | CONTRIBUTOR_ELEMENT, |
| 9 | LABEL_ELEMENT, |
| 10 | PRESENTATION_TITLE_ELEMENT, |
| 11 | } from "@/components/notebook/presentation/editor/lib"; |
| 12 | import { ToolbarButton, ToolbarGroup } from "@/components/plate/ui/toolbar"; |
| 13 | import ColorPicker from "@/components/ui/color-picker"; |
| 14 | import { useToolbarContext } from "./ToolbarContext"; |
| 15 | |
| 16 | const BACKGROUND_COLOR_ELEMENT_TYPES = new Set<string>([ |
| 17 | BUTTON_ELEMENT, |
| 18 | CONTRIBUTOR_ELEMENT, |
| 19 | KEYS.callout, |
| 20 | LABEL_ELEMENT, |
| 21 | PRESENTATION_TITLE_ELEMENT, |
| 22 | ]); |
| 23 | const DEFAULT_PICKER_COLOR = "#3b82f6"; |
| 24 | |
| 25 | function isHexColor(value: string) { |
| 26 | return /^#[0-9a-f]{6}$/i.test(value); |
| 27 | } |
| 28 | |
| 29 | export function ColorControl() { |
| 30 | const { |
| 31 | element, |
| 32 | elementType, |
| 33 | currentBackgroundColor, |
| 34 | currentColor, |
| 35 | currentTextColor, |
| 36 | handleNodePropertyUpdate, |
| 37 | } = useToolbarContext(); |
| 38 | const shouldShowBackgroundColor = |
| 39 | BACKGROUND_COLOR_ELEMENT_TYPES.has(elementType); |
| 40 | const hasCustomBackgroundColor = |
| 41 | (element as { backgroundColor?: string })?.backgroundColor !== undefined; |
| 42 | const hasCustomTextColor = |
| 43 | (element as { textColor?: string })?.textColor !== undefined; |
| 44 | const hasCustomColor = (element as { color?: string })?.color !== undefined; |
| 45 | const colorProperty = shouldShowBackgroundColor ? "textColor" : "color"; |
| 46 | const colorValue = shouldShowBackgroundColor |
| 47 | ? currentTextColor |
| 48 | : currentColor; |
| 49 | const pickerColor = isHexColor(colorValue) |
| 50 | ? colorValue |
| 51 | : DEFAULT_PICKER_COLOR; |
| 52 | const hasCustomEditableColor = shouldShowBackgroundColor |
| 53 | ? hasCustomTextColor || hasCustomColor |
| 54 | : hasCustomColor; |
| 55 | |
| 56 | return ( |
| 57 | <ToolbarGroup> |
| 58 | {shouldShowBackgroundColor && ( |
| 59 | <ColorPicker |
| 60 | value={currentBackgroundColor} |
| 61 | onChange={(color) => { |
| 62 | handleNodePropertyUpdate("backgroundColor", color); |
| 63 | }} |
| 64 | > |
| 65 | <ToolbarButton tooltip="Choose Background Color" size="sm"> |
| 66 | <PaintBucket className="size-4" /> |
| 67 | </ToolbarButton> |
| 68 | </ColorPicker> |
| 69 | )} |
| 70 | |
| 71 | {shouldShowBackgroundColor && hasCustomBackgroundColor && ( |
| 72 | <ToolbarButton |
| 73 | onClick={() => { |
| 74 | handleNodePropertyUpdate("backgroundColor", undefined); |
| 75 | }} |
| 76 | tooltip="Reset Background Color" |
| 77 | size="sm" |
| 78 | className="gap-1" |
| 79 | > |
| 80 | <RotateCcw className="size-4" /> |
| 81 | </ToolbarButton> |
| 82 | )} |
| 83 | |
| 84 | <ColorPicker |
| 85 | value={pickerColor} |
| 86 | onChange={(color) => { |
| 87 | handleNodePropertyUpdate(colorProperty, color); |
| 88 | }} |
| 89 | > |
| 90 | <ToolbarButton tooltip="Choose Color" size="sm"> |
| 91 | {shouldShowBackgroundColor ? ( |
| 92 | <Baseline className="size-4" /> |
| 93 | ) : ( |
| 94 | <span |
| 95 | className="size-4 rounded-[3px] border border-border shadow-inner" |
| 96 | style={{ backgroundColor: colorValue }} |
| 97 | /> |
| 98 | )} |
| 99 | </ToolbarButton> |
| 100 | </ColorPicker> |
| 101 | |
| 102 | {hasCustomEditableColor && ( |
| 103 | <ToolbarButton |
| 104 | onClick={() => { |
| 105 | handleNodePropertyUpdate(colorProperty, undefined); |
| 106 | if (shouldShowBackgroundColor) { |
| 107 | handleNodePropertyUpdate("color", undefined); |
| 108 | } |
| 109 | }} |
| 110 | tooltip="Reset Color" |
| 111 | size="sm" |
| 112 | className="gap-1" |
| 113 | > |
| 114 | <RotateCcw className="size-4" /> |
| 115 | </ToolbarButton> |
| 116 | )} |
| 117 | </ToolbarGroup> |
| 118 | ); |
| 119 | } |
| 120 |