question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Proposal] Add an option to Modal in order to avoid keyboard

See original GitHub issue

This is related to #1495

The problem

When a Modal/Dialog contains a TextInput and you focus the TextInput, the keyboard comes over the Modal. This often hides the Action buttons, other TextInput bellows, or even the focused text input in some cases (IOS). This is not very user Friendly. An option is to wrap the Modal around a KeyboardAvoidingView. However, this comes with its own set of issues:

  • When the modal is hidden, the KeyboardAvoidingView remains, making the below views not responsive to touches.
  • Adding pointerEvents="box-none" fixes the previous issue, but it is still not ideal, because when the modal is hidden, that View remains there top of everything (even if it’s empty). It is useless but is still rendered.
  • I have tried doing something like {visible && <KeyboardAvoidingView><Modal ..../></KeyboardAvoidingView>} but that messes up the fade in/out animations of the Dialog.

Possible solutions

Ideally, what we want is KeyboardAvoidingView to be inside the Modal component, around here That way, it would get rendered or not depending on the Modal state.

I can think of 2 ways to do this:

  1. we add an avoidKeyboard prop (default to false) to the Modal/Dialog components. When true, we would wrap the Modal inside a KeyboardAvoidingView. Otherwise, we keep doing as now.

  2. For more flexibility, we add a WrapperComponent prop that receives any kind of Component or a function that returns one. A bit similar to CellRendererComponent in a VirtualizedList. When given, the Modal is wrapped with it. This would let users choose what Component to use. For example, one could pass KeyboardAvoidingView with specific props (like behavior, keyboardVerticalOffset).

Let me know if you have any comment.

Thanks

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:11
  • Comments:13 (2 by maintainers)

github_iconTop GitHub Comments

14reactions
rye761commented, Nov 12, 2020

Here is my solution in the mean time:

import * as React from 'react';
import { Dialog } from 'react-native-paper';
import { Keyboard } from 'react-native';

export default class KeyboardAvoidingDialog extends React.Component {
  constructor(props) {
    super(props);
    this.subscriptions = [];
    this.state = {
      bottom: 0
    }
  }

  componentDidMount() {
    if (Platform.OS === 'ios') {
      this.subscriptions = [
        Keyboard.addListener('keyboardWillChangeFrame', this.onKeyboardChange.bind(this)),
      ];
    } else {
      this.subscriptions = [
        Keyboard.addListener('keyboardDidHide', this.onKeyboardChange.bind(this)),
        Keyboard.addListener('keyboardDidShow', this.onKeyboardChange.bind(this)),
      ];
    };
  }

  componentWillUnmount() {
    this.subscriptions.forEach(subscription => {
      subscription.remove();
    });
  }

  onKeyboardChange(e) {
    if (e.endCoordinates.screenY < e.startCoordinates.screenY)
      this.setState({ bottom: e.endCoordinates.height / 2});
    else
      this.setState({ bottom: 0 });
  }

  render() {
    return (
      <Dialog style={{ bottom: this.state.bottom }} visible={this.props.visible} onDismiss={this.props.onDismiss}>
        {this.props.children}
      </Dialog>
    )
  }
}

Not perfect, it takes advantage of the fact that a dialog is centered on the screen and bumps it up by half the keyboard height. I’m pretty satisfied with this and it’s a pretty simple implementation.

7reactions
jorgemastacommented, Mar 11, 2022

Thanks for your workaround @rye761. Here is a version using hooks:

  const [bottom, setBottom] = React.useState(0)

  React.useEffect(() => {
    function onKeyboardChange(e) {
      if (e.endCoordinates.screenY < e.startCoordinates.screenY)
        setBottom(e.endCoordinates.height / 2)
      else setBottom(0)
    }

    if (Platform.OS === "ios") {
      const subscription = Keyboard.addListener("keyboardWillChangeFrame", onKeyboardChange)
      return () => subscription.remove()
    }

    const subscriptions = [
      Keyboard.addListener("keyboardDidHide", onKeyboardChange),
      Keyboard.addListener("keyboardDidShow", onKeyboardChange),
    ]
    return () => subscriptions.forEach((subscription) => subscription.remove())
  }, [])
  
  
    return (
      <Dialog style={{ bottom }} visible={visible} onDismiss={onDismiss}>
        {children}
      </Dialog>
    )
Read more comments on GitHub >

github_iconTop Results From Across the Web

Change Bootstrap modal option once it already exists
First, I create a Modal doing this (as you can see, I declare the modal putting keyboard property to false): $('#myModal').modal({ show: false, ......
Read more >
How to create an accessible React modal | by Seif Ghezala
W3 recommends adding at least one keyboard shortcut to close the modal. The shortcut is the ESCAPE key. To do so, we use...
Read more >
Modal dialogs - Access & Use
A keyboard user focuses the button "ADD CONTENT" which shows no visible focus, and activates it (see note) · The dialog opens ·...
Read more >
Considerations for Styling a Modal | CSS-Tricks
Keyboard focus should be trapped inside of the modal, so the user does not accidentally tab outside of the modal (a.k.a. “escaping the...
Read more >
Modal - Bootstrap
Use Bootstrap's JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content. How it works. Before ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found