Skip to content

Commit 766214c

Browse files
authored
fix(ui): popover - scrollable container
1 parent 2cbf39c commit 766214c

File tree

3 files changed

+419
-253
lines changed

3 files changed

+419
-253
lines changed
Lines changed: 68 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import React from 'react';
22
import {
3+
Platform,
4+
StatusBar,
5+
StyleProp,
6+
StyleSheet,
37
View,
4-
FlexStyle,
58
ViewProps,
6-
StyleSheet,
9+
ViewStyle,
710
} from 'react-native';
811
import {
912
ModalComponentCloseProps,
@@ -16,19 +19,19 @@ import {
1619
Props as PopoverViewProps,
1720
} from './popoverView.component';
1821
import {
22+
MeasuredElement,
1923
MeasureNode,
2024
MeasureResult,
2125
MeasuringElement,
22-
MeasuringNode,
23-
MeasuredElement,
2426
MeasuringElementProps,
27+
MeasuringNode,
2528
} from './measure.component';
2629
import {
2730
Frame,
31+
OffsetRect,
32+
Offsets,
2833
Placement,
2934
Placements,
30-
MarginOffsets,
31-
getMarginOffsets,
3235
} from './type';
3336

3437
interface PopoverProps {
@@ -37,71 +40,39 @@ interface PopoverProps {
3740
visible?: boolean;
3841
}
3942

40-
interface State {
41-
layout: MeasureResult | undefined;
42-
}
43-
4443
export type Props = PopoverProps & ModalComponentCloseProps & StyledComponentProps & PopoverViewProps & ViewProps;
4544

4645
const TAG_CHILD: number = 0;
4746
const TAG_CONTENT: number = 1;
4847
const 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

190189
const styles = StyleSheet.create({
191190
popover: {
192191
position: 'absolute',
193-
},
194-
placeholder: {
195192
opacity: 0,
196193
},
197194
});

0 commit comments

Comments
 (0)