mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-31 10:11:38 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75db0e9183 | ||
|
|
4b9a5fd8b4 | ||
|
|
b6be677db9 | ||
|
|
91c9457392 | ||
|
|
006d315a1a | ||
|
|
220eb79357 | ||
|
|
5db9a765b0 | ||
|
|
931d666fcc | ||
|
|
40c433c6df | ||
|
|
9888c2a3c6 |
@@ -75,7 +75,7 @@ Examples of using React Native for Web with other web tools:
|
||||
|
||||
* [Docz](https://github.com/pedronauck/docz-plugin-react-native)
|
||||
* [Gatsby](https://github.com/slorber/gatsby-plugin-react-native-web)
|
||||
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
|
||||
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web) (and [example recipes](https://gist.github.com/necolas/f9034091723f1b279be86c7429eb0c96))
|
||||
* [Phenomic](https://github.com/phenomic/phenomic/tree/master/examples/react-native-web-app)
|
||||
* [Razzle](https://github.com/jaredpalmer/razzle/tree/master/examples/with-react-native-web)
|
||||
* [Storybook](https://github.com/necolas/react-native-web/tree/master/packages/website/storybook/.storybook)
|
||||
|
||||
@@ -24,20 +24,13 @@ yarn add react-art
|
||||
|
||||
## Starter kits
|
||||
|
||||
Web: [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
[create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
includes built-in support for aliasing `react-native-web` to `react-native`.
|
||||
|
||||
```
|
||||
create-react-app my-app
|
||||
```
|
||||
|
||||
Multi-platform: [create-react-native-app](https://github.com/react-community/create-react-native-app)
|
||||
includes experimental support for Web.
|
||||
|
||||
```
|
||||
create-react-native-app my-app --with-web-support
|
||||
```
|
||||
|
||||
## Configuring a module bundler
|
||||
|
||||
If you have a custom setup, you may choose to configure your module bundler to
|
||||
|
||||
@@ -54,7 +54,7 @@ from your web app build. To help with this, you can install the following Babel
|
||||
plugin:
|
||||
|
||||
```
|
||||
yarn install --dev babel-plugin-react-native-web
|
||||
yarn add --dev babel-plugin-react-native-web
|
||||
```
|
||||
|
||||
Create a `web/webpack.config.js` file:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"name": "react-native-web-monorepo",
|
||||
"scripts": {
|
||||
"clean": "del ./packages/*/dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "babel-plugin-react-native-web",
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"description": "Babel plugin for React Native for Web",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "benchmarks",
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -f index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
|
||||
"release": "yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
|
||||
@@ -18,7 +18,7 @@
|
||||
"react-dom": "^16.5.1",
|
||||
"react-fela": "^7.3.1",
|
||||
"react-jss": "^8.6.1",
|
||||
"react-native-web": "0.9.5",
|
||||
"react-native-web": "0.9.7",
|
||||
"reactxp": "^1.3.0",
|
||||
"styled-components": "^3.3.3",
|
||||
"styled-jsx": "^2.2.7",
|
||||
@@ -26,7 +26,7 @@
|
||||
"styletron-react": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.9.5",
|
||||
"babel-plugin-react-native-web": "0.9.7",
|
||||
"css-loader": "^1.0.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"url-loader": "^1.0.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "react-native-examples",
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -f src/index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
|
||||
"release": "yarn build && git checkout gh-pages && rm -rf ../../examples && mv dist ../../examples && git add -A && git commit -m \"Examples deploy\" && git push origin gh-pages && git checkout -"
|
||||
@@ -10,10 +10,10 @@
|
||||
"babel-runtime": "^6.26.0",
|
||||
"react": "^16.5.1",
|
||||
"react-dom": "^16.5.1",
|
||||
"react-native-web": "0.9.5"
|
||||
"react-native-web": "0.9.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.9.5",
|
||||
"babel-plugin-react-native-web": "0.9.7",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"webpack": "^4.8.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"description": "React Native for Web",
|
||||
"module": "dist/index.js",
|
||||
"main": "dist/cjs/index.js",
|
||||
|
||||
@@ -279,15 +279,13 @@ const styles = StyleSheet.create({
|
||||
...commonStyle,
|
||||
flexDirection: 'column',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
touchAction: 'pan-y'
|
||||
overflowY: 'auto'
|
||||
},
|
||||
baseHorizontal: {
|
||||
...commonStyle,
|
||||
flexDirection: 'row',
|
||||
overflowX: 'auto',
|
||||
overflowY: 'hidden',
|
||||
touchAction: 'pan-x'
|
||||
overflowY: 'hidden'
|
||||
},
|
||||
contentContainerHorizontal: {
|
||||
flexDirection: 'row'
|
||||
|
||||
@@ -11,125 +11,250 @@
|
||||
* 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/focus-ring
|
||||
* This software or document includes material copied from or derived from https://github.com/WICG/focus-visible.
|
||||
* Copyright © 2018 W3C® (MIT, ERCIM, Keio, Beihang).
|
||||
* W3C Software Notice and License: https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
|
||||
*
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import hash from '../../vendor/hash';
|
||||
|
||||
const rule = ':focus { outline: none; }';
|
||||
let ruleExists = false;
|
||||
const focusVisibleClass =
|
||||
'rn-' + (process.env.NODE_ENV !== 'production' ? 'focusVisible-' : '') + hash('focus-visible');
|
||||
|
||||
const rule = `:focus:not(.${focusVisibleClass}) { outline: none; }`;
|
||||
|
||||
const modality = styleElement => {
|
||||
if (!canUseDOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
let hadKeyboardEvent = false;
|
||||
let keyboardThrottleTimeoutID = 0;
|
||||
let hadKeyboardEvent = true;
|
||||
let hadFocusVisibleRecently = false;
|
||||
let hadFocusVisibleRecentlyTimeout = null;
|
||||
|
||||
const proto = window.Element.prototype;
|
||||
const matches =
|
||||
proto.matches ||
|
||||
proto.mozMatchesSelector ||
|
||||
proto.msMatchesSelector ||
|
||||
proto.webkitMatchesSelector;
|
||||
const inputTypesWhitelist = {
|
||||
text: true,
|
||||
search: true,
|
||||
url: true,
|
||||
tel: true,
|
||||
email: true,
|
||||
password: true,
|
||||
number: true,
|
||||
date: true,
|
||||
month: true,
|
||||
week: true,
|
||||
time: true,
|
||||
datetime: true,
|
||||
'datetime-local': true
|
||||
};
|
||||
|
||||
// These elements should always have a focus ring drawn, because they are
|
||||
// associated with switching to a keyboard modality.
|
||||
const keyboardModalityWhitelist = [
|
||||
'input:not([type])',
|
||||
'input[type=text]',
|
||||
'input[type=search]',
|
||||
'input[type=url]',
|
||||
'input[type=tel]',
|
||||
'input[type=email]',
|
||||
'input[type=password]',
|
||||
'input[type=number]',
|
||||
'input[type=date]',
|
||||
'input[type=month]',
|
||||
'input[type=week]',
|
||||
'input[type=time]',
|
||||
'input[type=datetime]',
|
||||
'input[type=datetime-local]',
|
||||
'textarea',
|
||||
'[role=textbox]'
|
||||
].join(',');
|
||||
/**
|
||||
* Helper function for legacy browsers and iframes which sometimes focus
|
||||
* elements like document, body, and non-interactive SVG.
|
||||
*/
|
||||
function isValidFocusTarget(el) {
|
||||
if (
|
||||
el &&
|
||||
el !== document &&
|
||||
el.nodeName !== 'HTML' &&
|
||||
el.nodeName !== 'BODY' &&
|
||||
'classList' in el &&
|
||||
'contains' in el.classList
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes whether the given element should automatically trigger the
|
||||
* `focus-ring`.
|
||||
* `focus-visible` class being added, i.e. whether it should always match
|
||||
* `:focus-visible` when focused.
|
||||
*/
|
||||
const focusTriggersKeyboardModality = el => {
|
||||
if (matches) {
|
||||
return matches.call(el, keyboardModalityWhitelist) && matches.call(el, ':not([readonly])');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
function focusTriggersKeyboardModality(el) {
|
||||
const type = el.type;
|
||||
const tagName = el.tagName;
|
||||
const isReadOnly = el.readOnly;
|
||||
|
||||
/**
|
||||
* Add the focus ring style
|
||||
*/
|
||||
const addFocusRing = () => {
|
||||
if (styleElement && ruleExists) {
|
||||
styleElement.sheet.deleteRule(0);
|
||||
ruleExists = false;
|
||||
if (tagName === 'INPUT' && inputTypesWhitelist[type] && !isReadOnly) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the focus ring style
|
||||
*/
|
||||
const removeFocusRing = () => {
|
||||
if (styleElement && !ruleExists) {
|
||||
styleElement.sheet.insertRule(rule, 0);
|
||||
ruleExists = true;
|
||||
if (tagName === 'TEXTAREA' && !isReadOnly) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* On `keydown`, set `hadKeyboardEvent`, to be removed 100ms later if there
|
||||
* are no further keyboard events. The 100ms throttle handles cases where
|
||||
* focus is redirected programmatically after a keyboard event, such as
|
||||
* opening a menu or dialog.
|
||||
*/
|
||||
const handleKeyDown = e => {
|
||||
hadKeyboardEvent = true;
|
||||
if (keyboardThrottleTimeoutID !== 0) {
|
||||
clearTimeout(keyboardThrottleTimeoutID);
|
||||
if (el.isContentEditable) {
|
||||
return true;
|
||||
}
|
||||
keyboardThrottleTimeoutID = setTimeout(() => {
|
||||
hadKeyboardEvent = false;
|
||||
keyboardThrottleTimeoutID = 0;
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the focus-ring when the keyboard was used to focus
|
||||
*/
|
||||
const handleFocus = e => {
|
||||
if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
|
||||
addFocusRing();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the focus-ring when the keyboard was used to focus
|
||||
*/
|
||||
const handleBlur = () => {
|
||||
if (!hadKeyboardEvent) {
|
||||
removeFocusRing();
|
||||
}
|
||||
};
|
||||
|
||||
if (document.body && document.body.addEventListener) {
|
||||
removeFocusRing();
|
||||
document.body.addEventListener('keydown', handleKeyDown, true);
|
||||
document.body.addEventListener('focus', handleFocus, true);
|
||||
document.body.addEventListener('blur', handleBlur, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the `focus-visible` class to the given element if it was not added by
|
||||
* the author.
|
||||
*/
|
||||
function addFocusVisibleClass(el) {
|
||||
if (el.classList.contains(focusVisibleClass)) {
|
||||
return;
|
||||
}
|
||||
el.classList.add(focusVisibleClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the `focus-visible` class from the given element if it was not
|
||||
* originally added by the author.
|
||||
*/
|
||||
function removeFocusVisibleClass(el) {
|
||||
el.classList.remove(focusVisibleClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat `keydown` as a signal that the user is in keyboard modality.
|
||||
* Apply `focus-visible` to any current active element and keep track
|
||||
* of our keyboard modality state with `hadKeyboardEvent`.
|
||||
*/
|
||||
function onKeyDown(e) {
|
||||
if (e.key !== 'Tab' && (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isValidFocusTarget(document.activeElement)) {
|
||||
addFocusVisibleClass(document.activeElement);
|
||||
}
|
||||
|
||||
hadKeyboardEvent = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If at any point a user clicks with a pointing device, ensure that we change
|
||||
* the modality away from keyboard.
|
||||
* This avoids the situation where a user presses a key on an already focused
|
||||
* element, and then clicks on a different element, focusing it with a
|
||||
* pointing device, while we still think we're in keyboard modality.
|
||||
*/
|
||||
function onPointerDown(e) {
|
||||
hadKeyboardEvent = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* On `focus`, add the `focus-visible` class to the target if:
|
||||
* - the target received focus as a result of keyboard navigation, or
|
||||
* - the event target is an element that will likely require interaction
|
||||
* via the keyboard (e.g. a text box)
|
||||
*/
|
||||
function onFocus(e) {
|
||||
// Prevent IE from focusing the document or HTML element.
|
||||
if (!isValidFocusTarget(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
|
||||
addFocusVisibleClass(e.target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On `blur`, remove the `focus-visible` class from the target.
|
||||
*/
|
||||
function onBlur(e) {
|
||||
if (!isValidFocusTarget(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.target.classList.contains(focusVisibleClass)) {
|
||||
// To detect a tab/window switch, we look for a blur event followed
|
||||
// rapidly by a visibility change.
|
||||
// If we don't see a visibility change within 100ms, it's probably a
|
||||
// regular focus change.
|
||||
hadFocusVisibleRecently = true;
|
||||
window.clearTimeout(hadFocusVisibleRecentlyTimeout);
|
||||
hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {
|
||||
hadFocusVisibleRecently = false;
|
||||
window.clearTimeout(hadFocusVisibleRecentlyTimeout);
|
||||
}, 100);
|
||||
removeFocusVisibleClass(e.target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user changes tabs, keep track of whether or not the previously
|
||||
* focused element had .focus-visible.
|
||||
*/
|
||||
function onVisibilityChange(e) {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
// If the tab becomes active again, the browser will handle calling focus
|
||||
// on the element (Safari actually calls it twice).
|
||||
// If this tab change caused a blur on an element with focus-visible,
|
||||
// re-apply the class when the user switches back to the tab.
|
||||
if (hadFocusVisibleRecently) {
|
||||
hadKeyboardEvent = true;
|
||||
}
|
||||
addInitialPointerMoveListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a group of listeners to detect usage of any pointing devices.
|
||||
* These listeners will be added when the polyfill first loads, and anytime
|
||||
* the window is blurred, so that they are active when the window regains
|
||||
* focus.
|
||||
*/
|
||||
function addInitialPointerMoveListeners() {
|
||||
document.addEventListener('mousemove', onInitialPointerMove);
|
||||
document.addEventListener('mousedown', onInitialPointerMove);
|
||||
document.addEventListener('mouseup', onInitialPointerMove);
|
||||
document.addEventListener('pointermove', onInitialPointerMove);
|
||||
document.addEventListener('pointerdown', onInitialPointerMove);
|
||||
document.addEventListener('pointerup', onInitialPointerMove);
|
||||
document.addEventListener('touchmove', onInitialPointerMove);
|
||||
document.addEventListener('touchstart', onInitialPointerMove);
|
||||
document.addEventListener('touchend', onInitialPointerMove);
|
||||
}
|
||||
|
||||
function removeInitialPointerMoveListeners() {
|
||||
document.removeEventListener('mousemove', onInitialPointerMove);
|
||||
document.removeEventListener('mousedown', onInitialPointerMove);
|
||||
document.removeEventListener('mouseup', onInitialPointerMove);
|
||||
document.removeEventListener('pointermove', onInitialPointerMove);
|
||||
document.removeEventListener('pointerdown', onInitialPointerMove);
|
||||
document.removeEventListener('pointerup', onInitialPointerMove);
|
||||
document.removeEventListener('touchmove', onInitialPointerMove);
|
||||
document.removeEventListener('touchstart', onInitialPointerMove);
|
||||
document.removeEventListener('touchend', onInitialPointerMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the polfyill first loads, assume the user is in keyboard modality.
|
||||
* If any event is received from a pointing device (e.g. mouse, pointer,
|
||||
* touch), turn off keyboard modality.
|
||||
* This accounts for situations where focus enters the page from the URL bar.
|
||||
*/
|
||||
function onInitialPointerMove(e) {
|
||||
// Work around a Safari quirk that fires a mousemove on <html> whenever the
|
||||
// window blurs, even if you're tabbing out of the page. ¯\_(ツ)_/¯
|
||||
if (e.target.nodeName === 'HTML') {
|
||||
return;
|
||||
}
|
||||
|
||||
hadKeyboardEvent = false;
|
||||
removeInitialPointerMoveListeners();
|
||||
}
|
||||
|
||||
styleElement.sheet.insertRule(rule, 0);
|
||||
|
||||
document.addEventListener('keydown', onKeyDown, true);
|
||||
document.addEventListener('mousedown', onPointerDown, true);
|
||||
document.addEventListener('pointerdown', onPointerDown, true);
|
||||
document.addEventListener('touchstart', onPointerDown, true);
|
||||
document.addEventListener('focus', onFocus, true);
|
||||
document.addEventListener('blur', onBlur, true);
|
||||
document.addEventListener('visibilitychange', onVisibilityChange, true);
|
||||
addInitialPointerMoveListeners();
|
||||
};
|
||||
|
||||
export default modality;
|
||||
|
||||
@@ -696,9 +696,16 @@ const TouchableMixin = {
|
||||
const touch = TouchEventUtils.extractSingleTouch(e.nativeEvent);
|
||||
const pageX = touch && touch.pageX;
|
||||
const pageY = touch && touch.pageY;
|
||||
const locationX = touch && touch.locationX;
|
||||
const locationY = touch && touch.locationY;
|
||||
this.pressInLocation = { pageX, pageY, locationX, locationY };
|
||||
this.pressInLocation = {
|
||||
pageX,
|
||||
pageY,
|
||||
get locationX() {
|
||||
return touch && touch.locationX;
|
||||
},
|
||||
get locationY() {
|
||||
return touch && touch.locationY;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_getDistanceBetweenPoints: function(aX: number, aY: number, bX: number, bY: number) {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import UIManager from '..';
|
||||
|
||||
const createStyledNode = (style = {}) => {
|
||||
const root = document.createElement('div');
|
||||
const createStyledNode = (name = 'div', style = {}) => {
|
||||
const root = document.createElement(name);
|
||||
Object.keys(style).forEach(prop => {
|
||||
root.style[prop] = style[prop];
|
||||
});
|
||||
@@ -18,6 +18,29 @@ const componentStub = {
|
||||
};
|
||||
|
||||
describe('apis/UIManager', () => {
|
||||
describe('focus', () => {
|
||||
test('sets tabIndex="-1" on elements not programmatically focusable by default', () => {
|
||||
const node = createStyledNode();
|
||||
UIManager.focus(node);
|
||||
expect(node.getAttribute('tabIndex')).toEqual('-1');
|
||||
});
|
||||
|
||||
test('doesn\'t set tabIndex="-1" on elements with an existing tabIndex', () => {
|
||||
const node = createStyledNode();
|
||||
node.tabIndex = 0;
|
||||
UIManager.focus(node);
|
||||
expect(node.getAttribute('tabIndex')).toEqual('0');
|
||||
});
|
||||
|
||||
test('doesn\'t set tabIndex="-1" on elements focusable by default', () => {
|
||||
['a', 'input', 'select', 'textarea'].forEach(name => {
|
||||
const node = createStyledNode(name);
|
||||
UIManager.focus(node);
|
||||
expect(node.getAttribute('tabIndex')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateView', () => {
|
||||
test('supports className alias for class', () => {
|
||||
const node = createStyledNode();
|
||||
@@ -27,7 +50,7 @@ describe('apis/UIManager', () => {
|
||||
});
|
||||
|
||||
test('adds correct DOM styles to existing style', () => {
|
||||
const node = createStyledNode({ color: 'red' });
|
||||
const node = createStyledNode('div', { color: 'red' });
|
||||
const props = { style: { marginTop: 0, marginBottom: 0, opacity: 0 } };
|
||||
UIManager.updateView(node, props, componentStub);
|
||||
expect(node.getAttribute('style')).toEqual(
|
||||
@@ -36,7 +59,7 @@ describe('apis/UIManager', () => {
|
||||
});
|
||||
|
||||
test('replaces input and textarea text', () => {
|
||||
const node = createStyledNode();
|
||||
const node = createStyledNode('textarea');
|
||||
node.value = 'initial';
|
||||
const textProp = { text: 'expected-text' };
|
||||
const valueProp = { value: 'expected-value' };
|
||||
|
||||
@@ -37,6 +37,13 @@ const measureLayout = (node, relativeToNativeNode, callback) => {
|
||||
}
|
||||
};
|
||||
|
||||
const focusableElements = {
|
||||
A: true,
|
||||
INPUT: true,
|
||||
SELECT: true,
|
||||
TEXTAREA: true
|
||||
};
|
||||
|
||||
const UIManager = {
|
||||
blur(node) {
|
||||
try {
|
||||
@@ -46,6 +53,13 @@ const UIManager = {
|
||||
|
||||
focus(node) {
|
||||
try {
|
||||
const name = node.nodeName;
|
||||
// A tabIndex of -1 allows element to be programmatically focused but
|
||||
// prevents keyboard focus, so we don't want to set the value on elements
|
||||
// that support keyboard focus by default.
|
||||
if (node.getAttribute('tabIndex') == null && focusableElements[name] == null) {
|
||||
node.setAttribute('tabIndex', '-1');
|
||||
}
|
||||
node.focus();
|
||||
} catch (err) {}
|
||||
},
|
||||
|
||||
@@ -78,6 +78,7 @@ function normalizeTouchEvent(nativeEvent) {
|
||||
typeof nativeEvent.stopPropagation === 'function'
|
||||
? nativeEvent.stopPropagation.bind(nativeEvent)
|
||||
: emptyFunction;
|
||||
const singleChangedTouch = changedTouches[0];
|
||||
|
||||
const event = {
|
||||
_normalized: true,
|
||||
@@ -85,11 +86,15 @@ function normalizeTouchEvent(nativeEvent) {
|
||||
cancelable: nativeEvent.cancelable,
|
||||
changedTouches,
|
||||
defaultPrevented: nativeEvent.defaultPrevented,
|
||||
identifier: undefined,
|
||||
locationX: undefined,
|
||||
locationY: undefined,
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
identifier: singleChangedTouch ? singleChangedTouch.identifier : undefined,
|
||||
get locationX() {
|
||||
return singleChangedTouch ? singleChangedTouch.locationX : undefined;
|
||||
},
|
||||
get locationY() {
|
||||
return singleChangedTouch ? singleChangedTouch.locationY : undefined;
|
||||
},
|
||||
pageX: singleChangedTouch ? singleChangedTouch.pageX : nativeEvent.pageX,
|
||||
pageY: singleChangedTouch ? singleChangedTouch.pageY : nativeEvent.pageY,
|
||||
preventDefault,
|
||||
stopImmediatePropagation,
|
||||
stopPropagation,
|
||||
@@ -102,14 +107,6 @@ function normalizeTouchEvent(nativeEvent) {
|
||||
which: nativeEvent.which
|
||||
};
|
||||
|
||||
if (changedTouches[0]) {
|
||||
event.identifier = changedTouches[0].identifier;
|
||||
event.pageX = changedTouches[0].pageX;
|
||||
event.pageY = changedTouches[0].pageY;
|
||||
event.locationX = changedTouches[0].locationX;
|
||||
event.locationY = changedTouches[0].locationY;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -164,8 +161,12 @@ function normalizeMouseEvent(nativeEvent) {
|
||||
changedTouches: touches,
|
||||
defaultPrevented: nativeEvent.defaultPrevented,
|
||||
identifier: touches[0].identifier,
|
||||
locationX: touches[0].locationX,
|
||||
locationY: touches[0].locationY,
|
||||
get locationX() {
|
||||
return touches[0].locationX;
|
||||
},
|
||||
get locationY() {
|
||||
return touches[0].locationY;
|
||||
},
|
||||
pageX: nativeEvent.pageX,
|
||||
pageY: nativeEvent.pageY,
|
||||
preventDefault,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "website",
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"scripts": {
|
||||
"build": "build-storybook -o ./dist -c ./storybook/.storybook",
|
||||
"start": "start-storybook -p 9001 -c ./storybook/.storybook",
|
||||
@@ -12,10 +12,10 @@
|
||||
"@storybook/react": "^3.4.3",
|
||||
"react": "^16.5.1",
|
||||
"react-dom": "^16.5.1",
|
||||
"react-native-web": "0.9.5"
|
||||
"react-native-web": "0.9.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.9.5",
|
||||
"babel-plugin-react-native-web": "0.9.7",
|
||||
"url-loader": "^1.0.1",
|
||||
"webpack": "^4.8.1"
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ class AppText extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style, ...rest } = this.props;
|
||||
const { accessibilityRole, style, ...rest } = this.props;
|
||||
const isInAParentText = this.context;
|
||||
return (
|
||||
<Text
|
||||
{...rest}
|
||||
accessibilityRole={rest.href ? 'link' : undefined}
|
||||
accessibilityRole={rest.href ? 'link' : accessibilityRole}
|
||||
style={[!isInAParentText && styles.baseText, style, rest.href && styles.link]}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,11 @@ import AppText from './AppText';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
const SectionTitle = ({ children }) => <AppText style={styles.sectionTitle}>{children}</AppText>;
|
||||
const SectionTitle = ({ children }) => (
|
||||
<AppText accessibilityRole="heading" aria-level="2" style={styles.sectionTitle}>
|
||||
{children}
|
||||
</AppText>
|
||||
);
|
||||
|
||||
const Section = ({ children, title }) => (
|
||||
<View>
|
||||
|
||||
@@ -10,7 +10,11 @@ import insertBetween from './insertBetween';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
const Title = ({ children }) => <AppText style={styles.title}>{children}</AppText>;
|
||||
const Title = ({ children }) => (
|
||||
<AppText accessibilityRole="heading" style={styles.title}>
|
||||
{children}
|
||||
</AppText>
|
||||
);
|
||||
|
||||
export const Description = ({ children }) => (
|
||||
<AppText style={styles.description}>
|
||||
|
||||
Reference in New Issue
Block a user