new interactive

This commit is contained in:
Biqoz
2025-09-15 19:05:59 +02:00
parent 130929b756
commit 050474e95b
16 changed files with 746 additions and 330 deletions

View File

@@ -1,7 +1,7 @@
import React, { useState, useRef, useEffect } from "react";
import { Trash2, Check, RotateCcw } from "lucide-react";
import { COLOR_PALETTE, type ColorOption } from "../config/colorPalette";
// import { EntityMapping } from "../config/entityLabels"; // SUPPRIMER cette ligne
import { EntityMapping } from "../config/entityLabels";
interface ContextMenuProps {
contextMenu: {
@@ -12,7 +12,7 @@ interface ContextMenuProps {
wordIndices: number[];
};
existingLabels: string[];
// entityMappings: EntityMapping[]; // SUPPRIMER cette ligne
entityMappings?: EntityMapping[];
onApplyLabel: (displayName: string, applyToAll?: boolean) => void;
onApplyColor: (
color: string,
@@ -28,15 +28,16 @@ const colorOptions: ColorOption[] = COLOR_PALETTE;
export const ContextMenu: React.FC<ContextMenuProps> = ({
contextMenu,
existingLabels,
// entityMappings, // SUPPRIMER cette ligne
entityMappings,
onApplyLabel,
onApplyColor,
onRemoveLabel,
getCurrentColor,
}) => {
const [customLabel, setCustomLabel] = useState("");
const [showNewLabelInput, setShowNewLabelInput] = useState(false);
const [newLabelValue, setNewLabelValue] = useState("");
const [showColorPalette, setShowColorPalette] = useState(false);
const [tempSelectedColor, setTempSelectedColor] = useState('');
const [applyToAll, setApplyToAll] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
@@ -55,16 +56,27 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
e.preventDefault();
e.stopPropagation();
}
if (customLabel.trim()) {
if (newLabelValue.trim()) {
console.log(
"Application du label personnalisé:",
customLabel.trim(),
newLabelValue.trim(),
"À toutes les occurrences:",
applyToAll
);
onApplyLabel(customLabel.trim(), applyToAll); // CORRIGER: 2 paramètres seulement
setCustomLabel("");
// Appliquer d'abord le label
onApplyLabel(newLabelValue.trim(), applyToAll);
// Puis appliquer la couleur temporaire si elle existe
if (tempSelectedColor) {
setTimeout(() => {
onApplyColor(tempSelectedColor, 'Couleur personnalisée', applyToAll);
}, 100);
}
setNewLabelValue("");
setShowNewLabelInput(false);
setTempSelectedColor(''); // Reset de la couleur temporaire
}
};
@@ -74,7 +86,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
e.stopPropagation();
console.log("Annulation du nouveau label");
setShowNewLabelInput(false);
setCustomLabel("");
setNewLabelValue("");
};
// Fonction pour empêcher la propagation des événements
@@ -146,36 +158,47 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
<div className="h-6 w-px bg-gray-300 flex-shrink-0"></div>
{/* Labels existants */}
{existingLabels.length > 0 && (
<>
<div className="flex-shrink-0">
<select
onChange={(e) => {
e.stopPropagation();
if (e.target.value) {
const selectedDisplayName = e.target.value; // displayName
// CORRECTION: Plus besoin de chercher entity_type !
onApplyLabel(selectedDisplayName, applyToAll);
}
}}
onClick={(e) => e.stopPropagation()}
className="text-xs border border-gray-300 rounded px-2 py-1 bg-white focus:outline-none focus:ring-1 focus:ring-blue-500"
defaultValue=""
>
<option value="" disabled>
Choisissez un label
</option>
{existingLabels.map((label) => (
<option key={label} value={label}>
{label}
</option>
))}
</select>
</div>
<div className="h-6 w-px bg-gray-300 flex-shrink-0"></div>
</>
)}
{/* Labels existants - toujours visible */}
<div className="flex-shrink-0">
<select
onChange={(e) => {
e.stopPropagation();
if (e.target.value) {
const selectedDisplayName = e.target.value;
console.log("📋 Label sélectionné:", selectedDisplayName);
// Appliquer d'abord le label
onApplyLabel(selectedDisplayName, applyToAll);
// Puis appliquer la couleur temporaire si elle existe
if (tempSelectedColor) {
setTimeout(() => {
onApplyColor(tempSelectedColor, 'Couleur personnalisée', applyToAll);
}, 100);
}
// Reset du select et de la couleur temporaire
e.target.value = "";
setTempSelectedColor('');
}
}}
onClick={(e) => e.stopPropagation()}
className="text-xs border border-blue-300 rounded px-2 py-1 bg-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 min-w-[120px] cursor-pointer hover:bg-blue-100 transition-colors"
defaultValue=""
>
<option value="" disabled className="text-gray-500">
{existingLabels.length > 0
? `📋 Labels (${existingLabels.length})`
: "📋 Aucun label"}
</option>
{existingLabels.map((label) => (
<option key={label} value={label} className="text-gray-800">
{label}
</option>
))}
</select>
</div>
<div className="h-6 w-px bg-gray-300 flex-shrink-0"></div>
{/* Nouveau label */}
<div className="flex-shrink-0">
@@ -207,10 +230,10 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
<input
ref={inputRef}
type="text"
value={customLabel}
value={newLabelValue}
onChange={(e) => {
e.stopPropagation();
setCustomLabel(e.target.value);
setNewLabelValue(e.target.value);
}}
onKeyDown={(e) => {
e.stopPropagation();
@@ -232,7 +255,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
type="button"
onClick={handleApplyCustomLabel}
onMouseDown={(e) => e.stopPropagation()}
disabled={!customLabel.trim()}
disabled={!newLabelValue.trim()}
className="px-1 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors focus:outline-none focus:ring-1 focus:ring-blue-500"
title="Appliquer le label"
>
@@ -260,7 +283,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
type="button"
className="w-5 h-5 rounded-full border-2 border-gray-300 cursor-pointer hover:border-gray-400 transition-all"
style={{
backgroundColor: getCurrentColor(contextMenu.selectedText),
backgroundColor: tempSelectedColor || getCurrentColor(contextMenu.selectedText),
}}
onClick={(e) => {
e.stopPropagation();
@@ -273,21 +296,36 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
{showColorPalette && (
<div className="flex items-center space-x-1 bg-gray-50 p-1 rounded border absolute z-10 mt-1 left-0">
{colorOptions.map((color) => (
<button
key={color.value}
type="button"
onClick={(e) => {
e.stopPropagation();
onApplyColor(color.value, color.name, applyToAll);
setShowColorPalette(false);
}}
onMouseDown={(e) => e.stopPropagation()}
className="w-4 h-4 rounded-full border-2 border-gray-300 cursor-pointer hover:border-gray-400 transition-all"
style={{ backgroundColor: color.value }}
title={color.name}
/>
))}
{colorOptions.map((color) => {
return (
<button
key={color.value}
type="button"
onClick={(e) => {
e.stopPropagation();
// Vérifier si le texte a déjà un mapping (modification)
const existingMapping = entityMappings?.find(mapping =>
mapping.text === contextMenu.selectedText
);
if (existingMapping) {
// MODIFICATION : Appliquer directement la couleur
onApplyColor(color.value, color.name, applyToAll);
} else {
// CRÉATION : Juste stocker la couleur temporaire
setTempSelectedColor(color.value);
}
setShowColorPalette(false);
}}
onMouseDown={(e) => e.stopPropagation()}
className="w-4 h-4 rounded-full border-2 border-gray-300 cursor-pointer hover:border-gray-400 transition-all"
style={{ backgroundColor: color.value }}
title={color.name}
/>
);
})}
</div>
)}
</div>