File size: 3,449 Bytes
8443315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import {
    StreamlitComponentBase,
    withStreamlitConnection,
} from "streamlit-component-lib";

type HighlightedTextState = {
    activeIndex: number | null,
    hoverIndex: number | null,
    isFrozen: boolean
};

/**
 * This is a React-based component template. The `render()` function is called
 * automatically when your component should be re-rendered.
 */
class HighlightedText extends StreamlitComponentBase<HighlightedTextState> {
    public state = {activeIndex: null, hoverIndex: null, isFrozen: false};

    render() {
        const tokens: string[] = this.props.args["tokens"];
        const scores: number[] = this.getScores();
        console.log(scores);

        let className = "highlighted-text";
        if (this.state.isFrozen) {
            className += " frozen";
        }

        const onClick = () => {
            this.setState({ isFrozen: false });
        };

        return <>
            <div className="status-bar" key="status-bar">
                <span className={this.state.isFrozen ? "" : " d-none"} key="lock-icon"><i className="fa fa-lock"></i> </span>
                <strong key="target-label">target:</strong>
                {
                    this.state.activeIndex != null
                        ? <span className="token" key="target">{tokens[this.state.activeIndex]}</span>
                        : <></>
                }
            </div>
            <div className={className} onClick={onClick} key="text">
                {
                    tokens.map((t: string, i: number) => {
                        let className = "token";
                        if (this.state) {
                            if (this.state.activeIndex == i) {
                                className += " active";
                            }
                        }
                        const style = {
                            backgroundColor:
                                scores[i] > 0
                                    ? `rgba(255, 32, 32, ${scores[i]})`
                                    : `rgba(32, 255, 32, ${-scores[i]})`
                        };

                        const onMouseOver = () => {
                            if (!this.state.isFrozen) {
                                this.setState({ activeIndex: i });
                            }
                            this.setState({ hoverIndex: i });
                        };
                        return <span key={i} className={className} style={style}
                            onMouseOver={onMouseOver} onClick={onClick}>{t}</span>;
                    })
                }
            </div>
        </>;
    }

    private getScores() {
        const tokens = this.props.args["tokens"];
        if (!this.state || this.state.activeIndex == null || this.state.activeIndex < 1) {
            return tokens.map(() => 0);
        }
        const allScores: number[][] = this.props.args["scores"];

        const i = this.state.activeIndex - 1;
        const hi = Math.min(Math.max(0, i), allScores[i].length);
        const row = allScores[i].slice(0, hi);
        row.reverse();
        let result = [
            ...Array(Math.max(0, i - 1 - row.length)).fill(0), 
            ...row.map((x) => x == undefined || isNaN(x) ? 0 : x)
        ];
        result = [...result, ...Array(tokens.length - result.length).fill(0)];
        return result;
    }
}

export default withStreamlitConnection(HighlightedText);