1
1
2
- import React , { Component } from 'react' ;
2
+ import React , { Component , CSSProperties } from 'react' ;
3
3
import ReactCursorPosition from 'react-cursor-position'
4
- import PropTypes from 'prop- types'
4
+ import { Coord2 } from '../redux/ types' ;
5
5
6
- export class CharPosition extends Component {
6
+ type Omit < T , K > = Pick < T , Exclude < keyof T , K > > ;
7
+ type Subtract < T , K > = Omit < T , keyof K > ;
7
8
8
- constructor ( props ) {
9
+ type DragStartFunc = ( charPos : Coord2 ) => void ;
10
+ type DragMoveFunc = ( charPos : Coord2 ) => void ;
11
+ type DragEndFunc = ( ) => void ;
12
+ type AltClickFunc = ( charPos : Coord2 ) => void ;
13
+
14
+ type Position = {
15
+ position : { x : number , y : number } ;
16
+ elementDimensions : { width : number , height : number } ;
17
+ }
18
+
19
+ type IsActive = { isActive : boolean } ;
20
+ interface CharPositionProps {
21
+ onActivationChanged ( args : IsActive ) : void ;
22
+ onCharPosChanged : ( pos : Coord2 | null ) => void ;
23
+ }
24
+
25
+ export class CharPosition extends Component < CharPositionProps > {
26
+
27
+ prevCharPos : Coord2 | null = null ;
28
+
29
+ constructor ( props : CharPositionProps ) {
9
30
super ( props )
10
31
this . prevCharPos = null
11
32
}
12
33
13
- toCharPos = ( { position, elementDimensions} ) => {
34
+ toCharPos = ( { position, elementDimensions} : Position ) : Coord2 | null => {
14
35
if ( elementDimensions === null ) {
15
36
return null
16
37
}
@@ -24,17 +45,17 @@ export class CharPosition extends Component {
24
45
// The parent component needs to know if the cursor is active (inside the
25
46
// child div) to conditionally render sibling components like cursor pos,
26
47
// char under cursor, etc.
27
- handleActivationChanged = ( { isActive} ) => {
48
+ handleActivationChanged = ( { isActive} : { isActive : boolean } ) => {
28
49
this . props . onActivationChanged ( { isActive} )
29
50
}
30
51
31
52
// The parent component needs to know what the current charpos is inside the
32
53
// child div).
33
- handlePositionChanged = ( vals ) => {
54
+ handlePositionChanged = ( vals : Position ) => {
34
55
if ( vals . elementDimensions === undefined ) {
35
56
return
36
57
}
37
- const { row, col } = this . toCharPos ( vals )
58
+ const { row, col } = this . toCharPos ( vals ) !
38
59
if ( this . prevCharPos === null ||
39
60
row !== this . prevCharPos . row ||
40
61
col !== this . prevCharPos . col ) {
@@ -56,21 +77,54 @@ export class CharPosition extends Component {
56
77
}
57
78
}
58
79
59
- export const withMouseCharPositionShiftLockAxis = C => {
60
- class ToCharRowCol extends Component {
61
- static propTypes = {
62
- altKey : PropTypes . bool . isRequired ,
63
- shiftKey : PropTypes . bool . isRequired
64
- }
65
- constructor ( props ) {
66
- super ( props )
80
+ // The component wrapped by withMouseCharPositionShiftLockAxis must
81
+ // have these props in its component type.
82
+ interface WithMouseCharPosWrappeeProps {
83
+ charPos : Coord2 | null ;
84
+ onMouseDown : ( e : any , dragStart : DragStartFunc , altClick : AltClickFunc ) => void ;
85
+ onMouseMove : ( e : any , dragMove : DragMoveFunc ) => void ;
86
+ onMouseUp : ( e : any , dragEnd : DragEndFunc ) => void ;
87
+ }
67
88
68
- this . prevCharPos = null
69
- this . dragging = false
70
- this . prevCoord = null
71
- this . lockStartCoord = null
72
- this . lockedCharPos = null
73
- this . shiftLockAxis = null
89
+ interface WithMouseCharPositionShiftLockAxisProps {
90
+ altKey : boolean ;
91
+ shiftKey : boolean ;
92
+ isActive : boolean ;
93
+ onCharPosChange : ( args :{
94
+ isActive : boolean ;
95
+ charPos : Coord2
96
+ } ) => void ;
97
+ }
98
+
99
+ interface CursorPositionProps {
100
+ containerSize : CSSProperties ;
101
+ onActivationChanged ( args : IsActive ) : void ;
102
+ }
103
+
104
+ interface ToCharRowColProps extends Position {
105
+ framebufWidth : number ;
106
+ framebufHeight : number ;
107
+ }
108
+
109
+ export const withMouseCharPositionShiftLockAxis = < P extends object > ( C : React . ComponentType < WithMouseCharPosWrappeeProps > ) => {
110
+ class ToCharRowCol extends Component < P & CursorPositionProps & WithMouseCharPositionShiftLockAxisProps & ToCharRowColProps > {
111
+
112
+ prevCharPos : Coord2 | null = null ;
113
+ prevCoord : Coord2 | null = null ;
114
+ lockStartCoord : Coord2 | null = null ;
115
+ lockedCharPos : Coord2 | null = null ;
116
+ shiftLockAxis : 'shift' | 'row' | 'col' | null = null ;
117
+ dragging = false ;
118
+
119
+ constructor ( props : P & CursorPositionProps & WithMouseCharPositionShiftLockAxisProps & ToCharRowColProps ) {
120
+ super ( props ) ;
121
+
122
+ this . prevCharPos = null ;
123
+ this . dragging = false ;
124
+ this . prevCoord = null ;
125
+ this . lockStartCoord = null ;
126
+ this . lockedCharPos = null ;
127
+ this . shiftLockAxis = null ;
74
128
}
75
129
76
130
currentCharPos = ( ) => {
@@ -87,13 +141,13 @@ export const withMouseCharPositionShiftLockAxis = C => {
87
141
}
88
142
}
89
143
90
- handleMouseDown = ( e , dragStart , altClick ) => {
144
+ handleMouseDown = ( e : any , dragStart : DragStartFunc , altClick : AltClickFunc ) => {
91
145
const charPos = this . currentCharPos ( )
92
146
// alt-left click doesn't start dragging
93
147
if ( this . props . altKey ) {
94
- this . dragging = false
95
- altClick ( charPos )
96
- return
148
+ this . dragging = false ;
149
+ altClick ( charPos ) ;
150
+ return ;
97
151
}
98
152
99
153
this . dragging = true
@@ -110,7 +164,7 @@ export const withMouseCharPositionShiftLockAxis = C => {
110
164
}
111
165
}
112
166
113
- handleMouseUp = ( e , dragEnd ) => {
167
+ handleMouseUp = ( _e : React . PointerEvent , dragEnd : DragEndFunc ) => {
114
168
if ( this . dragging ) {
115
169
dragEnd ( )
116
170
}
@@ -120,7 +174,7 @@ export const withMouseCharPositionShiftLockAxis = C => {
120
174
this . shiftLockAxis = null
121
175
}
122
176
123
- handleMouseMove = ( e , dragMove ) => {
177
+ handleMouseMove = ( _e : any , dragMove : DragMoveFunc ) => {
124
178
const charPos = this . currentCharPos ( )
125
179
126
180
if ( this . prevCharPos === null ||
@@ -134,21 +188,22 @@ export const withMouseCharPositionShiftLockAxis = C => {
134
188
return
135
189
}
136
190
137
- const coord = charPos
138
- if ( this . prevCoord . row !== coord . row ||
139
- this . prevCoord . col !== coord . col ) {
191
+ // Note: prevCoord is known to be not null here as it's been set
192
+ // in mouse down
193
+ const coord = charPos ;
194
+ if ( this . prevCoord ! . row !== coord . row || this . prevCoord ! . col !== coord . col ) {
140
195
141
196
if ( this . shiftLockAxis === 'shift' ) {
142
- if ( this . prevCoord . row === coord . row ) {
197
+ if ( this . prevCoord ! . row === coord . row ) {
143
198
this . shiftLockAxis = 'row'
144
- } else if ( this . prevCoord . col === coord . col ) {
199
+ } else if ( this . prevCoord ! . col === coord . col ) {
145
200
this . shiftLockAxis = 'col'
146
201
}
147
202
}
148
203
149
204
if ( this . shiftLockAxis !== null ) {
150
205
let lockedCharPos = {
151
- ...this . lockStartCoord
206
+ ...this . lockStartCoord !
152
207
}
153
208
154
209
if ( this . shiftLockAxis === 'row' ) {
@@ -184,30 +239,44 @@ export const withMouseCharPositionShiftLockAxis = C => {
184
239
)
185
240
}
186
241
}
187
- return class extends Component {
242
+ return class extends Component < P & WithMouseCharPositionShiftLockAxisProps & CursorPositionProps & ToCharRowColProps > {
188
243
render ( ) {
189
244
return (
190
245
< ReactCursorPosition
191
246
style = { {
192
247
...this . props . containerSize
193
248
} }
194
249
onActivationChanged = { this . props . onActivationChanged } >
195
- < ToCharRowCol { ...this . props } />
250
+ < ToCharRowCol { ...this . props } />
196
251
</ ReactCursorPosition >
197
252
)
198
253
}
199
254
}
200
255
}
201
256
202
- export const withHoverFade = ( C , options ) => {
203
- return class extends Component {
204
- constructor ( props ) {
205
- super ( props )
206
- this . timerId = null
207
- this . state = {
208
- fadeOut : false
209
- }
210
- }
257
+ interface WithHoverInjectedProps {
258
+ onToggleActive : ( ) => void ;
259
+ fadeOut : boolean ;
260
+ }
261
+
262
+ interface WithHoverFadeProps {
263
+ pickerId : any ;
264
+ active : boolean ;
265
+ containerClassName : string ;
266
+ onSetActive : ( pickerId : any , active : boolean ) => void ;
267
+ }
268
+
269
+ interface WithHoverFadeState {
270
+ fadeOut : boolean ;
271
+ }
272
+
273
+ // See https://medium.com/@jrwebdev /react-higher-order-component-patterns-in-typescript-42278f7590fb
274
+ export const withHoverFade = < P extends WithHoverInjectedProps > ( C : React . ComponentType < P > ) => {
275
+ return class extends Component < Subtract < P , WithHoverInjectedProps > & WithHoverFadeProps , WithHoverFadeState > {
276
+ state : WithHoverFadeState = {
277
+ fadeOut : false
278
+ } ;
279
+ timerId : any = null ;
211
280
212
281
componentWillUnmount ( ) {
213
282
if ( this . timerId !== null ) {
@@ -244,16 +313,28 @@ export const withHoverFade = (C, options) => {
244
313
}
245
314
246
315
render ( ) {
316
+ // const { pickerId, active, containerClassName, onSetActive, ...rest} = this.props as WithHoverFadeProps;
317
+ // See: https://github.com/Microsoft/TypeScript/issues/28938
318
+ const ts32workAround : any = {
319
+ ...this . props ,
320
+ onToggleActive : this . handleToggleActive ,
321
+ fadeOut : this . state . fadeOut
322
+ }
323
+ /*
324
+ <C
325
+ {...rest}
326
+ onToggleActive={this.handleToggleActive}
327
+ fadeOut={this.state.fadeOut}
328
+ />
329
+ */
247
330
return (
248
331
< div
249
332
className = { this . props . containerClassName }
250
333
onMouseLeave = { this . handleMouseLeave }
251
334
onMouseEnter = { this . handleMouseEnter }
252
335
>
253
336
< C
254
- onToggleActive = { this . handleToggleActive }
255
- fadeOut = { this . state . fadeOut }
256
- { ...this . props }
337
+ { ...ts32workAround }
257
338
/>
258
339
</ div >
259
340
)
0 commit comments