new interactive
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user