Skip to content

Commit

Permalink
fix: menu position fix when dimensions change (#3546)
Browse files Browse the repository at this point in the history
  • Loading branch information
mathias-berg authored Dec 28, 2022
1 parent 01c6ea8 commit d3246a8
Showing 1 changed file with 24 additions and 52 deletions.
76 changes: 24 additions & 52 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
LayoutRectangle,
NativeEventSubscription,
Platform,
ScaledSize,
ScrollView,
ScrollViewProps,
StyleProp,
Expand Down Expand Up @@ -191,10 +190,6 @@ class Menu extends React.Component<Props, State> {
'keyboardDidHide',
this.keyboardDidHide
);
this.dimensionsWindowSubscription = Dimensions.addEventListener(
'change',
this.onWindowChangeHandler
);
}

componentDidUpdate(prevProps: Props) {
Expand All @@ -207,16 +202,15 @@ class Menu extends React.Component<Props, State> {
this.removeListeners();
this.keyboardDidShowListener?.remove();
this.keyboardDidHideListener?.remove();
this.dimensionsWindowSubscription?.remove();
}

private anchor?: View | null = null;
private menu?: View | null = null;
private backHandlerSubscription: NativeEventSubscription | undefined;
private dimensionsSubscription: NativeEventSubscription | undefined;
private dimensionsWindowSubscription: NativeEventSubscription | undefined;
private keyboardDidShowListener: EmitterSubscription | undefined;
private keyboardDidHideListener: EmitterSubscription | undefined;
private keyboardHeight: number = 0;

private isCoordinate = (anchor: any): anchor is { x: number; y: number } =>
!React.isValidElement(anchor) &&
Expand Down Expand Up @@ -347,6 +341,10 @@ class Menu extends React.Component<Props, State> {
width: menuLayout.width,
height: menuLayout.height,
},
windowLayout: {
height: windowLayout.height - this.keyboardHeight,
width: windowLayout.width,
},
}),
() => {
this.attachListeners();
Expand Down Expand Up @@ -394,31 +392,11 @@ class Menu extends React.Component<Props, State> {

private keyboardDidShow = (e: RNKeyboardEvent) => {
const keyboardHeight = e.endCoordinates.height;
const { height, width } = Dimensions.get('window');

this.setState({
windowLayout: {
height: height - keyboardHeight,
width,
},
});
this.keyboardHeight = keyboardHeight;
};

private keyboardDidHide = () => {
const { height } = Dimensions.get('window');

this.setState((state) => ({
windowLayout: {
width: state.windowLayout.width,
height,
},
}));
};

private onWindowChangeHandler = ({ window }: { window: ScaledSize }) => {
this.setState({
windowLayout: { height: window.height, width: window.width },
});
this.keyboardHeight = 0;
};

render() {
Expand All @@ -442,6 +420,7 @@ class Menu extends React.Component<Props, State> {
anchorLayout,
opacityAnimation,
scaleAnimation,
windowLayout,
} = this.state;

let { left, top } = this.state;
Expand Down Expand Up @@ -475,10 +454,7 @@ class Menu extends React.Component<Props, State> {
const positionTransforms = [];

// Check if menu fits horizontally and if not align it to right.
if (
left <=
this.state.windowLayout.width - menuLayout.width - SCREEN_INDENT
) {
if (left <= windowLayout.width - menuLayout.width - SCREEN_INDENT) {
positionTransforms.push({
translateX: scaleAnimation.x.interpolate({
inputRange: [0, menuLayout.width],
Expand All @@ -502,8 +478,8 @@ class Menu extends React.Component<Props, State> {

const right = left + menuLayout.width;
// Check if menu position has enough space from right side
if (right > this.state.windowLayout.width - SCREEN_INDENT) {
left = this.state.windowLayout.width - SCREEN_INDENT - menuLayout.width;
if (right > windowLayout.width - SCREEN_INDENT) {
left = windowLayout.width - SCREEN_INDENT - menuLayout.width;
}
}

Expand All @@ -515,28 +491,25 @@ class Menu extends React.Component<Props, State> {
if (
// Check if the menu overflows from bottom side
top >=
this.state.windowLayout.height -
windowLayout.height -
menuLayout.height -
SCREEN_INDENT -
additionalVerticalValue &&
// And bottom side of the screen has more space than top side
top <= this.state.windowLayout.height - top
top <= windowLayout.height - top
) {
// Scrollable menu should be below the anchor (expands downwards)
scrollableMenuHeight =
this.state.windowLayout.height -
top -
SCREEN_INDENT -
additionalVerticalValue;
windowLayout.height - top - SCREEN_INDENT - additionalVerticalValue;
} else if (
// Check if the menu overflows from bottom side
top >=
this.state.windowLayout.height -
windowLayout.height -
menuLayout.height -
SCREEN_INDENT -
additionalVerticalValue &&
// And top side of the screen has more space than bottom side
top >= this.state.windowLayout.height - top &&
top >= windowLayout.height - top &&
// And menu overflows from top side
top <=
menuLayout.height -
Expand All @@ -551,27 +524,27 @@ class Menu extends React.Component<Props, State> {

// Scrollable menu max height
scrollableMenuHeight =
scrollableMenuHeight > this.state.windowLayout.height - 2 * SCREEN_INDENT
? this.state.windowLayout.height - 2 * SCREEN_INDENT
scrollableMenuHeight > windowLayout.height - 2 * SCREEN_INDENT
? windowLayout.height - 2 * SCREEN_INDENT
: scrollableMenuHeight;

// Menu is typically positioned below the element that generates it
// So first check if it fits below the anchor (expands downwards)
if (
// Check if menu fits vertically
top <=
this.state.windowLayout.height -
windowLayout.height -
menuLayout.height -
SCREEN_INDENT -
additionalVerticalValue ||
// Or if the menu overflows from bottom side
(top >=
this.state.windowLayout.height -
windowLayout.height -
menuLayout.height -
SCREEN_INDENT -
additionalVerticalValue &&
// And bottom side of the screen has more space than top side
top <= this.state.windowLayout.height - top)
top <= windowLayout.height - top)
) {
positionTransforms.push({
translateY: scaleAnimation.y.interpolate({
Expand Down Expand Up @@ -600,12 +573,11 @@ class Menu extends React.Component<Props, State> {
additionalVerticalValue;

// Check if menu position has enough space from bottom side
if (bottom > this.state.windowLayout.height - SCREEN_INDENT) {
if (bottom > windowLayout.height - SCREEN_INDENT) {
top =
scrollableMenuHeight ===
this.state.windowLayout.height - 2 * SCREEN_INDENT
scrollableMenuHeight === windowLayout.height - 2 * SCREEN_INDENT
? -SCREEN_INDENT * 2
: this.state.windowLayout.height -
: windowLayout.height -
menuLayout.height -
SCREEN_INDENT -
additionalVerticalValue;
Expand Down

0 comments on commit d3246a8

Please sign in to comment.