Files
Anonyme/app/components/InteractiveTextEditor.tsx
2025-09-07 12:30:23 +02:00

124 lines
3.4 KiB
TypeScript

import React, { useState, useRef, useCallback } from "react";
import { EntityMapping } from "@/app/config/entityLabels";
import { useTextParsing } from "./hooks/useTextParsing";
import { useContextMenu } from "./hooks/useContextMenu";
import { useColorMapping } from "./hooks/useColorMapping";
import { TextDisplay } from "./TextDisplay";
import { ContextMenu } from "./ContextMenu";
import { InstructionsPanel } from "./InstructionsPanel";
interface InteractiveTextEditorProps {
text: string;
entityMappings: EntityMapping[];
onUpdateMapping: (
originalValue: string,
newLabel: string,
entityType: string,
applyToAllOccurrences?: boolean,
customColor?: string // Ajouter ce paramètre
) => void;
onRemoveMapping?: (originalValue: string) => void;
}
export const InteractiveTextEditor: React.FC<InteractiveTextEditorProps> = ({
text,
entityMappings,
onUpdateMapping,
onRemoveMapping,
}) => {
const [selectedWords, setSelectedWords] = useState<Set<number>>(new Set());
const [hoveredWord, setHoveredWord] = useState<number | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
const { words } = useTextParsing(text, entityMappings);
const { getCurrentColor } = useColorMapping(entityMappings); // CORRECTION: Passer entityMappings
const {
contextMenu,
showContextMenu,
applyLabel,
applyColorDirectly,
removeLabel,
getExistingLabels,
} = useContextMenu({
entityMappings,
words, // NOUVEAU: passer les mots
onUpdateMapping,
onRemoveMapping,
getCurrentColor,
setSelectedWords,
});
const handleWordClick = useCallback(
(index: number, event: React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
if (event.ctrlKey || event.metaKey) {
setSelectedWords((prev) => {
const newSet = new Set(prev);
if (newSet.has(index)) {
newSet.delete(index);
} else {
newSet.add(index);
}
return newSet;
});
} else {
setSelectedWords(new Set([index]));
}
},
[]
);
const handleContextMenu = useCallback(
(event: React.MouseEvent) => {
event.preventDefault();
if (selectedWords.size === 0) return;
const selectedText = Array.from(selectedWords)
.map((index) => {
const word = words[index];
return word?.isEntity ? word.text : word?.text;
})
.filter(Boolean)
.join(" ");
showContextMenu({
x: event.clientX,
y: event.clientY,
selectedText,
wordIndices: Array.from(selectedWords),
});
},
[selectedWords, words, showContextMenu]
);
return (
<div ref={containerRef} className="relative">
<InstructionsPanel />
<TextDisplay
words={words}
text={text}
selectedWords={selectedWords}
hoveredWord={hoveredWord}
onWordClick={handleWordClick}
onContextMenu={handleContextMenu}
onWordHover={setHoveredWord}
/>
{contextMenu.visible && (
<ContextMenu
contextMenu={contextMenu}
existingLabels={getExistingLabels()}
// entityMappings={entityMappings} // SUPPRIMER cette ligne
onApplyLabel={applyLabel}
onApplyColor={applyColorDirectly}
onRemoveLabel={removeLabel}
getCurrentColor={getCurrentColor}
/>
)}
</div>
);
};