| 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> |
| ) |
| } |
|
|