Files
Stirling-PDF/frontend/src/core/tools/annotate/useAnnotationStyleState.ts
T

326 lines
9.8 KiB
TypeScript

import { useCallback, useMemo, useState } from 'react';
import type { AnnotationToolId } from '@app/components/viewer/viewerTypes';
type Size = { width: number; height: number };
export type BuildToolOptionsExtras = {
includeMetadata?: boolean;
stampImageData?: string;
stampImageSize?: Size | null;
};
interface StyleState {
inkColor: string;
inkWidth: number;
highlightColor: string;
highlightOpacity: number;
freehandHighlighterWidth: number;
underlineColor: string;
underlineOpacity: number;
strikeoutColor: string;
strikeoutOpacity: number;
squigglyColor: string;
squigglyOpacity: number;
textColor: string;
textSize: number;
textAlignment: 'left' | 'center' | 'right';
textBackgroundColor: string;
noteBackgroundColor: string;
shapeStrokeColor: string;
shapeFillColor: string;
shapeOpacity: number;
shapeStrokeOpacity: number;
shapeFillOpacity: number;
shapeThickness: number;
}
interface StyleActions {
setInkColor: (value: string) => void;
setInkWidth: (value: number) => void;
setHighlightColor: (value: string) => void;
setHighlightOpacity: (value: number) => void;
setFreehandHighlighterWidth: (value: number) => void;
setUnderlineColor: (value: string) => void;
setUnderlineOpacity: (value: number) => void;
setStrikeoutColor: (value: string) => void;
setStrikeoutOpacity: (value: number) => void;
setSquigglyColor: (value: string) => void;
setSquigglyOpacity: (value: number) => void;
setTextColor: (value: string) => void;
setTextSize: (value: number) => void;
setTextAlignment: (value: 'left' | 'center' | 'right') => void;
setTextBackgroundColor: (value: string) => void;
setNoteBackgroundColor: (value: string) => void;
setShapeStrokeColor: (value: string) => void;
setShapeFillColor: (value: string) => void;
setShapeOpacity: (value: number) => void;
setShapeStrokeOpacity: (value: number) => void;
setShapeFillOpacity: (value: number) => void;
setShapeThickness: (value: number) => void;
}
export type BuildToolOptionsFn = (
toolId: AnnotationToolId,
extras?: BuildToolOptionsExtras
) => Record<string, unknown>;
export interface AnnotationStyleStateReturn {
styleState: StyleState;
styleActions: StyleActions;
buildToolOptions: BuildToolOptionsFn;
getActiveColor: (target: string | null) => string;
}
export const useAnnotationStyleState = (
cssToPdfSize?: (size: Size) => Size
): AnnotationStyleStateReturn => {
const [inkColor, setInkColor] = useState('#1f2933');
const [inkWidth, setInkWidth] = useState(2);
const [highlightColor, setHighlightColor] = useState('#ffd54f');
const [highlightOpacity, setHighlightOpacity] = useState(60);
const [freehandHighlighterWidth, setFreehandHighlighterWidth] = useState(6);
const [underlineColor, setUnderlineColor] = useState('#ffb300');
const [underlineOpacity, setUnderlineOpacity] = useState(100);
const [strikeoutColor, setStrikeoutColor] = useState('#e53935');
const [strikeoutOpacity, setStrikeoutOpacity] = useState(100);
const [squigglyColor, setSquigglyColor] = useState('#00acc1');
const [squigglyOpacity, setSquigglyOpacity] = useState(100);
const [textColor, setTextColor] = useState('#111111');
const [textSize, setTextSize] = useState(14);
const [textAlignment, setTextAlignment] = useState<'left' | 'center' | 'right'>('left');
const [textBackgroundColor, setTextBackgroundColor] = useState<string>('');
const [noteBackgroundColor, setNoteBackgroundColor] = useState('#ffd54f');
const [shapeStrokeColor, setShapeStrokeColor] = useState('#cf5b5b');
const [shapeFillColor, setShapeFillColor] = useState('#0000ff');
const [shapeOpacity, setShapeOpacity] = useState(50);
const [shapeStrokeOpacity, setShapeStrokeOpacity] = useState(50);
const [shapeFillOpacity, setShapeFillOpacity] = useState(50);
const [shapeThickness, setShapeThickness] = useState(2);
const buildToolOptions = useCallback<BuildToolOptionsFn>(
(toolId, extras) => {
const includeMetadata = extras?.includeMetadata ?? true;
const metadata = includeMetadata
? {
customData: {
toolId,
annotationToolId: toolId,
source: 'annotate',
author: 'User',
createdAt: new Date().toISOString(),
modifiedAt: new Date().toISOString(),
},
}
: {};
switch (toolId) {
case 'ink':
return { color: inkColor, thickness: inkWidth, ...metadata };
case 'inkHighlighter':
return {
color: highlightColor,
opacity: highlightOpacity / 100,
thickness: freehandHighlighterWidth,
...metadata,
};
case 'highlight':
return { color: highlightColor, opacity: highlightOpacity / 100, ...metadata };
case 'underline':
return { color: underlineColor, opacity: underlineOpacity / 100, ...metadata };
case 'strikeout':
return { color: strikeoutColor, opacity: strikeoutOpacity / 100, ...metadata };
case 'squiggly':
return { color: squigglyColor, opacity: squigglyOpacity / 100, ...metadata };
case 'text': {
const textAlignNumber = textAlignment === 'left' ? 0 : textAlignment === 'center' ? 1 : 2;
return {
color: textColor,
textColor: textColor,
fontSize: textSize,
textAlign: textAlignNumber,
...(textBackgroundColor ? { fillColor: textBackgroundColor } : {}),
...metadata,
};
}
case 'note': {
const noteFillColor = noteBackgroundColor || 'transparent';
return {
color: textColor,
fillColor: noteFillColor,
opacity: 1,
...metadata,
};
}
case 'square':
case 'circle':
case 'polygon':
return {
color: shapeFillColor,
strokeColor: shapeStrokeColor,
opacity: shapeOpacity / 100,
strokeOpacity: shapeStrokeOpacity / 100,
fillOpacity: shapeFillOpacity / 100,
borderWidth: shapeThickness,
...metadata,
};
case 'line':
case 'polyline':
case 'lineArrow':
return {
color: shapeStrokeColor,
strokeColor: shapeStrokeColor,
opacity: shapeStrokeOpacity / 100,
borderWidth: shapeThickness,
...metadata,
};
case 'stamp': {
const pdfSize =
extras?.stampImageSize && cssToPdfSize ? cssToPdfSize(extras.stampImageSize) : undefined;
return {
imageSrc: extras?.stampImageData,
...(pdfSize ? { imageSize: pdfSize } : {}),
...metadata,
};
}
default:
return { ...metadata };
}
},
[
cssToPdfSize,
freehandHighlighterWidth,
highlightColor,
highlightOpacity,
inkColor,
inkWidth,
noteBackgroundColor,
shapeFillColor,
shapeFillOpacity,
shapeOpacity,
shapeStrokeColor,
shapeStrokeOpacity,
shapeThickness,
squigglyColor,
squigglyOpacity,
strikeoutColor,
strikeoutOpacity,
textAlignment,
textBackgroundColor,
textColor,
textSize,
underlineColor,
underlineOpacity,
]
);
const getActiveColor = useCallback(
(target: string | null) => {
if (target === 'ink') return inkColor;
if (target === 'highlight' || target === 'inkHighlighter') return highlightColor;
if (target === 'underline') return underlineColor;
if (target === 'strikeout') return strikeoutColor;
if (target === 'squiggly') return squigglyColor;
if (target === 'shapeStroke') return shapeStrokeColor;
if (target === 'shapeFill') return shapeFillColor;
if (target === 'textBackground') return textBackgroundColor || '#ffffff';
if (target === 'noteBackground') return noteBackgroundColor || '#ffffff';
return textColor;
},
[
highlightColor,
inkColor,
noteBackgroundColor,
shapeFillColor,
shapeStrokeColor,
squigglyColor,
strikeoutColor,
textBackgroundColor,
textColor,
underlineColor,
]
);
const styleState: StyleState = useMemo(
() => ({
inkColor,
inkWidth,
highlightColor,
highlightOpacity,
freehandHighlighterWidth,
underlineColor,
underlineOpacity,
strikeoutColor,
strikeoutOpacity,
squigglyColor,
squigglyOpacity,
textColor,
textSize,
textAlignment,
textBackgroundColor,
noteBackgroundColor,
shapeStrokeColor,
shapeFillColor,
shapeOpacity,
shapeStrokeOpacity,
shapeFillOpacity,
shapeThickness,
}),
[
freehandHighlighterWidth,
highlightColor,
highlightOpacity,
inkColor,
inkWidth,
noteBackgroundColor,
shapeFillColor,
shapeFillOpacity,
shapeOpacity,
shapeStrokeColor,
shapeStrokeOpacity,
shapeThickness,
squigglyColor,
squigglyOpacity,
strikeoutColor,
strikeoutOpacity,
textAlignment,
textBackgroundColor,
textColor,
textSize,
underlineColor,
underlineOpacity,
]
);
const styleActions: StyleActions = {
setInkColor,
setInkWidth,
setHighlightColor,
setHighlightOpacity,
setFreehandHighlighterWidth,
setUnderlineColor,
setUnderlineOpacity,
setStrikeoutColor,
setStrikeoutOpacity,
setSquigglyColor,
setSquigglyOpacity,
setTextColor,
setTextSize,
setTextAlignment,
setTextBackgroundColor,
setNoteBackgroundColor,
setShapeStrokeColor,
setShapeFillColor,
setShapeOpacity,
setShapeStrokeOpacity,
setShapeFillOpacity,
setShapeThickness,
};
return {
styleState,
styleActions,
buildToolOptions,
getActiveColor,
};
};