1
- import * as React from 'react'
1
+ import React from 'react'
2
2
3
3
import Mark , { MarkProps } from './Mark'
4
4
import { selectionIsEmpty , selectionIsBackwards , splitTokensWithOffsets } from './utils'
5
+ import { Span } from './span'
5
6
6
7
interface TokenProps {
7
8
i : number
@@ -18,40 +19,26 @@ const Token: React.SFC<TokenProps> = props => {
18
19
return < span data-i = { props . i } > { props . content } </ span >
19
20
}
20
21
21
- export interface TokenAnnotatorProps
22
+ export interface TokenAnnotatorProps < T >
22
23
extends Omit < React . HTMLAttributes < HTMLDivElement > , 'onChange' > {
23
24
tokens : string [ ]
24
- value : TokenSpan [ ]
25
- onChange : ( value : TokenSpan [ ] ) => any
26
- getSpan ?: ( span : TokenSpan ) => TokenSpan
25
+ value : T [ ]
26
+ onChange : ( value : T [ ] ) => any
27
+ getSpan ?: ( span : TokenSpan ) => T
27
28
renderMark ?: ( props : MarkProps ) => JSX . Element
28
29
// TODO: determine whether to overwrite or leave intersecting ranges.
29
30
}
30
31
31
- // TODO: When React 16.3 types are ready, remove casts.
32
- class TokenAnnotator extends React . Component < TokenAnnotatorProps , { } > {
33
- static defaultProps = {
34
- renderMark : props => < Mark { ...props } /> ,
35
- }
36
-
37
- rootRef : React . RefObject < HTMLDivElement >
38
-
39
- constructor ( props ) {
40
- super ( props )
41
-
42
- this . rootRef = React . createRef ( )
43
- }
32
+ const TokenAnnotator = < T extends Span > ( props : TokenAnnotatorProps < T > ) => {
33
+ const renderMark = props . renderMark || ( props => < Mark { ...props } /> )
44
34
45
- componentDidMount ( ) {
46
- this . rootRef . current . addEventListener ( 'mouseup' , this . handleMouseUp )
35
+ const getSpan = ( span : TokenSpan ) : T => {
36
+ if ( props . getSpan ) return props . getSpan ( span )
37
+ return { start : span . start , end : span . end } as T
47
38
}
48
39
49
- componentWillUnmount ( ) {
50
- this . rootRef . current . removeEventListener ( 'mouseup' , this . handleMouseUp )
51
- }
52
-
53
- handleMouseUp = ( ) => {
54
- if ( ! this . props . onChange ) return
40
+ const handleMouseUp = ( ) => {
41
+ if ( ! props . onChange ) return
55
42
56
43
const selection = window . getSelection ( )
57
44
@@ -74,48 +61,35 @@ class TokenAnnotator extends React.Component<TokenAnnotatorProps, {}> {
74
61
75
62
end += 1
76
63
77
- this . props . onChange ( [
78
- ...this . props . value ,
79
- this . getSpan ( { start, end, tokens : this . props . tokens . slice ( start , end ) } ) ,
80
- ] )
64
+ props . onChange ( [ ...props . value , getSpan ( { start, end, tokens : props . tokens . slice ( start , end ) } ) ] )
81
65
window . getSelection ( ) . empty ( )
82
66
}
83
67
84
- handleSplitClick = ( { start, end} ) => {
68
+ const handleSplitClick = ( { start, end} ) => {
85
69
// Find and remove the matching split.
86
- const splitIndex = this . props . value . findIndex ( s => s . start === start && s . end === end )
70
+ const splitIndex = props . value . findIndex ( s => s . start === start && s . end === end )
87
71
if ( splitIndex >= 0 ) {
88
- this . props . onChange ( [
89
- ...this . props . value . slice ( 0 , splitIndex ) ,
90
- ...this . props . value . slice ( splitIndex + 1 ) ,
91
- ] )
72
+ props . onChange ( [ ...props . value . slice ( 0 , splitIndex ) , ...props . value . slice ( splitIndex + 1 ) ] )
92
73
}
93
74
}
94
75
95
- getSpan = ( span : TokenSpan ) => {
96
- if ( this . props . getSpan ) return this . props . getSpan ( span )
97
- return span
98
- }
99
-
100
- render ( ) {
101
- const { tokens, value, renderMark, onChange, getSpan, ...divProps } = this . props
102
- const splits = splitTokensWithOffsets ( tokens , value )
103
- return (
104
- < div ref = { this . rootRef } { ...divProps } >
105
- { splits . map ( ( split , i ) =>
106
- split . mark ? (
107
- renderMark ( {
108
- key : `${ split . start } -${ split . end } ` ,
109
- ...split ,
110
- onClick : this . handleSplitClick ,
111
- } )
112
- ) : (
113
- < Token key = { split . i } { ...split } />
114
- )
115
- ) }
116
- </ div >
117
- )
118
- }
76
+ const { tokens, value, onChange, getSpan : _ , ...divProps } = props
77
+ const splits = splitTokensWithOffsets ( tokens , value )
78
+ return (
79
+ < div { ...divProps } onMouseUp = { handleMouseUp } >
80
+ { splits . map ( ( split , i ) =>
81
+ split . mark ? (
82
+ renderMark ( {
83
+ key : `${ split . start } -${ split . end } ` ,
84
+ ...split ,
85
+ onClick : handleSplitClick ,
86
+ } )
87
+ ) : (
88
+ < Token key = { split . i } { ...split } />
89
+ )
90
+ ) }
91
+ </ div >
92
+ )
119
93
}
120
94
121
95
export default TokenAnnotator
0 commit comments