|
import React, { useState, useEffect, useRef } from "react" |
|
import { Replacement } from "../interfaces"; |
|
|
|
interface WordChipProps { |
|
word: string; |
|
logprob: number; |
|
threshold: number; |
|
replacements: Replacement[]; |
|
onReplace: (newWord: string) => Promise<void>; |
|
} |
|
|
|
export function WordChip({ |
|
word, |
|
logprob, |
|
threshold, |
|
replacements, |
|
onReplace |
|
}: WordChipProps) { |
|
const [isExpanded, setIsExpanded] = useState(false); |
|
const [selectedIndex, setSelectedIndex] = useState(-1); |
|
const dropdownRef = useRef<HTMLDivElement>(null); |
|
|
|
useEffect(() => { |
|
const handleClickOutside = (event: MouseEvent) => { |
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { |
|
setIsExpanded(false); |
|
} |
|
}; |
|
|
|
document.addEventListener("mousedown", handleClickOutside); |
|
return () => { |
|
document.removeEventListener("mousedown", handleClickOutside); |
|
}; |
|
}, []); |
|
|
|
const handleClick = () => { |
|
if (logprob < threshold && replacements.length > 0) { |
|
setIsExpanded(true); |
|
} |
|
} |
|
|
|
const handleReplacement = async (newWord: string) => { |
|
console.log("handleReplacement", newWord); |
|
await onReplace(newWord); |
|
setIsExpanded(false); |
|
}; |
|
|
|
console.log(`word: ->${word}<-`); |
|
|
|
let w1; |
|
let w2; |
|
let w3; |
|
|
|
if (word.includes("\n")) { |
|
[w1, w3] = word.split("\n"); |
|
w2 = "\n"; |
|
console.log(`split: ${w1} | ${w2} | ${w3}`); |
|
} else { |
|
w1 = word; |
|
w2 = ""; |
|
w3 = ""; |
|
} |
|
|
|
|
|
const sortedReplacements = [...replacements].sort((a, b) => b.logprob - a.logprob) |
|
|
|
const withProbabilities = sortedReplacements.map(r => ({ ...r, probability: Math.exp(r.logprob)*100 })) |
|
|
|
return ( |
|
<span |
|
title={logprob.toFixed(2)} |
|
className={`word-chip ${logprob < threshold ? "flagged" : ""}`} |
|
style={{ position: "relative", cursor: logprob < threshold ? "pointer" : "default" }} |
|
onClick={handleClick} |
|
> |
|
{w1} |
|
{w2 && <br />} |
|
{w3} |
|
{isExpanded && ( |
|
<div |
|
ref={dropdownRef} |
|
style={{ |
|
position: "absolute", |
|
top: "100%", |
|
left: 0, |
|
zIndex: 100, |
|
maxHeight: "400px", |
|
overflowY: "auto", |
|
backgroundColor: "white", |
|
border: "1px solid #ccc", |
|
borderRadius: "4px", |
|
boxShadow: "0 2px 4px rgba(0,0,0,0.1)" |
|
}} |
|
> |
|
{withProbabilities.map((option, index) => ( |
|
<div |
|
key={index} |
|
onClick={() => handleReplacement(option.text)} |
|
onMouseEnter={() => setSelectedIndex(index)} |
|
style={{ |
|
padding: "5px 10px", |
|
cursor: "pointer", |
|
color: "black", |
|
backgroundColor: selectedIndex === index ? "#f0f0f0" : "white", |
|
whiteSpace: "nowrap" |
|
}} |
|
> |
|
{option.text} <small style={{ fontSize: "0.7em", color: "#666" }}>{option.probability.toFixed(1)}%</small> |
|
</div> |
|
))} |
|
</div> |
|
)} |
|
</span> |
|
) |
|
} |
|
|