1
1
import React from 'react' ;
2
2
import {
3
+ Platform ,
4
+ StatusBar ,
5
+ StyleProp ,
6
+ StyleSheet ,
3
7
View ,
4
- FlexStyle ,
5
8
ViewProps ,
6
- StyleSheet ,
9
+ ViewStyle ,
7
10
} from 'react-native' ;
8
11
import {
9
12
ModalComponentCloseProps ,
@@ -16,19 +19,19 @@ import {
16
19
Props as PopoverViewProps ,
17
20
} from './popoverView.component' ;
18
21
import {
22
+ MeasuredElement ,
19
23
MeasureNode ,
20
24
MeasureResult ,
21
25
MeasuringElement ,
22
- MeasuringNode ,
23
- MeasuredElement ,
24
26
MeasuringElementProps ,
27
+ MeasuringNode ,
25
28
} from './measure.component' ;
26
29
import {
27
30
Frame ,
31
+ OffsetRect ,
32
+ Offsets ,
28
33
Placement ,
29
34
Placements ,
30
- MarginOffsets ,
31
- getMarginOffsets ,
32
35
} from './type' ;
33
36
34
37
interface PopoverProps {
@@ -37,71 +40,39 @@ interface PopoverProps {
37
40
visible ?: boolean ;
38
41
}
39
42
40
- interface State {
41
- layout : MeasureResult | undefined ;
42
- }
43
-
44
43
export type Props = PopoverProps & ModalComponentCloseProps & StyledComponentProps & PopoverViewProps & ViewProps ;
45
44
46
45
const TAG_CHILD : number = 0 ;
47
46
const TAG_CONTENT : number = 1 ;
48
47
const PLACEMENT_DEFAULT : Placement = Placements . BOTTOM ;
49
48
50
- export class Popover extends React . Component < Props , State > {
49
+ export class Popover extends React . Component < Props > {
51
50
52
51
static styledComponentName : string = 'Popover' ;
53
52
54
53
static defaultProps : Partial < Props > = {
55
54
placement : PLACEMENT_DEFAULT . rawValue ,
56
55
visible : false ,
57
- scrollOffset : 0 ,
58
- } ;
59
-
60
- public state : State = {
61
- layout : undefined ,
62
56
} ;
63
57
64
58
private popoverElement : MeasuredElement = undefined ;
65
- private modalIdentifier : string = '' ;
66
-
67
- public shouldComponentUpdate ( nextProps : Props , nextState : State , nextContext : any ) : boolean {
68
- const isLayoutChanged : boolean = nextState . layout !== undefined ;
69
- const isVisibilityChanged : boolean = this . props . visible !== nextProps . visible ;
70
-
71
- return isLayoutChanged || isVisibilityChanged ;
72
- }
59
+ private popoverModalId : string = '' ;
73
60
74
- public componentDidUpdate ( prevProps : Props , prevState : State ) : void {
75
- const {
76
- visible,
77
- placement,
78
- onRequestClose,
79
- children,
80
- } = this . props ;
61
+ public componentDidUpdate ( prevProps : Props ) : void {
62
+ const { visible } = this . props ;
81
63
82
64
if ( prevProps . visible !== visible ) {
83
65
if ( visible ) {
84
- const marginOffsets : MarginOffsets = getMarginOffsets ( children . props . style ) ;
85
- const { origin : popoverPosition } = this . getPopoverFrame ( placement , marginOffsets ) ;
86
- const style : FlexStyle = {
87
- left : popoverPosition . x ,
88
- top : popoverPosition . y ,
89
- } ;
90
-
91
- const popover : React . ReactElement < ModalComponentCloseProps > = React . cloneElement ( this . popoverElement , {
92
- style : style ,
93
- onRequestClose : onRequestClose ,
94
- } ) ;
95
-
96
- this . modalIdentifier = ModalService . show ( popover , true ) ;
66
+ // Toggles re-measuring
67
+ this . setState ( { layout : undefined } ) ;
97
68
} else {
98
- ModalService . hide ( this . modalIdentifier ) ;
69
+ ModalService . hide ( this . popoverModalId ) ;
99
70
}
100
71
}
101
72
}
102
73
103
- public componentWillUnmount ( ) : void {
104
- this . modalIdentifier = '' ;
74
+ public componentWillUnmount ( ) {
75
+ this . popoverModalId = '' ;
105
76
}
106
77
107
78
private getComponentStyle = ( source : StyleType ) : StyleType => {
@@ -111,23 +82,54 @@ export class Popover extends React.Component<Props, State> {
111
82
} ;
112
83
} ;
113
84
114
- private getPopoverFrame = ( rawPlacement : string | Placement , offsets : MarginOffsets ) : Frame => {
115
- const { layout } = this . state ;
116
- const { [ TAG_CONTENT ] : popoverFrame , [ TAG_CHILD ] : childFrame } = layout ;
85
+ private onMeasure = ( layout : MeasureResult ) => {
86
+ const { visible } = this . props ;
117
87
118
- const placement : Placement = Placements . parse ( rawPlacement , PLACEMENT_DEFAULT ) ;
88
+ if ( visible ) {
89
+ this . popoverModalId = this . showPopoverModal ( this . popoverElement , layout ) ;
90
+ }
91
+ } ;
92
+
93
+ private showPopoverModal = ( element : MeasuredElement , layout : MeasureResult ) : string => {
94
+ const { placement, onRequestClose } = this . props ;
95
+
96
+ const popoverFrame : Frame = this . getPopoverFrame ( layout , placement ) ;
97
+
98
+ const { origin : popoverPosition } = popoverFrame ;
99
+
100
+ const additionalStyle : ViewStyle = {
101
+ left : popoverPosition . x ,
102
+ top : Platform . select ( {
103
+ android : popoverPosition . y + StatusBar . currentHeight ,
104
+ default : popoverPosition . y ,
105
+ } ) ,
106
+ opacity : 1 ,
107
+ } ;
108
+
109
+ const popover : React . ReactElement < ModalComponentCloseProps > = React . cloneElement ( element , {
110
+ style : additionalStyle ,
111
+ onRequestClose : onRequestClose ,
112
+ } ) ;
119
113
120
- return placement . frame ( popoverFrame , childFrame , offsets ) ;
114
+ return ModalService . show ( popover , true ) ;
121
115
} ;
122
116
123
- private onMeasure = ( layout : MeasureResult ) => {
124
- this . setState ( { layout } ) ;
117
+ private getPopoverFrame = ( layout : MeasureResult , rawPlacement : string | Placement ) : Frame => {
118
+ const { children } = this . props ;
119
+ const { [ TAG_CONTENT ] : popoverFrame , [ TAG_CHILD ] : childFrame } = layout ;
120
+
121
+ const offsetRect : OffsetRect = Offsets . find ( children . props . style ) ;
122
+ const placement : Placement = Placements . parse ( rawPlacement , PLACEMENT_DEFAULT ) ;
123
+
124
+ return placement . frame ( popoverFrame , childFrame , offsetRect ) ;
125
125
} ;
126
126
127
- private renderPopoverElement = ( children : React . ReactElement < any > , style : StyleType ) : MeasuringElement => {
127
+ private renderPopoverElement = ( children : React . ReactElement < any > , style : StyleProp < ViewStyle > ) : MeasuringElement => {
128
128
const { placement, ...derivedProps } = this . props ;
129
129
130
- const measuringProps : MeasuringElementProps = { tag : TAG_CONTENT , scrollOffset : this . props . scrollOffset } ;
130
+ const measuringProps : MeasuringElementProps = {
131
+ tag : TAG_CONTENT ,
132
+ } ;
131
133
132
134
const popoverPlacement : Placement = Placements . parse ( placement , PLACEMENT_DEFAULT ) ;
133
135
const indicatorPlacement : Placement = popoverPlacement . reverse ( ) ;
@@ -147,51 +149,46 @@ export class Popover extends React.Component<Props, State> {
147
149
) ;
148
150
} ;
149
151
150
- private renderChildElement = ( source : React . ReactElement < any > , style : StyleType ) : MeasuringElement => {
152
+ private renderChildElement = ( source : React . ReactElement < any > , style : StyleProp < ViewStyle > ) : MeasuringElement => {
151
153
const measuringProps : MeasuringElementProps = { tag : TAG_CHILD } ;
152
154
153
155
return (
154
156
< View
155
157
{ ...measuringProps }
156
- style = { style }
157
- key = { TAG_CHILD } >
158
+ key = { TAG_CHILD }
159
+ style = { style } >
158
160
{ source }
159
161
</ View >
160
162
) ;
161
163
} ;
162
164
163
- private renderPlaceholderElement = ( ...children : MeasuringElement [ ] ) : MeasuringNode => {
165
+ private renderMeasuringElement = ( ...children : MeasuringElement [ ] ) : MeasuringNode => {
164
166
return (
165
167
< MeasureNode
166
- style = { styles . placeholder }
167
168
onResult = { this . onMeasure } >
168
169
{ children }
169
170
</ MeasureNode >
170
171
) ;
171
172
} ;
172
173
173
174
public render ( ) : MeasuringNode | React . ReactNode {
174
- const { themedStyle, content, children } = this . props ;
175
+ const { themedStyle, content, visible , children } = this . props ;
175
176
const { child, popover } = this . getComponentStyle ( themedStyle ) ;
176
177
177
- const measuringChild : MeasuringElement = this . renderChildElement ( children , child ) ;
178
- const measuringPopover : MeasuringElement = this . renderPopoverElement ( content , popover ) ;
178
+ if ( visible ) {
179
+ this . popoverElement = this . renderPopoverElement ( content , popover ) ;
180
+ const childElement : MeasuringElement = this . renderChildElement ( children , child ) ;
179
181
180
- if ( this . state . layout === undefined ) {
181
- return this . renderPlaceholderElement ( measuringChild , measuringPopover ) ;
182
+ return this . renderMeasuringElement ( childElement , this . popoverElement ) ;
182
183
}
183
184
184
- this . popoverElement = measuringPopover ;
185
-
186
- return measuringChild ;
185
+ return children ;
187
186
}
188
187
}
189
188
190
189
const styles = StyleSheet . create ( {
191
190
popover : {
192
191
position : 'absolute' ,
193
- } ,
194
- placeholder : {
195
192
opacity : 0 ,
196
193
} ,
197
194
} ) ;
0 commit comments