interface interactive
This commit is contained in:
123
app/components/InteractiveTextEditor.tsx
Normal file
123
app/components/InteractiveTextEditor.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user