mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-06-12 05:53:56 +00:00
[change] modality-specific focus styles
Remove the default focus ring when the keyboard is not being used. This provides a superior UX when using touch or mouse. Fix #310
This commit is contained in:
@@ -18,6 +18,9 @@ import View from './components/View';
|
||||
|
||||
// modules
|
||||
import createDOMElement from './modules/createDOMElement';
|
||||
import modality from './modules/modality';
|
||||
|
||||
modality();
|
||||
|
||||
const ReactNativeCore = {
|
||||
createDOMElement,
|
||||
|
||||
@@ -39,6 +39,7 @@ import View from './components/View';
|
||||
|
||||
// modules
|
||||
import createDOMElement from './modules/createDOMElement';
|
||||
import modality from './modules/modality';
|
||||
import NativeModules from './modules/NativeModules';
|
||||
|
||||
// propTypes
|
||||
@@ -46,6 +47,8 @@ import ColorPropType from './propTypes/ColorPropType';
|
||||
import EdgeInsetsPropType from './propTypes/EdgeInsetsPropType';
|
||||
import PointPropType from './propTypes/PointPropType';
|
||||
|
||||
modality();
|
||||
|
||||
const ReactNative = {
|
||||
// top-level API
|
||||
findNodeHandle,
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/* global document, window */
|
||||
|
||||
/**
|
||||
* Adapts focus styles based on the user's active input modality (i.e., how
|
||||
* they are interacting with the UI right now).
|
||||
*
|
||||
* Focus styles are only relevant when using the keyboard to interact with the
|
||||
* page. If we only show the focus ring when relevant, we can avoid user
|
||||
* confusion without compromising accessibility.
|
||||
*
|
||||
* The script uses two heuristics to determine whether the keyboard is being used:
|
||||
*
|
||||
* 1. a keydown event occurred immediately before a focus event;
|
||||
* 2. a focus event happened on an element which requires keyboard interaction (e.g., a text field);
|
||||
*
|
||||
* Based on https://github.com/WICG/modality
|
||||
*/
|
||||
const modality = () => {
|
||||
/**
|
||||
* Determine whether the keyboard is required when an element is focused
|
||||
*/
|
||||
const proto = window.Element.prototype;
|
||||
const matcher = proto.matches || proto.mozMatchesSelector || proto.msMatchesSelector || proto.webkitMatchesSelector;
|
||||
const keyboardModalityWhitelist = [
|
||||
'input:not([type])',
|
||||
'input[type=text]',
|
||||
'input[type=number]',
|
||||
'input[type=date]',
|
||||
'input[type=time]',
|
||||
'input[type=datetime]',
|
||||
'textarea',
|
||||
'[role=textbox]',
|
||||
// indicates that a custom element supports the keyboard
|
||||
'[supports-modality=keyboard]'
|
||||
].join(',');
|
||||
|
||||
const focusTriggersKeyboardModality = (el) => {
|
||||
if (matcher) {
|
||||
return matcher.call(el, keyboardModalityWhitelist) && matcher.call(el, ':not([readonly])');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable the focus ring by default
|
||||
*/
|
||||
const id = 'modality__';
|
||||
const style = `<style id="${id}">:focus { outline: none; }</style>`;
|
||||
document.head.insertAdjacentHTML('afterbegin', style);
|
||||
const styleElement = document.getElementById(id);
|
||||
|
||||
const disableFocus = () => {
|
||||
if (styleElement) {
|
||||
styleElement.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
const enableFocus = () => {
|
||||
if (styleElement) {
|
||||
styleElement.disabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Manage the modality focus state
|
||||
*/
|
||||
let keyboardTimer;
|
||||
let hadKeyboardEvent = false;
|
||||
|
||||
// track when the keyboard is in use
|
||||
document.body.addEventListener('keydown', () => {
|
||||
hadKeyboardEvent = true;
|
||||
if (keyboardTimer) {
|
||||
clearTimeout(keyboardTimer);
|
||||
}
|
||||
keyboardTimer = setTimeout(() => {
|
||||
hadKeyboardEvent = false;
|
||||
}, 100);
|
||||
}, true);
|
||||
|
||||
// disable focus style reset when the keyboard is in use
|
||||
document.body.addEventListener('focus', (e) => {
|
||||
if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
|
||||
enableFocus();
|
||||
}
|
||||
}, true);
|
||||
|
||||
// enable focus style reset when keyboard is no longer in use
|
||||
document.body.addEventListener('blur', () => {
|
||||
if (!hadKeyboardEvent) {
|
||||
disableFocus();
|
||||
}
|
||||
}, true);
|
||||
};
|
||||
|
||||
export default modality;
|
||||
Reference in New Issue
Block a user