Просмотр исходного кода

Add `KeyboardManager` to solve z-index issues

Handle all key presses using the `KeyboardManager` class so issues
where multiple key presses listeners can be registered can be
gracefully handled.
For now, this happens when opening a modal (which listens to `Esc` key)
while on the wizard page (which also listens to `Esc` and `Enter` key).
Without a keyboard manager class, both listeners would have been
triggered at the same time.
Sergiu Miclea 8 лет назад
Родитель
Сommit
f64bf1efea

+ 5 - 1
src/components/molecules/Modal/Modal.jsx

@@ -19,6 +19,7 @@ import Modal from 'react-modal'
 
 import Palette from '../../styleUtils/Palette'
 import StyleProps from '../../styleUtils/StyleProps'
+import KeyboardManager from '../../../utils/KeyboardManager'
 
 const Title = styled.div`
   height: 48px;
@@ -56,7 +57,10 @@ class NewModal extends React.Component {
   }
 
   componentWillReceiveProps(newProps) {
-    if (this.props.isOpen === true && newProps.isOpen === false) {
+    if (!this.props.isOpen && newProps.isOpen) {
+      KeyboardManager.onKeyDown('modal', null, 1)
+    } else if (this.props.isOpen && !newProps.isOpen) {
+      KeyboardManager.removeKeyDown('modal')
       this.handleModalClose()
     }
   }

+ 9 - 16
src/components/organisms/AlertModal/AlertModal.jsx

@@ -19,6 +19,7 @@ import PropTypes from 'prop-types'
 import { Modal, Button, StatusImage } from 'components'
 
 import Palette from '../../styleUtils/Palette'
+import KeyboardManager from '../../../utils/KeyboardManager'
 
 import questionImage from './images/question.svg'
 import errorImage from './images/error.svg'
@@ -65,24 +66,16 @@ class AlertModal extends React.Component {
     type: 'confirmation',
   }
 
-  constructor() {
-    super()
-
-    this.handleKeyPress = this.handleKeyPress.bind(this)
-  }
-
-  componentDidMount() {
-    document.addEventListener('keypress', this.handleKeyPress)
-  }
-
-  componentWillUnmount() {
-    document.removeEventListener('keypress', this.handleKeyPress)
+  componentWillReceiveProps(newProps) {
+    if (newProps.isOpen && !this.props.isOpen) {
+      KeyboardManager.onEnter('alert', () => { this.handleEnter() }, 2)
+    } else if (!newProps.isOpen && this.props.isOpen) {
+      KeyboardManager.removeKeyDown('alert')
+    }
   }
 
-  handleKeyPress(evt) {
-    if (evt.keyCode === 13 && this.props.isOpen) {
-      this.props.onConfirmation()
-    }
+  handleEnter() {
+    this.props.onConfirmation()
   }
 
   renderDismissButton() {

+ 10 - 12
src/components/pages/WizardPage/WizardPage.jsx

@@ -42,6 +42,7 @@ import ReplicaActions from '../../../actions/ReplicaActions'
 import ScheduleActions from '../../../actions/ScheduleActions'
 import ScheduleStore from '../../../stores/ScheduleStore'
 import Wait from '../../../utils/Wait'
+import KeyboardManager from '../../../utils/KeyboardManager'
 import { wizardConfig, executionOptions } from '../../../config'
 
 const Wrapper = styled.div``
@@ -80,8 +81,6 @@ class WizardPage extends React.Component {
       showNewEndpointModal: false,
       nextButtonDisabled: false,
     }
-
-    this.handleKeyPress = this.handleKeyPress.bind(this)
   }
 
   componentWillMount() {
@@ -94,24 +93,23 @@ class WizardPage extends React.Component {
 
   componentDidMount() {
     document.title = 'Coriolis Wizard'
-    document.addEventListener('keydown', this.handleKeyPress)
+    KeyboardManager.onEnter('wizard', () => { this.handleEnterKey() })
+    KeyboardManager.onEsc('wizard', () => { this.handleEscKey() })
   }
 
   componentWillUnmount() {
     WizardActions.clearData()
-    document.removeEventListener('keydown', this.handleKeyPress)
+    KeyboardManager.removeKeyDown('wizard')
   }
 
-  handleKeyPress(evt) {
-    if (this.state.showNewEndpointModal) {
-      return
-    }
-    if (evt.keyCode === 13 && this.contentRef && !this.contentRef.isNextButtonDisabled()) {
+  handleEnterKey() {
+    if (this.contentRef && !this.contentRef.isNextButtonDisabled()) {
       this.handleNextClick()
     }
-    if (evt.keyCode === 27) {
-      this.handleBackClick()
-    }
+  }
+
+  handleEscKey() {
+    this.handleBackClick()
   }
 
   loadDataForPage(page) {

+ 59 - 0
src/utils/KeyboardManager.js

@@ -0,0 +1,59 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+let eventAdded = false
+let listeners = []
+const keyDownHandler = evt => {
+  let maxPriority = 0
+  listeners.forEach(l => { maxPriority = Math.max(l.priority, maxPriority) })
+  let prioritizedListeners = listeners.filter(l => l.priority === maxPriority)
+  prioritizedListeners.forEach(listener => {
+    if (listener.callback) listener.callback(evt)
+  })
+}
+export default class KeyboardManager {
+  static eventAdded = false
+  static onKeyDown(id, callback, priority) {
+    if (!eventAdded) {
+      eventAdded = true
+      document.addEventListener('keydown', evt => { keyDownHandler(evt) })
+    }
+
+    let listener = listeners.find(l => l.id === id)
+    if (listener) {
+      return
+    }
+    listeners.push({ id, callback, priority: priority || 0 })
+  }
+
+  static onEnter(id, callback, priority) {
+    this.onKeyDown(`${id}-enter`, evt => {
+      if (evt.keyCode === 13) {
+        callback(evt)
+      }
+    }, priority)
+  }
+
+  static onEsc(id, callback, priority) {
+    this.onKeyDown(`${id}-esc`, evt => {
+      if (evt.keyCode === 27) {
+        callback(evt)
+      }
+    }, priority)
+  }
+
+  static removeKeyDown(id) {
+    listeners = listeners.filter(l => l.id !== id && l.id !== `${id}-enter` && l.id !== `${id}-esc`)
+  }
+}