Rescue Paperjet v1 implementation
This commit is contained in:
parent
7ff5c8130d
commit
db9e2ca51b
83 changed files with 6186 additions and 3894 deletions
|
|
@ -1,4 +1,6 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import * as fabric from 'fabric';
|
||||
import type { Canvas } from 'fabric';
|
||||
import { useEditorStore } from '../store';
|
||||
import type { TextAnnotation, TextProps } from '../../../lib/annotations/types';
|
||||
import type { ViewportParams } from '../../../lib/coords';
|
||||
|
|
@ -17,6 +19,7 @@ import {
|
|||
interface TextFormatToolbarProps {
|
||||
annotationId: string;
|
||||
viewportParams: ViewportParams;
|
||||
canvas: Canvas;
|
||||
}
|
||||
|
||||
const FONTS = ['Liberation Sans', 'Outfit', 'Plus Jakarta Sans', 'Arial', 'Times New Roman', 'Courier New'];
|
||||
|
|
@ -24,7 +27,7 @@ const SIZES = [6, 7, 8, 10, 12, 14, 16, 18, 24, 36, 48, 72];
|
|||
const COLORS = ['#000000', '#EF4444', '#3B82F6', '#10B981', '#F59E0B', '#ffffff'];
|
||||
const HIGHLIGHTS = ['transparent', '#FEF08A', '#BBF7D0', '#BFDBFE', '#FBCFE8', '#000000'];
|
||||
|
||||
export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatToolbarProps) {
|
||||
export function TextFormatToolbar({ annotationId, viewportParams, canvas }: TextFormatToolbarProps) {
|
||||
const { annotations, updateAnnotation, deleteAnnotation, addAnnotation, setDefaultTextProps, draftAnnotation } = useEditorStore();
|
||||
|
||||
const [activeDropdown, setActiveDropdown] = useState<'font' | 'size' | 'color' | 'highlight' | null>(null);
|
||||
|
|
@ -51,11 +54,9 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
const top = pt.y - 48; // 48px above
|
||||
const left = pt.x;
|
||||
|
||||
const applyStyle = (styleName: string, value: any, globalPropName: keyof TextProps, globalValue?: any) => {
|
||||
const canvas = (window as any).__fabricCanvas as any;
|
||||
|
||||
if (canvas) {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
const applyStyle = (styleName: string, value: unknown, globalPropName: keyof TextProps, globalValue?: unknown) => {
|
||||
const activeObj = canvas.getActiveObject() as (fabric.Textbox & { id?: string; customHeight?: number }) | undefined;
|
||||
if (activeObj) {
|
||||
if (activeObj && activeObj.id === annotationId) {
|
||||
const isStructural = styleName === 'fontSize' || styleName === 'fontFamily';
|
||||
|
||||
|
|
@ -81,7 +82,7 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
activeObj.styles = {};
|
||||
if (activeObj.hiddenTextarea) {
|
||||
if (styleName === 'fontSize') activeObj.hiddenTextarea.style.fontSize = `${value}px`;
|
||||
if (styleName === 'fontFamily') activeObj.hiddenTextarea.style.fontFamily = value;
|
||||
if (styleName === 'fontFamily') activeObj.hiddenTextarea.style.fontFamily = String(value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -100,7 +101,7 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
for (const line in activeObj.styles) {
|
||||
for (const char in activeObj.styles[line]) {
|
||||
if (activeObj.styles[line][char]) {
|
||||
delete activeObj.styles[line][char][styleName];
|
||||
delete (activeObj.styles[line][char] as Record<string, unknown>)[styleName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,8 +111,8 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
|
||||
// Critical: Fabric 7 heavily caches text. We MUST mark it dirty to force a redraw!
|
||||
activeObj.dirty = true;
|
||||
if ((activeObj as any)._forceClearCache !== undefined) {
|
||||
(activeObj as any)._forceClearCache = true;
|
||||
if ('_forceClearCache' in activeObj) {
|
||||
(activeObj as typeof activeObj & { _forceClearCache?: boolean })._forceClearCache = true;
|
||||
}
|
||||
|
||||
// Remove manual height constraint so the box can grow with the new font size
|
||||
|
|
@ -123,7 +124,7 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
}
|
||||
|
||||
const finalGlobalValue = globalValue !== undefined ? globalValue : value;
|
||||
const newProps = { [globalPropName]: finalGlobalValue } as any;
|
||||
const newProps = { [globalPropName]: finalGlobalValue } as Partial<TextProps>;
|
||||
setDefaultTextProps(newProps);
|
||||
|
||||
// Always update store so the toolbar displays the new value
|
||||
|
|
@ -154,12 +155,9 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
const handleDelete = () => {
|
||||
if (isDraft) {
|
||||
useEditorStore.getState().setDraftAnnotation(null);
|
||||
const canvas = (window as any).__fabricCanvas as any;
|
||||
if (canvas) {
|
||||
const activeObj = canvas.getActiveObject();
|
||||
if (activeObj && activeObj.id === annotationId) {
|
||||
canvas.remove(activeObj);
|
||||
}
|
||||
const activeObj = canvas.getActiveObject();
|
||||
if (activeObj && (activeObj as fabric.FabricObject & { id?: string }).id === annotationId) {
|
||||
canvas.remove(activeObj);
|
||||
}
|
||||
} else {
|
||||
deleteAnnotation(annotationId);
|
||||
|
|
@ -220,7 +218,7 @@ export function TextFormatToolbar({ annotationId, viewportParams }: TextFormatTo
|
|||
<button
|
||||
key={s}
|
||||
className={`w-full text-center px-3 py-1 text-xs hover:bg-blue-50 ${props.fontSize === s ? 'bg-blue-50 text-blue-600 font-medium' : ''}`}
|
||||
onClick={() => { applyStyle('fontSize', s * viewportParams.scale * (viewportParams.dpr || 1), 'fontSize', s); setActiveDropdown(null); }}
|
||||
onClick={() => { applyStyle('fontSize', s * viewportParams.scale, 'fontSize', s); setActiveDropdown(null); }}
|
||||
>
|
||||
{s}
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue