11import React from 'react' ;
22import {
3+ Platform ,
4+ StatusBar ,
5+ StyleProp ,
6+ StyleSheet ,
37 View ,
4- FlexStyle ,
58 ViewProps ,
6- StyleSheet ,
9+ ViewStyle ,
710} from 'react-native' ;
811import {
912 ModalComponentCloseProps ,
@@ -16,19 +19,19 @@ import {
1619 Props as PopoverViewProps ,
1720} from './popoverView.component' ;
1821import {
22+ MeasuredElement ,
1923 MeasureNode ,
2024 MeasureResult ,
2125 MeasuringElement ,
22- MeasuringNode ,
23- MeasuredElement ,
2426 MeasuringElementProps ,
27+ MeasuringNode ,
2528} from './measure.component' ;
2629import {
2730 Frame ,
31+ OffsetRect ,
32+ Offsets ,
2833 Placement ,
2934 Placements ,
30- MarginOffsets ,
31- getMarginOffsets ,
3235} from './type' ;
3336
3437interface PopoverProps {
@@ -37,71 +40,39 @@ interface PopoverProps {
3740 visible ?: boolean ;
3841}
3942
40- interface State {
41- layout : MeasureResult | undefined ;
42- }
43-
4443export type Props = PopoverProps & ModalComponentCloseProps & StyledComponentProps & PopoverViewProps & ViewProps ;
4544
4645const TAG_CHILD : number = 0 ;
4746const TAG_CONTENT : number = 1 ;
4847const PLACEMENT_DEFAULT : Placement = Placements . BOTTOM ;
4948
50- export class Popover extends React . Component < Props , State > {
49+ export class Popover extends React . Component < Props > {
5150
5251 static styledComponentName : string = 'Popover' ;
5352
5453 static defaultProps : Partial < Props > = {
5554 placement : PLACEMENT_DEFAULT . rawValue ,
5655 visible : false ,
57- scrollOffset : 0 ,
58- } ;
59-
60- public state : State = {
61- layout : undefined ,
6256 } ;
6357
6458 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 = '' ;
7360
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 ;
8163
8264 if ( prevProps . visible !== visible ) {
8365 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 } ) ;
9768 } else {
98- ModalService . hide ( this . modalIdentifier ) ;
69+ ModalService . hide ( this . popoverModalId ) ;
9970 }
10071 }
10172 }
10273
103- public componentWillUnmount ( ) : void {
104- this . modalIdentifier = '' ;
74+ public componentWillUnmount ( ) {
75+ this . popoverModalId = '' ;
10576 }
10677
10778 private getComponentStyle = ( source : StyleType ) : StyleType => {
@@ -111,23 +82,54 @@ export class Popover extends React.Component<Props, State> {
11182 } ;
11283 } ;
11384
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 ;
11787
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+ } ) ;
119113
120- return placement . frame ( popoverFrame , childFrame , offsets ) ;
114+ return ModalService . show ( popover , true ) ;
121115 } ;
122116
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 ) ;
125125 } ;
126126
127- private renderPopoverElement = ( children : React . ReactElement < any > , style : StyleType ) : MeasuringElement => {
127+ private renderPopoverElement = ( children : React . ReactElement < any > , style : StyleProp < ViewStyle > ) : MeasuringElement => {
128128 const { placement, ...derivedProps } = this . props ;
129129
130- const measuringProps : MeasuringElementProps = { tag : TAG_CONTENT , scrollOffset : this . props . scrollOffset } ;
130+ const measuringProps : MeasuringElementProps = {
131+ tag : TAG_CONTENT ,
132+ } ;
131133
132134 const popoverPlacement : Placement = Placements . parse ( placement , PLACEMENT_DEFAULT ) ;
133135 const indicatorPlacement : Placement = popoverPlacement . reverse ( ) ;
@@ -147,51 +149,46 @@ export class Popover extends React.Component<Props, State> {
147149 ) ;
148150 } ;
149151
150- private renderChildElement = ( source : React . ReactElement < any > , style : StyleType ) : MeasuringElement => {
152+ private renderChildElement = ( source : React . ReactElement < any > , style : StyleProp < ViewStyle > ) : MeasuringElement => {
151153 const measuringProps : MeasuringElementProps = { tag : TAG_CHILD } ;
152154
153155 return (
154156 < View
155157 { ...measuringProps }
156- style = { style }
157- key = { TAG_CHILD } >
158+ key = { TAG_CHILD }
159+ style = { style } >
158160 { source }
159161 </ View >
160162 ) ;
161163 } ;
162164
163- private renderPlaceholderElement = ( ...children : MeasuringElement [ ] ) : MeasuringNode => {
165+ private renderMeasuringElement = ( ...children : MeasuringElement [ ] ) : MeasuringNode => {
164166 return (
165167 < MeasureNode
166- style = { styles . placeholder }
167168 onResult = { this . onMeasure } >
168169 { children }
169170 </ MeasureNode >
170171 ) ;
171172 } ;
172173
173174 public render ( ) : MeasuringNode | React . ReactNode {
174- const { themedStyle, content, children } = this . props ;
175+ const { themedStyle, content, visible , children } = this . props ;
175176 const { child, popover } = this . getComponentStyle ( themedStyle ) ;
176177
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 ) ;
179181
180- if ( this . state . layout === undefined ) {
181- return this . renderPlaceholderElement ( measuringChild , measuringPopover ) ;
182+ return this . renderMeasuringElement ( childElement , this . popoverElement ) ;
182183 }
183184
184- this . popoverElement = measuringPopover ;
185-
186- return measuringChild ;
185+ return children ;
187186 }
188187}
189188
190189const styles = StyleSheet . create ( {
191190 popover : {
192191 position : 'absolute' ,
193- } ,
194- placeholder : {
195192 opacity : 0 ,
196193 } ,
197194} ) ;
0 commit comments