Compare commits

..

26 Commits

Author SHA1 Message Date
Hiroki Sato
d880457f22 [fix] set textShadow if only blur and color provided
Mirrors a recent fix to React Native.

Close #1182
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
7663adebe8 [change] Update VirtualizedSectionList implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
1d1ebdb984 [change] Update VirtualizedList implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
04b71784a4 [change] Update SwipeableFlatList implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
a2402e6077 [change] Update SectionList implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
3d4a179ef6 [change] Update FlatList implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
56ced136d7 Make Batchinator flow strict-local 2018-12-31 10:29:38 -08:00
Nicolas Gallagher
dfb878dc26 [change] update Animated implementation
Mirror contents of React Native 0.57.5

Ref #1172
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
ead21b3aa2 [change] remove 'resizeMode' static from 'Image'
Use strings instead of the 'Image.resizeMode' static. Corresponding change in React Native:

https://github.com/facebook/react-native/commit/870775e
2018-12-31 10:29:38 -08:00
Nicolas Gallagher
2d1e303a6a [fix] ScrollView with stickyHeaderIndices regression
A ScrollView with stickyHeaderIndices would not render children that
weren't sticky.

Fixes 1e202b6bd5
2018-12-31 10:26:33 -08:00
Nicolas Gallagher
209ff1fa40 0.9.11 2018-12-31 08:23:58 -08:00
Nicolas Gallagher
34d8160a43 [fix] CSS animation insertion for Android 5.1 HuaWei browser
Inserting unprefixed CSS keyframes rules causes a `SYNTAX_ERR: DOM Exception
12` error in Android 5.1. A similar issue with inserting rules containing
vendor-prefixed pseudo-selectors was patched by wrapping rule in `@media all
{}` blocks. This patch removes the media query wrapper from keyframe animations
(as it doesn't prevent the error) and relies on `CSSStyleSheet::insertRule`
being called within a try-catch block.

Fix #1199
Close #1210
2018-12-31 07:47:52 -08:00
Nicolas Gallagher
ada5651be2 Don't minify local benchmarks build 2018-12-24 13:35:21 +00:00
Nicolas Gallagher
9fe9d3c68a Update react-native-web dependencies 2018-12-24 13:03:49 +00:00
krister
1e202b6bd5 [add] ScrollView support for pagingEnabled
Available in browsers that support CSS snappoints.

Fix #1057
Close #1212

Co-authored-by: Nicolas Gallagher <nicolasgallagher@gmail.com>
2018-12-23 18:05:29 +00:00
Robert Sayre
2b77bfd853 [fix] improve style resolver performance
Script time in the benchmark was profiled by adding `console.profile` around
the timings for script time. The call to Array.join in the resolve function
stood out. Since the code already iterates over the array it can run slightly
faster by building the cache key in that loop instead.

Close #1213
2018-12-23 15:07:48 +00:00
Nicolas Gallagher
d0ac55aa4f Update benchmarks dependencies 2018-12-21 21:33:43 +00:00
Nicolas Gallagher
66dd1bd9ef Remove glamor and radium from benchmarks
Glamor is unmaintained. Radium is extremely slow. There is no need to compare
against these libraries.
2018-12-21 21:20:30 +00:00
Nicolas Gallagher
6add18c6f0 0.9.10 2018-12-21 21:04:26 +00:00
krister
30d7c31b65 [fix] ScrollView smooth scrolling
Rely on the `element.scroll()` programmatic API when available (or polyfilled).

Fix #1203
Fix #1173
Close #1208
2018-12-21 20:57:25 +00:00
Raibima Putra
f7e6b43422 Fix docs typo
Close #1209
2018-12-21 20:46:29 +00:00
Nicolas Gallagher
4b3f6efb21 Support style inspection in production 2018-12-10 17:01:23 -08:00
Nicolas Gallagher
85e098deec 0.9.9 2018-12-05 14:46:11 -08:00
Nicolas Gallagher
c949b0145a [fix] TextInput onKeyPress supports Escape key
Fix #1189
2018-11-27 12:15:59 -08:00
Charlie Croom
86b4cf5a51 [add] scaleZ and scale3d style transforms
Web-specific additions similar to the web-specific additions to `translate` styles.

Close #1184
2018-11-27 11:53:04 -08:00
Giuseppe
1b7ce4eec6 [fix] Fix regression in modality refactor
Fixes https://github.com/necolas/react-native-web/pull/1169#issuecomment-440590544

And removes the focus ring for press-after-keyboard edge cases.

Close #1186

Co-authored-by: Nicolas Gallagher <nicolasgallagher@gmail.com>
2018-11-27 11:52:46 -08:00
66 changed files with 1977 additions and 1029 deletions

View File

@@ -140,7 +140,7 @@ React Native v0.55
| Picker | ✓ | |
| RefreshControl | ✘ | Not started ([#1027](https://github.com/necolas/react-native-web/issues/1027)). |
| SafeAreaView | ✓ | |
| ScrollView | ✓ | Missing momentum scroll events ([#1021](https://github.com/necolas/react-native-web/issues/1021)) and `pagingEnabled` ([#1057](https://github.com/necolas/react-native-web/issues/1057)). |
| ScrollView | ✓ | Missing momentum scroll events ([#1021](https://github.com/necolas/react-native-web/issues/1021)). |
| SectionList | ✓ | |
| Slider | ✘ | Not started ([#1022](https://github.com/necolas/react-native-web/issues/1022)). |
| StatusBar | (✓) | Mock. No equivalent web APIs. |

View File

@@ -36,7 +36,7 @@ target platform.
What follows is merely an _example_ of one basic way to package a web app using
[Webpack](https://webpack.js.org) and [Babel](https://babeljs.io/). (You can
also the React Native bundler, [Metro](https://github.com/facebook/metro), to
also use the React Native bundler, [Metro](https://github.com/facebook/metro), to
build web apps.)
Packaging web apps is subtly different to packaging React Native apps and is

View File

@@ -1,6 +1,6 @@
{
"private": true,
"version": "0.9.8",
"version": "0.9.11",
"name": "react-native-web-monorepo",
"scripts": {
"clean": "del ./packages/*/dist",
@@ -54,10 +54,10 @@
"lint-staged": "^7.1.0",
"npm-run-all": "^4.1.3",
"prettier": "^1.12.1",
"react": "^16.5.1",
"react-art": "^16.5.1",
"react-dom": "^16.5.1",
"react-test-renderer": "^16.5.1"
"react": "^16.7.0",
"react-art": "^16.7.0",
"react-dom": "^16.7.0",
"react-test-renderer": "^16.7.0"
},
"workspaces": [
"packages/*"

View File

@@ -1,6 +1,6 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.9.8",
"version": "0.9.11",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {

View File

@@ -1,37 +1,35 @@
{
"private": true,
"name": "benchmarks",
"version": "0.9.8",
"version": "0.9.11",
"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 -"
"release": "NODE_ENV=production 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 -"
},
"dependencies": {
"aphrodite": "^2.2.2",
"aphrodite": "^2.2.3",
"classnames": "^2.2.6",
"d3-scale-chromatic": "^1.3.0",
"emotion": "^9.2.4",
"fela": "^6.1.9",
"glamor": "2.20.40",
"radium": "^0.24.0",
"react": "^16.5.1",
"react-dom": "^16.5.1",
"react-fela": "^7.3.1",
"d3-scale-chromatic": "^1.3.3",
"emotion": "^10.0.5",
"fela": "^10.0.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-fela": "^10.0.2",
"react-jss": "^8.6.1",
"react-native-web": "0.9.8",
"reactxp": "^1.3.0",
"styled-components": "^3.3.3",
"styled-jsx": "^2.2.7",
"styletron-engine-atomic": "^1.0.5",
"styletron-react": "^4.3.1"
"react-native-web": "0.9.11",
"reactxp": "^1.5.0",
"styled-components": "^4.1.3",
"styled-jsx": "^3.1.2",
"styletron-engine-atomic": "^1.0.13",
"styletron-react": "^4.4.4"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.9.8",
"css-loader": "^1.0.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.15.1",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-cli": "^3.0.8"
"babel-plugin-react-native-web": "0.9.11",
"css-loader": "^2.0.2",
"style-loader": "^0.23.1",
"url-loader": "^1.1.2",
"webpack": "^4.28.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2"
}
}

View File

@@ -1,49 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: '#E0245E'
},
fixed: {
width: 6,
height: 6
}
};
export default Box;

View File

@@ -1,33 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { css } from 'glamor';
const Dot = ({ size, x, y, children, color }) => (
<div
className={css(styles.root, {
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
marginLeft: `${x}px`,
marginTop: `${y}px`
})}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};
export default Dot;

View File

@@ -1,2 +0,0 @@
import View from './View';
export default View;

View File

@@ -1,29 +0,0 @@
/* eslint-disable react/prop-types */
import { css } from 'glamor';
import React from 'react';
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} className={css(viewStyle, ...style)} />;
}
}
const viewStyle = {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
};
export default View;

View File

@@ -1,11 +0,0 @@
import Box from './Box';
import Dot from './Dot';
import Provider from './Provider';
import View from './View';
export default {
Box,
Dot,
Provider,
View
};

View File

@@ -1,50 +0,0 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
import View from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: '#E0245E'
},
fixed: {
width: 6,
height: 6
}
};
export default Radium(Box);

View File

@@ -1,36 +0,0 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
const Dot = ({ size, x, y, children, color }) => (
<div
style={[
styles.root,
{
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
marginLeft: `${x}px`,
marginTop: `${y}px`
}
]}
>
{children}
</div>
);
const styles = {
root: {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};
export default Radium(Dot);

View File

@@ -1,2 +0,0 @@
import View from './View';
export default View;

View File

@@ -1,31 +0,0 @@
/* eslint-disable react/prop-types */
import Radium from 'radium';
import React from 'react';
class View extends React.Component {
render() {
const { style, ...other } = this.props;
return <div {...other} style={[styles.root, style]} />;
}
}
const styles = {
root: {
alignItems: 'stretch',
borderWidth: 0,
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
margin: 0,
padding: 0,
position: 'relative',
// fix flexbox bugs
minHeight: 0,
minWidth: 0
}
};
export default Radium(View);

View File

@@ -1,11 +0,0 @@
import Box from './Box';
import Dot from './Dot';
import Provider from './Provider';
import View from './View';
export default {
Box,
Dot,
Provider,
View
};

View File

@@ -12,6 +12,9 @@ module.exports = {
path: path.resolve(appDirectory, 'dist'),
filename: 'bundle.js'
},
optimization: {
minimize: process.env.NODE_ENV === 'production'
},
module: {
rules: [
{

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "react-native-examples",
"version": "0.9.8",
"version": "0.9.11",
"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.8"
"react-native-web": "0.9.11"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.9.8",
"babel-plugin-react-native-web": "0.9.11",
"babel-plugin-transform-runtime": "^6.23.0",
"file-loader": "^1.1.11",
"webpack": "^4.8.1",

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-web",
"version": "0.9.8",
"version": "0.9.11",
"description": "React Native for Web",
"module": "dist/index.js",
"main": "dist/cjs/index.js",
@@ -15,14 +15,14 @@
"dependencies": {
"array-find-index": "^1.0.2",
"create-react-class": "^15.6.2",
"debounce": "^1.1.0",
"deep-assign": "^2.0.0",
"fbjs": "^0.8.16",
"debounce": "^1.2.0",
"deep-assign": "^3.0.0",
"fbjs": "^1.0.0",
"hyphenate-style-name": "^1.0.2",
"inline-style-prefixer": "^4.0.2",
"inline-style-prefixer": "^5.0.3",
"normalize-css-color": "^1.0.2",
"prop-types": "^15.6.0",
"react-timer-mixin": "^0.13.3"
"react-timer-mixin": "^0.13.4"
},
"peerDependencies": {
"react": ">=16.5.1",

View File

@@ -1,24 +1,29 @@
/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/
import AnimatedImplementation from '../../vendor/react-native/Animated/AnimatedImplementation';
import FlatList from '../FlatList';
import Image from '../Image';
import SectionList from '../SectionList';
import ScrollView from '../ScrollView';
import Text from '../Text';
import View from '../View';
const Animated = {
...AnimatedImplementation,
FlatList: AnimatedImplementation.createAnimatedComponent(FlatList),
Image: AnimatedImplementation.createAnimatedComponent(Image),
ScrollView: AnimatedImplementation.createAnimatedComponent(ScrollView),
View: AnimatedImplementation.createAnimatedComponent(View),
Text: AnimatedImplementation.createAnimatedComponent(Text)
SectionList: AnimatedImplementation.createAnimatedComponent(SectionList),
Text: AnimatedImplementation.createAnimatedComponent(Text),
View: AnimatedImplementation.createAnimatedComponent(View)
};
export default Animated;

View File

@@ -12,6 +12,8 @@ exports[`components/Image prop "resizeMode" value "cover" 1`] = `"cover"`;
exports[`components/Image prop "resizeMode" value "none" 1`] = `"auto"`;
exports[`components/Image prop "resizeMode" value "repeat" 1`] = `"auto"`;
exports[`components/Image prop "resizeMode" value "stretch" 1`] = `"100% 100%"`;
exports[`components/Image prop "resizeMode" value "undefined" 1`] = `"cover"`;

View File

@@ -141,13 +141,7 @@ describe('components/Image', () => {
});
describe('prop "resizeMode"', () => {
[
Image.resizeMode.contain,
Image.resizeMode.cover,
Image.resizeMode.none,
Image.resizeMode.stretch,
undefined
].forEach(resizeMode => {
['contain', 'cover', 'none', 'repeat', 'stretch', undefined].forEach(resizeMode => {
test(`value "${resizeMode}"`, () => {
const component = shallow(<Image resizeMode={resizeMode} />);
expect(findImageSurfaceStyle(component).backgroundSize).toMatchSnapshot();
@@ -214,7 +208,7 @@ describe('components/Image', () => {
describe('prop "style"', () => {
test('supports "resizeMode" property', () => {
const component = shallow(<Image style={{ resizeMode: Image.resizeMode.contain }} />);
const component = shallow(<Image style={{ resizeMode: 'contain' }} />);
expect(findImageSurfaceStyle(component).backgroundSize).toMatchSnapshot();
});

View File

@@ -132,8 +132,6 @@ class Image extends Component<*, State> {
return ImageLoader.prefetch(uri);
}
static resizeMode = ImageResizeMode;
_filterId = 0;
_imageRef = null;
_imageRequestId = null;

View File

@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/ScrollView "pagingEnabled" prop 1`] = `undefined`;
exports[`components/ScrollView "pagingEnabled" prop 2`] = `"y mandatory"`;
exports[`components/ScrollView "pagingEnabled" prop 3`] = `"start"`;

View File

@@ -2,7 +2,9 @@
import React from 'react';
import ScrollView from '..';
import { mount } from 'enzyme';
import StyleSheet from '../../StyleSheet';
import View from '../../View';
import { mount, shallow } from 'enzyme';
describe('components/ScrollView', () => {
test('instance method setNativeProps', () => {
@@ -11,4 +13,32 @@ describe('components/ScrollView', () => {
instance.setNativeProps();
}).not.toThrow();
});
test('"children" prop', () => {
const component = shallow(
<ScrollView>
<View testID="child" />
</ScrollView>
);
expect(component.find({ testID: 'child' }).length).toBe(1);
component.setProps({ stickyHeaderIndices: [4] });
expect(component.find({ testID: 'child' }).length).toBe(1);
component.setProps({ pagingEnabled: true });
expect(component.find({ testID: 'child' }).length).toBe(1);
});
test('"pagingEnabled" prop', () => {
const getStyleProp = (component, prop) => StyleSheet.flatten(component.prop('style'))[prop];
// false
const component = shallow(<ScrollView children={'Child'} />);
expect(getStyleProp(component, 'scrollSnapType')).toMatchSnapshot();
// true
component.setProps({ pagingEnabled: true });
expect(getStyleProp(component, 'scrollSnapType')).toMatchSnapshot();
expect(getStyleProp(component.children().childAt(0), 'scrollSnapAlign')).toMatchSnapshot();
});
});

View File

@@ -5,7 +5,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
* @flow
*/
import createReactClass from 'create-react-class';
@@ -137,10 +137,10 @@ const ScrollView = createReactClass({
onContentSizeChange,
refreshControl,
stickyHeaderIndices,
pagingEnabled,
/* eslint-disable */
keyboardDismissMode,
onScroll,
pagingEnabled,
/* eslint-enable */
...other
} = this.props;
@@ -164,11 +164,22 @@ const ScrollView = createReactClass({
};
}
const hasStickyHeaderIndices = !horizontal && Array.isArray(stickyHeaderIndices);
const children =
!horizontal && Array.isArray(stickyHeaderIndices)
hasStickyHeaderIndices || pagingEnabled
? React.Children.map(this.props.children, (child, i) => {
if (child && stickyHeaderIndices.indexOf(i) > -1) {
return <View style={styles.stickyHeader}>{child}</View>;
const isSticky = hasStickyHeaderIndices && stickyHeaderIndices.indexOf(i) > -1;
if (child != null && (isSticky || pagingEnabled)) {
return (
<View
style={StyleSheet.compose(
isSticky && styles.stickyHeader,
pagingEnabled && styles.pagingEnabledChild
)}
>
{child}
</View>
);
} else {
return child;
}
@@ -181,15 +192,21 @@ const ScrollView = createReactClass({
children={children}
collapsable={false}
ref={this._setInnerViewRef}
style={[horizontal && styles.contentContainerHorizontal, contentContainerStyle]}
style={StyleSheet.compose(
horizontal && styles.contentContainerHorizontal,
contentContainerStyle
)}
/>
);
const baseStyle = horizontal ? styles.baseHorizontal : styles.baseVertical;
const pagingEnabledStyle = horizontal
? styles.pagingEnabledHorizontal
: styles.pagingEnabledVertical;
const props = {
...other,
style: [baseStyle, this.props.style],
style: [baseStyle, pagingEnabled && pagingEnabledStyle, this.props.style],
onTouchStart: this.scrollResponderHandleTouchStart,
onTouchMove: this.scrollResponderHandleTouchMove,
onTouchEnd: this.scrollResponderHandleTouchEnd,
@@ -223,7 +240,7 @@ const ScrollView = createReactClass({
}
return (
<ScrollViewClass {...props} ref={this._setScrollViewRef} style={props.style}>
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
{contentContainer}
</ScrollViewClass>
);
@@ -294,6 +311,15 @@ const styles = StyleSheet.create({
position: 'sticky',
top: 0,
zIndex: 10
},
pagingEnabledHorizontal: {
scrollSnapType: 'x mandatory'
},
pagingEnabledVertical: {
scrollSnapType: 'y mandatory'
},
pagingEnabledChild: {
scrollSnapAlign: 'start'
}
});

View File

@@ -84,15 +84,19 @@ export default class ReactNativeStyleResolver {
// otherwise fallback to resolving
const flatArray = flattenArray(style);
let isArrayOfNumbers = true;
let cacheKey = '';
for (let i = 0; i < flatArray.length; i++) {
const id = flatArray[i];
if (typeof id !== 'number') {
isArrayOfNumbers = false;
} else {
if (isArrayOfNumbers) {
cacheKey += (id + '-');
}
this._injectRegisteredStyle(id);
}
}
const key = isArrayOfNumbers ? createCacheKey(flatArray.join('-')) : null;
const key = isArrayOfNumbers ? createCacheKey(cacheKey) : null;
return this._resolveStyleIfNeeded(flatArray, key);
}

View File

@@ -55,7 +55,15 @@ export default class WebStyleSheet {
// doesn't include styles injected via 'insertRule')
if (this._textContent.indexOf(rule) === -1 && this._sheet) {
const pos = position || this._sheet.cssRules.length;
this._sheet.insertRule(rule, pos);
try {
this._sheet.insertRule(rule, pos);
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
`Failed to inject CSS: "${rule}". The browser may have rejecting unrecognized vendor prefixes. (This should have no user-facing impact.)`
);
}
}
}
}
}

View File

@@ -2,10 +2,10 @@
exports[`StyleSheet/createAtomicRules transforms custom "animationName" declaration 1`] = `
Array [
"@media all {@-webkit-keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}}",
"@media all {@keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}}",
"@media all {@-webkit-keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}}",
"@media all {@keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}}",
"@-webkit-keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}",
"@keyframes rn-anim-2k74q5{0%{top:0px}50%{top:5px}100%{top:10px}}",
"@-webkit-keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}",
"@keyframes rn-anim-zc91cv{from{left:0px}to{left:10px}}",
".test{-webkit-animation-name:rn-anim-2k74q5,rn-anim-zc91cv;animation-name:rn-anim-2k74q5,rn-anim-zc91cv}",
]
`;

View File

@@ -284,7 +284,10 @@ describe('StyleSheet/createReactDOMStyle', () => {
});
test('textShadowColor and textShadowRadius only', () => {
expect(createReactDOMStyle({ textShadowColor: 'red', textShadowRadius: 5 })).toEqual({});
expect(createReactDOMStyle({ textShadowColor: 'red', textShadowRadius: 0 })).toEqual({});
expect(createReactDOMStyle({ textShadowColor: 'red', textShadowRadius: 5 })).toEqual({
textShadow: '0px 0px 5px rgba(255,0,0,1.00)'
});
});
test('textShadowColor, textShadowOffset, textShadowRadius', () => {

View File

@@ -29,7 +29,7 @@ const makeSteps = keyframes =>
const createKeyframesRules = (keyframes: Object): Array<String> => {
const identifier = createIdentifier(keyframes);
const rules = prefixes.map(prefix => {
return `@media all {@${prefix}keyframes ${identifier}{${makeSteps(keyframes)}}}`;
return `@${prefix}keyframes ${identifier}{${makeSteps(keyframes)}}`;
});
return { identifier, rules };
};

View File

@@ -117,12 +117,13 @@ const resolveTextDecoration = (resolvedStyle, style) => {
const resolveTextShadow = (resolvedStyle, style) => {
const { textShadowColor, textShadowOffset, textShadowRadius } = style;
const { height, width } = textShadowOffset || defaultOffset;
const radius = textShadowRadius || 0;
const offsetX = normalizeValue(null, width);
const offsetY = normalizeValue(null, height);
const blurRadius = normalizeValue(null, textShadowRadius || 0);
const blurRadius = normalizeValue(null, radius);
const color = normalizeColor(textShadowColor);
if (color && (height !== 0 || width !== 0)) {
if (color && (height !== 0 || width !== 0 || radius !== 0)) {
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
}
};

View File

@@ -1,11 +1,9 @@
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import StyleSheet from './StyleSheet';
// allow component styles to be editable in React Dev Tools
if (process.env.NODE_ENV !== 'production') {
if (canUseDOM && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.resolveRNStyle = StyleSheet.flatten;
}
// allow original component styles to be inspected in React Dev Tools
if (canUseDOM && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.resolveRNStyle = StyleSheet.flatten;
}
export default StyleSheet;

View File

@@ -21,10 +21,12 @@
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import hash from '../../vendor/hash';
const focusVisibleClass =
'rn-' + (process.env.NODE_ENV !== 'production' ? 'focusVisible-' : '') + hash('focus-visible');
const focusVisibleAttributeName =
'data-rn-' +
(process.env.NODE_ENV !== 'production' ? 'focusvisible-' : '') +
hash('focusvisible');
const rule = `:focus:not(.${focusVisibleClass}) { outline: none; }`;
const rule = `:focus:not([${focusVisibleAttributeName}]){outline: none;}`;
const modality = styleElement => {
if (!canUseDOM) {
@@ -71,7 +73,7 @@ const modality = styleElement => {
/**
* Computes whether the given element should automatically trigger the
* `focus-visible` class being added, i.e. whether it should always match
* `focus-visible` attribute being added, i.e. whether it should always match
* `:focus-visible` when focused.
*/
function focusTriggersKeyboardModality(el) {
@@ -95,22 +97,32 @@ const modality = styleElement => {
}
/**
* Add the `focus-visible` class to the given element if it was not added by
* Add the `focus-visible` attribute to the given element if it was not added by
* the author.
*/
function addFocusVisibleClass(el) {
if (el.classList.contains(focusVisibleClass)) {
function addFocusVisibleAttribute(el) {
if (el.hasAttribute(focusVisibleAttributeName)) {
return;
}
el.classList.add(focusVisibleClass);
el.setAttribute(focusVisibleAttributeName, true);
}
/**
* Remove the `focus-visible` class from the given element if it was not
* Remove the `focus-visible` attribute from the given element if it was not
* originally added by the author.
*/
function removeFocusVisibleClass(el) {
el.classList.remove(focusVisibleClass);
function removeFocusVisibleAttribute(el) {
el.removeAttribute(focusVisibleAttributeName);
}
/**
* Remove the `focus-visible` attribute from all elements in the document.
*/
function removeAllFocusVisibleAttributes() {
const list = document.querySelectorAll(`[${focusVisibleAttributeName}]`);
for (let i = 0; i < list.length; i += 1) {
removeFocusVisibleAttribute(list[i]);
}
}
/**
@@ -124,7 +136,7 @@ const modality = styleElement => {
}
if (isValidFocusTarget(document.activeElement)) {
addFocusVisibleClass(document.activeElement);
addFocusVisibleAttribute(document.activeElement);
}
hadKeyboardEvent = true;
@@ -136,13 +148,20 @@ const modality = styleElement => {
* 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.
* It also avoids the situation where a user presses on an element within a
* previously keyboard-focused element (i.e., `e.target` is not the previously
* focused element, but one of its descendants) and we need to remove the
* focus ring because a `blur` event doesn't occur.
*/
function onPointerDown(e) {
if (hadKeyboardEvent === true) {
removeAllFocusVisibleAttributes();
}
hadKeyboardEvent = false;
}
/**
* On `focus`, add the `focus-visible` class to the target if:
* On `focus`, add the `focus-visible` attribute 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)
@@ -154,19 +173,19 @@ const modality = styleElement => {
}
if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
addFocusVisibleClass(e.target);
addFocusVisibleAttribute(e.target);
}
}
/**
* On `blur`, remove the `focus-visible` class from the target.
* On `blur`, remove the `focus-visible` attribute from the target.
*/
function onBlur(e) {
if (!isValidFocusTarget(e.target)) {
return;
}
if (e.target.classList.contains(focusVisibleClass)) {
if (e.target.hasAttribute(focusVisibleAttributeName)) {
// 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
@@ -177,20 +196,20 @@ const modality = styleElement => {
hadFocusVisibleRecently = false;
window.clearTimeout(hadFocusVisibleRecentlyTimeout);
}, 100);
removeFocusVisibleClass(e.target);
removeFocusVisibleAttribute(e.target);
}
}
/**
* If the user changes tabs, keep track of whether or not the previously
* focused element had .focus-visible.
* focused element had the focus-visible attribute.
*/
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.
// re-apply the attribute when the user switches back to the tab.
if (hadFocusVisibleRecently) {
hadKeyboardEvent = true;
}

View File

@@ -180,6 +180,25 @@ describe('components/TextInput', () => {
});
describe('prop "onKeyPress"', () => {
test('arrow key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
input.simulate('keyPress', { which: 37 });
expect(onKeyPress).toHaveBeenCalledTimes(1);
expect(onKeyPress).toBeCalledWith(
expect.objectContaining({
nativeEvent: {
altKey: undefined,
ctrlKey: undefined,
key: 'ArrowLeft',
metaKey: undefined,
shiftKey: undefined,
target: expect.anything()
}
})
);
});
test('backspace key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
@@ -199,25 +218,6 @@ describe('components/TextInput', () => {
);
});
test('tab key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
input.simulate('keyDown', { which: 9 });
expect(onKeyPress).toHaveBeenCalledTimes(1);
expect(onKeyPress).toBeCalledWith(
expect.objectContaining({
nativeEvent: {
altKey: undefined,
ctrlKey: undefined,
key: 'Tab',
metaKey: undefined,
shiftKey: undefined,
target: expect.anything()
}
})
);
});
test('enter key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
@@ -237,6 +237,25 @@ describe('components/TextInput', () => {
);
});
test('escape key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
input.simulate('keyPress', { which: 27 });
expect(onKeyPress).toHaveBeenCalledTimes(1);
expect(onKeyPress).toBeCalledWith(
expect.objectContaining({
nativeEvent: {
altKey: undefined,
ctrlKey: undefined,
key: 'Escape',
metaKey: undefined,
shiftKey: undefined,
target: expect.anything()
}
})
);
});
test('space key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
@@ -256,17 +275,17 @@ describe('components/TextInput', () => {
);
});
test('arrow key', () => {
test('tab key', () => {
const onKeyPress = jest.fn();
const input = findNativeInput(mount(<TextInput onKeyPress={onKeyPress} />));
input.simulate('keyPress', { which: 37 });
input.simulate('keyDown', { which: 9 });
expect(onKeyPress).toHaveBeenCalledTimes(1);
expect(onKeyPress).toBeCalledWith(
expect.objectContaining({
nativeEvent: {
altKey: undefined,
ctrlKey: undefined,
key: 'ArrowLeft',
key: 'Tab',
metaKey: undefined,
shiftKey: undefined,
target: expect.anything()

View File

@@ -317,11 +317,13 @@ class TextInput extends Component<*> {
// Prevent key events bubbling (see #612)
e.stopPropagation();
// Backspace, Tab, Cmd+Enter, and Arrow keys only fire 'keydown' DOM events
// Backspace, Escape, Tab, Cmd+Enter, and Arrow keys only fire 'keydown'
// DOM events
if (
e.which === 8 ||
e.which === 9 ||
(e.which === 13 && e.metaKey) ||
e.which === 27 ||
e.which === 37 ||
e.which === 38 ||
e.which === 39 ||
@@ -348,6 +350,9 @@ class TextInput extends Component<*> {
case 13:
keyValue = 'Enter';
break;
case 27:
keyValue = 'Escape';
break;
case 32:
keyValue = ' ';
break;

View File

@@ -51,6 +51,8 @@ const ViewStylePropTypes = {
overscrollBehavior: overscrollBehaviorType,
overscrollBehaviorX: overscrollBehaviorType,
overscrollBehaviorY: overscrollBehaviorType,
scrollSnapAlign: string,
scrollSnapType: string,
WebkitMaskImage: string,
WebkitOverflowScrolling: oneOf(['auto', 'touch'])
};

View File

@@ -375,9 +375,14 @@ const ScrollResponderMixin = {
({ x, y, animated } = x || emptyObject);
}
const node = this.scrollResponderGetScrollableNode();
UIManager.updateView(node, { style: { scrollBehavior: !animated ? 'auto' : 'smooth' } }, this);
node.scrollLeft = x || 0;
node.scrollTop = y || 0;
const left = x || 0;
const top = y || 0;
if (typeof node.scroll === 'function') {
node.scroll({ top, left, behavior: !animated ? 'auto' : 'smooth' });
} else {
node.scrollLeft = left;
node.scrollTop = top;
}
},
/**

View File

@@ -23,6 +23,8 @@ const TransformPropTypes = {
shape({ scale: number }),
shape({ scaleX: number }),
shape({ scaleY: number }),
shape({ scaleZ: number }),
shape({ scale3d: string }),
shape({ skewX: string }),
shape({ skewY: string }),
shape({ translateX: numberOrString }),

View File

@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @noflow
* @flow
* @format
*/
'use strict';
@@ -158,6 +158,7 @@ class AnimatedEvent {
};
}
// $FlowFixMe
_callListeners(...args) {
this._listeners.forEach(listener => listener(...args));
}

View File

@@ -19,6 +19,7 @@ import AnimatedModulo from './nodes/AnimatedModulo';
import AnimatedMultiplication from './nodes/AnimatedMultiplication';
import AnimatedNode from './nodes/AnimatedNode';
import AnimatedProps from './nodes/AnimatedProps';
import AnimatedSubtraction from './nodes/AnimatedSubtraction';
import AnimatedTracking from './nodes/AnimatedTracking';
import AnimatedValue from './nodes/AnimatedValue';
import AnimatedValueXY from './nodes/AnimatedValueXY';
@@ -49,6 +50,13 @@ const add = function(
return new AnimatedAddition(a, b);
};
const subtract = function(
a: AnimatedNode | number,
b: AnimatedNode | number,
): AnimatedSubtraction {
return new AnimatedSubtraction(a, b);
};
const divide = function(
a: AnimatedNode | number,
b: AnimatedNode | number,
@@ -516,7 +524,7 @@ const AnimatedImplementation = {
/**
* 2D value class for driving 2D animations, such as pan gestures.
*
* See https://facebook.github.io/react-native/releases/next/docs/animatedvaluexy.html
* See https://facebook.github.io/react-native/docs/animatedvaluexy.html
*/
ValueXY: AnimatedValueXY,
/**
@@ -563,6 +571,14 @@ const AnimatedImplementation = {
*/
add,
/**
* Creates a new Animated value composed by subtracting the second Animated
* value from the first Animated value.
*
* See http://facebook.github.io/react-native/docs/animated.html#subtract
*/
subtract,
/**
* Creates a new Animated value composed by dividing the first Animated value
* by the second Animated value.

View File

@@ -4,8 +4,10 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
import _bezier from './bezier';
@@ -131,7 +133,7 @@ class Easing {
* http://easings.net/#easeInSine
*/
static sin(t: number) {
return 1 - Math.cos(t * Math.PI / 2);
return 1 - Math.cos((t * Math.PI) / 2);
}
/**
@@ -164,7 +166,7 @@ class Easing {
*/
static elastic(bounciness: number = 1): (t: number) => number {
const p = bounciness * Math.PI;
return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p);
return t => 1 - Math.pow(Math.cos((t * Math.PI) / 2), 3) * Math.cos(t * p);
}
/**
@@ -179,7 +181,7 @@ class Easing {
if (s === undefined) {
s = 1.70158;
}
return (t) => t * t * ((s + 1) * t - s);
return t => t * t * ((s + 1) * t - s);
}
/**
@@ -217,7 +219,7 @@ class Easing {
x1: number,
y1: number,
x2: number,
y2: number
y2: number,
): (t: number) => number {
return _bezier(x1, y1, x2, y2);
}
@@ -225,19 +227,15 @@ class Easing {
/**
* Runs an easing function forwards.
*/
static in(
easing: (t: number) => number,
): (t: number) => number {
static in(easing: (t: number) => number): (t: number) => number {
return easing;
}
/**
* Runs an easing function backwards.
*/
static out(
easing: (t: number) => number,
): (t: number) => number {
return (t) => 1 - easing(1 - t);
static out(easing: (t: number) => number): (t: number) => number {
return t => 1 - easing(1 - t);
}
/**
@@ -245,10 +243,8 @@ class Easing {
* forwards for half of the duration, then backwards for the rest of the
* duration.
*/
static inOut(
easing: (t: number) => number,
): (t: number) => number {
return (t) => {
static inOut(easing: (t: number) => number): (t: number) => number {
return t => {
if (t < 0.5) {
return easing(t * 2) / 2;
}

View File

@@ -145,6 +145,15 @@ const API = {
const STYLES_WHITELIST = {
opacity: true,
transform: true,
borderRadius: true,
borderBottomEndRadius: true,
borderBottomLeftRadius: true,
borderBottomRightRadius: true,
borderBottomStartRadius: true,
borderTopEndRadius: true,
borderTopLeftRadius: true,
borderTopRightRadius: true,
borderTopStartRadius: true,
/* ios styles */
shadowOpacity: true,
shadowRadius: true,
@@ -200,7 +209,7 @@ function validateTransform(configs: Array<Object>): void {
}
function validateStyles(styles: Object): void {
for (var key in styles) {
for (const key in styles) {
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
throw new Error(
`Style property '${key}' is not supported by native animated module`,
@@ -210,7 +219,7 @@ function validateStyles(styles: Object): void {
}
function validateInterpolation(config: Object): void {
for (var key in config) {
for (const key in config) {
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
throw new Error(
`Interpolation property '${key}' is not supported by native animated module`,

View File

@@ -1 +0,0 @@
facebook/react-native@71006f74cdafdae7212c8a10603fb972c6ee338c

View File

@@ -4,7 +4,8 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @flow strict
*/
'use strict';
@@ -41,7 +42,7 @@ function fromBouncinessAndSpeed(
}
function projectNormal(n, start, end) {
return start + (n * (end - start));
return start + n * (end - start);
}
function linearInterpolation(t, start, end) {
@@ -53,18 +54,20 @@ function fromBouncinessAndSpeed(
}
function b3Friction1(x) {
return (0.0007 * Math.pow(x, 3)) -
(0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28;
return 0.0007 * Math.pow(x, 3) - 0.031 * Math.pow(x, 2) + 0.64 * x + 1.28;
}
function b3Friction2(x) {
return (0.000044 * Math.pow(x, 3)) -
(0.006 * Math.pow(x, 2)) + 0.36 * x + 2;
return 0.000044 * Math.pow(x, 3) - 0.006 * Math.pow(x, 2) + 0.36 * x + 2;
}
function b3Friction3(x) {
return (0.00000045 * Math.pow(x, 3)) -
(0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84;
return (
0.00000045 * Math.pow(x, 3) -
0.000332 * Math.pow(x, 2) +
0.1078 * x +
5.84
);
}
function b3Nobounce(tension) {
@@ -77,14 +80,14 @@ function fromBouncinessAndSpeed(
}
}
var b = normalize(bounciness / 1.7, 0, 20);
let b = normalize(bounciness / 1.7, 0, 20);
b = projectNormal(b, 0, 0.8);
var s = normalize(speed / 1.7, 0, 20);
var bouncyTension = projectNormal(s, 0.5, 200);
var bouncyFriction = quadraticOutInterpolation(
const s = normalize(speed / 1.7, 0, 20);
const bouncyTension = projectNormal(s, 0.5, 200);
const bouncyFriction = quadraticOutInterpolation(
b,
b3Nobounce(bouncyTension),
0.01
0.01,
);
return {

View File

@@ -266,7 +266,7 @@ class SpringAnimation extends Animation {
position =
this._toValue -
envelope *
((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) +
(((v0 + zeta * omega0 * x0) / omega1) * Math.sin(omega1 * t) +
x0 * Math.cos(omega1 * t));
// This looks crazy -- it's actually just the derivative of the
// oscillation function
@@ -274,7 +274,7 @@ class SpringAnimation extends Animation {
zeta *
omega0 *
envelope *
(Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 +
((Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0)) / omega1 +
x0 * Math.cos(omega1 * t)) -
envelope *
(Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) -

View File

@@ -2,106 +2,144 @@
* BezierEasing - use bezier curve for transition easing function
* https://github.com/gre/bezier-easing
*
* @flow
* @format
* @copyright 2014-2015 Gaëtan Renaudeau. MIT License.
* @noflow
*/
'use strict';
// These values are established by empiricism with tests (tradeoff: performance VS precision)
var NEWTON_ITERATIONS = 4;
var NEWTON_MIN_SLOPE = 0.001;
var SUBDIVISION_PRECISION = 0.0000001;
var SUBDIVISION_MAX_ITERATIONS = 10;
// These values are established by empiricism with tests (tradeoff: performance VS precision)
const NEWTON_ITERATIONS = 4;
const NEWTON_MIN_SLOPE = 0.001;
const SUBDIVISION_PRECISION = 0.0000001;
const SUBDIVISION_MAX_ITERATIONS = 10;
var kSplineTableSize = 11;
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
const kSplineTableSize = 11;
const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
var float32ArraySupported = typeof Float32Array === 'function';
const float32ArraySupported = typeof Float32Array === 'function';
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C (aA1) { return 3.0 * aA1; }
function A(aA1, aA2) {
return 1.0 - 3.0 * aA2 + 3.0 * aA1;
}
function B(aA1, aA2) {
return 3.0 * aA2 - 6.0 * aA1;
}
function C(aA1) {
return 3.0 * aA1;
}
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function calcBezier(aT, aA1, aA2) {
return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
}
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function getSlope(aT, aA1, aA2) {
return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
}
function binarySubdivide (aX, aA, aB, mX1, mX2) {
var currentX, currentT, i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
} else {
aA = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}
function binarySubdivide(aX, aA, aB, mX1, mX2) {
let currentX,
currentT,
i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
} else {
aA = currentT;
}
} while (
Math.abs(currentX) > SUBDIVISION_PRECISION &&
++i < SUBDIVISION_MAX_ITERATIONS
);
return currentT;
}
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
var currentSlope = getSlope(aGuessT, mX1, mX2);
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
const currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
}
module.exports = function bezier (mX1, mY1, mX2, mY2) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { // eslint-disable-line yoda
throw new Error('bezier x values must be in [0, 1] range');
}
function bezier(
mX1: number,
mY1: number,
mX2: number,
mY2: number,
) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
throw new Error('bezier x values must be in [0, 1] range');
}
// Precompute samples table
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (var i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
}
// Precompute samples table
const sampleValues = float32ArraySupported
? new Float32Array(kSplineTableSize)
: new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
}
function getTForX (aX) {
var intervalStart = 0.0;
var currentSample = 1;
var lastSample = kSplineTableSize - 1;
function getTForX(aX) {
let intervalStart = 0.0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
for (
;
currentSample !== lastSample && sampleValues[currentSample] <= aX;
++currentSample
) {
intervalStart += kSampleStepSize;
}
--currentSample;
// Interpolate to provide an initial guess for t
var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
var guessForT = intervalStart + dist * kSampleStepSize;
// Interpolate to provide an initial guess for t
const dist =
(aX - sampleValues[currentSample]) /
(sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
var initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
} else if (initialSlope === 0.0) {
return guessForT;
} else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
const initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
} else if (initialSlope === 0.0) {
return guessForT;
} else {
return binarySubdivide(
aX,
intervalStart,
intervalStart + kSampleStepSize,
mX1,
mX2,
);
}
}
return function BezierEasing (x) {
if (mX1 === mY1 && mX2 === mY2) {
return x; // linear
}
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if (x === 0) {
return 0;
}
if (x === 1) {
return 1;
}
return calcBezier(getTForX(x), mY1, mY2);
};
};
return function BezierEasing(x: number): number {
if (mX1 === mY1 && mX2 === mY2) {
return x; // linear
}
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if (x === 0) {
return 0;
}
if (x === 1) {
return 1;
}
return calcBezier(getTForX(x), mY1, mY2);
};
};
export default bezier;

View File

@@ -17,7 +17,7 @@ import invariant from 'fbjs/lib/invariant';
function createAnimatedComponent(Component: any): any {
invariant(
typeof Component === 'string' ||
typeof Component !== 'function' ||
(Component.prototype && Component.prototype.isReactComponent),
'`createAnimatedComponent` does not support stateless functional components; ' +
'use a class component instead.',

View File

@@ -285,7 +285,7 @@ function checkValidInputRange(arr: Array<number>) {
* mean this implicit string conversion, you can do something like
* String(myThing)
*/
'inputRange must be monotonically increasing ' + arr,
'inputRange must be monotonically non-decreasing ' + arr,
);
}
}
@@ -349,13 +349,16 @@ class AnimatedInterpolation extends AnimatedWithChildren {
__transformDataType(range: Array<any>) {
// Change the string array type to number array
// So we can reuse the same logic in iOS and Android platform
/* $FlowFixMe(>=0.70.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.70 was deployed. To see the error delete this
* comment and run Flow. */
return range.map(function(value) {
if (typeof value !== 'string') {
return value;
}
if (/deg$/.test(value)) {
const degrees = parseFloat(value) || 0;
const radians = degrees * Math.PI / 180.0;
const radians = (degrees * Math.PI) / 180.0;
return radians;
} else {
// Assume radians

View File

@@ -32,7 +32,7 @@ class AnimatedModulo extends AnimatedWithChildren {
__getValue(): number {
return (
(this._a.__getValue() % this._modulus + this._modulus) % this._modulus
((this._a.__getValue() % this._modulus) + this._modulus) % this._modulus
);
}

View File

@@ -96,13 +96,13 @@ class AnimatedStyle extends AnimatedWithChildren {
}
__makeNative() {
super.__makeNative();
for (const key in this._style) {
const value = this._style[key];
if (value instanceof AnimatedNode) {
value.__makeNative();
}
}
super.__makeNative();
}
__getNativeConfig(): Object {

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import AnimatedInterpolation from './AnimatedInterpolation';
import AnimatedNode from './AnimatedNode';
import AnimatedValue from './AnimatedValue';
import AnimatedWithChildren from './AnimatedWithChildren';
import type {InterpolationConfigType} from './AnimatedInterpolation';
class AnimatedSubtraction extends AnimatedWithChildren {
_a: AnimatedNode;
_b: AnimatedNode;
constructor(a: AnimatedNode | number, b: AnimatedNode | number) {
super();
this._a = typeof a === 'number' ? new AnimatedValue(a) : a;
this._b = typeof b === 'number' ? new AnimatedValue(b) : b;
}
__makeNative() {
this._a.__makeNative();
this._b.__makeNative();
super.__makeNative();
}
__getValue(): number {
return this._a.__getValue() - this._b.__getValue();
}
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
}
__detach(): void {
this._a.__removeChild(this);
this._b.__removeChild(this);
super.__detach();
}
__getNativeConfig(): any {
return {
type: 'subtraction',
input: [this._a.__getNativeTag(), this._b.__getNativeTag()],
};
}
}
export default AnimatedSubtraction;

View File

@@ -22,7 +22,6 @@ class AnimatedTransform extends AnimatedWithChildren {
}
__makeNative() {
super.__makeNative();
this._transforms.forEach(transform => {
for (const key in transform) {
const value = transform[key];
@@ -31,6 +30,7 @@ class AnimatedTransform extends AnimatedWithChildren {
}
}
});
super.__makeNative();
}
__getValue(): $ReadOnlyArray<Object> {

View File

@@ -50,6 +50,9 @@ let _uniqueId = 1;
function _flush(rootNode: AnimatedValue): void {
const animatedStyles = new Set();
function findAnimatedStyles(node) {
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
if (typeof node.update === 'function') {
animatedStyles.add(node);
} else {

View File

@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
* @format
*/
'use strict';

View File

@@ -9,5 +9,5 @@
module.exports = {
createInteractionHandle: function() {},
clearInteractionHandle: function() {}
clearInteractionHandle: function() {},
};

View File

@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @flow strict-local
*/
import InteractionManager from '../../../exports/InteractionManager';

View File

@@ -7,17 +7,20 @@
* @noflow
* @format
*/
'use strict';
import UnimplementedView from '../../../modules/UnimplementedView';
import React from 'react';
import StyleSheet from '../../../exports/StyleSheet';
import View from '../../../exports/View';
import VirtualizedList, { type Props as VirtualizedListProps } from '../VirtualizedList';
import invariant from 'fbjs/lib/invariant';
import type {
ViewabilityConfig,
ViewToken,
ViewabilityConfigCallbackPair
} from '../ViewabilityHelper';
import invariant from 'fbjs/lib/invariant';
export type SeparatorsObj = {
highlight: () => void,
@@ -81,11 +84,19 @@ type OptionalProps<ItemT> = {
* a rendered element.
*/
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListFooterComponent
*/
ListFooterComponentStyle?: any,
/**
* Rendered at the top of all the items. Can be a React Component Class, a render function, or
* a rendered element.
*/
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListHeaderComponent
*/
ListHeaderComponentStyle?: any,
/**
* Optional custom style for multi-item rows generated when numColumns > 1.
*/
@@ -340,6 +351,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
viewPosition?: number,
}) {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
this._listRef.scrollToIndex(params);
}
}
@@ -356,6 +368,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
viewPosition?: number,
}) {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
this._listRef.scrollToItem(params);
}
}
@@ -367,6 +380,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
*/
scrollToOffset(params: {animated?: ?boolean, offset: number}) {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
this._listRef.scrollToOffset(params);
}
}
@@ -378,6 +392,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
*/
recordInteraction() {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
this._listRef.recordInteraction();
}
}
@@ -389,6 +404,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
*/
flashScrollIndicators() {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
this._listRef.flashScrollIndicators();
}
}
@@ -398,51 +414,27 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
*/
getScrollResponder() {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
return this._listRef.getScrollResponder();
}
}
getScrollableNode() {
if (this._listRef) {
// $FlowFixMe Found when typing ListView
return this._listRef.getScrollableNode();
}
}
setNativeProps(props: Object) {
setNativeProps(props: {[string]: mixed}) {
if (this._listRef) {
this._listRef.setNativeProps(props);
}
}
UNSAFE_componentWillMount() {
this._checkProps(this.props);
}
UNSAFE_componentWillReceiveProps(nextProps: Props<ItemT>) {
invariant(
nextProps.numColumns === this.props.numColumns,
'Changing numColumns on the fly is not supported. Change the key prop on FlatList when ' +
'changing the number of columns to force a fresh render of the component.',
);
invariant(
nextProps.onViewableItemsChanged === this.props.onViewableItemsChanged,
'Changing onViewableItemsChanged on the fly is not supported',
);
invariant(
nextProps.viewabilityConfig === this.props.viewabilityConfig,
'Changing viewabilityConfig on the fly is not supported',
);
invariant(
nextProps.viewabilityConfigCallbackPairs ===
this.props.viewabilityConfigCallbackPairs,
'Changing viewabilityConfigCallbackPairs on the fly is not supported',
);
this._checkProps(nextProps);
}
constructor(props: Props<*>) {
constructor(props: Props<ItemT>) {
super(props);
this._checkProps(this.props);
if (this.props.viewabilityConfigCallbackPairs) {
this._virtualizedListPairs = this.props.viewabilityConfigCallbackPairs.map(
pair => ({
@@ -465,6 +457,29 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
}
}
componentDidUpdate(prevProps: Props<ItemT>) {
invariant(
prevProps.numColumns === this.props.numColumns,
'Changing numColumns on the fly is not supported. Change the key prop on FlatList when ' +
'changing the number of columns to force a fresh render of the component.',
);
invariant(
prevProps.onViewableItemsChanged === this.props.onViewableItemsChanged,
'Changing onViewableItemsChanged on the fly is not supported',
);
invariant(
prevProps.viewabilityConfig === this.props.viewabilityConfig,
'Changing viewabilityConfig on the fly is not supported',
);
invariant(
prevProps.viewabilityConfigCallbackPairs ===
this.props.viewabilityConfigCallbackPairs,
'Changing viewabilityConfigCallbackPairs on the fly is not supported',
);
this._checkProps(this.props);
}
_hasWarnedLegacy = false;
_listRef: null | VirtualizedList;
_virtualizedListPairs: Array<ViewabilityConfigCallbackPair> = [];
@@ -505,8 +520,9 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
// comparison.
if (!this._hasWarnedLegacy) {
console.warn(
'FlatList: Using legacyImplementation - some features not supported and performance ' +
'may suffer',
'FlatList: legacyImplementation is deprecated and will be removed in a ' +
'future release - some features not supported and performance may suffer. ' +
'Please migrate to the default implementation.',
);
this._hasWarnedLegacy = true;
}
@@ -524,7 +540,9 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
const ret = [];
for (let kk = 0; kk < numColumns; kk++) {
const item = data[index * numColumns + kk];
item && ret.push(item);
if (item != null) {
ret.push(item);
}
}
return ret;
} else {
@@ -601,7 +619,11 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
'Expected array of items with numColumns > 1',
);
return (
<View style={[{flexDirection: 'row'}, columnWrapperStyle]}>
<View
style={StyleSheet.compose(
styles.row,
columnWrapperStyle,
)}>
{item.map((it, kk) => {
const element = renderItem({
item: it,
@@ -648,4 +670,8 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
}
}
const styles = StyleSheet.create({
row: {flexDirection: 'row'},
});
export default FlatList;

View File

@@ -1,2 +1,2 @@
facebook/react-native@0.55.4
facebook/react-native@370bcffba748e895ad8afa825bfef40bff859c95
facebook/react-native@v0.57.5
facebook/react-native@44bfffb11666f2d1775dee5dd2890fc2255d2ab0

View File

@@ -282,6 +282,7 @@ class SectionList<SectionT: SectionBase<any>> extends React.PureComponent<
*/
recordInteraction() {
const listRef = this._wrapperListRef && this._wrapperListRef.getListRef();
// $FlowFixMe Found when typing ListView
listRef && listRef.recordInteraction();
}

View File

@@ -101,6 +101,7 @@ class SwipeableFlatList<ItemT> extends React.Component<Props<ItemT>, State> {
}}
onScroll={this._onScroll}
renderItem={this._renderItem}
extraData={this.state}
/>
);
}

View File

@@ -7,6 +7,8 @@
* @noflow
* @format
*/
'use strict';
import Batchinator from '../Batchinator';
import FillRateHelper from '../FillRateHelper';
import PropTypes from 'prop-types';
@@ -34,6 +36,8 @@ type DangerouslyImpreciseStyleProp = any;
type Item = any;
type ViewStyleProp = any;
export type renderItemType = (info: any) => ?React.Element<any>;
type ViewabilityHelperCallbackTuple = {
@@ -118,11 +122,19 @@ type OptionalProps = {
* a rendered element.
*/
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListFooterComponent
*/
ListFooterComponentStyle?: ViewStyleProp,
/**
* Rendered at the top of all the items. Can be a React Component Class, a render function, or
* a rendered element.
*/
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListHeaderComponent
*/
ListHeaderComponentStyle?: ViewStyleProp,
/**
* A unique identifier for this list. If there are multiple VirtualizedLists at the same level of
* nesting within another VirtualizedList, this key is necessary for virtualization to
@@ -166,6 +178,12 @@ type OptionalProps = {
* @platform android
*/
progressViewOffset?: number,
/**
* A custom refresh control element. When set, it overrides the default
* <RefreshControl> component built internally. The onRefresh and refreshing
* props are also ignored. Only works for vertical VirtualizedList.
*/
refreshControl?: ?React.Element<any>,
/**
* Set this true while waiting for new data from a refresh.
*/
@@ -205,6 +223,7 @@ type OptionalProps = {
export type Props = RequiredProps & OptionalProps;
let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';
type Frame = {
offset: number,
@@ -399,7 +418,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
if (this._scrollRef && this._scrollRef.getScrollableNode) {
return this._scrollRef.getScrollableNode();
} else {
return findNodeHandle(this._scrollRef);
return ReactNative.findNodeHandle(this._scrollRef);
}
}
@@ -410,7 +429,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
static defaultProps = {
disableVirtualization: process.env.NODE_ENV === 'test',
disableVirtualization: false,
horizontal: false,
initialNumToRender: 10,
keyExtractor: (item: Item, index: number) => {
@@ -418,6 +437,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
return item.key;
}
_usedIndexForKey = true;
if (item.type && item.type.displayName) {
_keylessItemComponentName = item.type.displayName;
}
return String(index);
},
maxToRenderPerBatch: 10,
@@ -740,12 +762,16 @@ class VirtualizedList extends React.PureComponent<Props, State> {
<VirtualizedCellWrapper
cellKey={this._getCellKey() + '-header'}
key="$header">
<View onLayout={this._onLayoutHeader} style={inversionStyle}>
{/*
Flow doesn't know this is a React.Element and not a React.Component
$FlowFixMe https://fburl.com/b9xmtm09
*/}
{element}
<View
onLayout={this._onLayoutHeader}
style={StyleSheet.compose(
inversionStyle,
this.props.ListHeaderComponentStyle,
)}>
{
// $FlowFixMe - Typing ReactNativeComponent revealed errors
element
}
</View>
</VirtualizedCellWrapper>,
);
@@ -753,6 +779,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
const itemCount = this.props.getItemCount(data);
if (itemCount > 0) {
_usedIndexForKey = false;
_keylessItemComponentName = '';
const spacerKey = !horizontal ? 'height' : 'width';
const lastInitialIndex = this.props.initialScrollIndex
? -1
@@ -776,8 +803,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
if (stickyIndicesFromProps.has(ii + stickyOffset)) {
const initBlock = this._getFrameMetricsApprox(lastInitialIndex);
const stickyBlock = this._getFrameMetricsApprox(ii);
const leadSpace =
stickyBlock.offset - (initBlock.offset + initBlock.length);
const leadSpace = stickyBlock.offset - initBlock.offset;
cells.push(
<View key="$sticky_lead" style={{[spacerKey]: leadSpace}} />,
);
@@ -822,6 +848,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
console.warn(
'VirtualizedList: missing keys for items, make sure to specify a key property on each ' +
'item or provide a custom keyExtractor.',
_keylessItemComponentName,
);
this._hasWarned.keys = true;
}
@@ -843,23 +870,25 @@ class VirtualizedList extends React.PureComponent<Props, State> {
);
}
} else if (ListEmptyComponent) {
const element = React.isValidElement(ListEmptyComponent) ? (
const element: React.Element<any> = ((React.isValidElement(
ListEmptyComponent,
) ? (
ListEmptyComponent
) : (
// $FlowFixMe
<ListEmptyComponent />
);
)): any);
cells.push(
<View
key="$empty"
onLayout={this._onLayoutEmpty}
style={inversionStyle}>
{/*
Flow doesn't know this is a React.Element and not a React.Component
$FlowFixMe https://fburl.com/b9xmtm09
*/}
{element}
</View>,
React.cloneElement(element, {
key: '$empty',
onLayout: event => {
this._onLayoutEmpty(event);
if (element.props.onLayout) {
element.props.onLayout(event);
}
},
style: [element.props.style, inversionStyle],
}),
);
}
if (ListFooterComponent) {
@@ -873,12 +902,16 @@ class VirtualizedList extends React.PureComponent<Props, State> {
<VirtualizedCellWrapper
cellKey={this._getCellKey() + '-footer'}
key="$footer">
<View onLayout={this._onLayoutFooter} style={inversionStyle}>
{/*
Flow doesn't know this is a React.Element and not a React.Component
$FlowFixMe https://fburl.com/b9xmtm09
*/}
{element}
<View
onLayout={this._onLayoutFooter}
style={StyleSheet.compose(
inversionStyle,
this.props.ListFooterComponentStyle,
)}>
{
// $FlowFixMe - Typing ReactNativeComponent revealed errors
element
}
</View>
</VirtualizedCellWrapper>,
);
@@ -892,12 +925,16 @@ class VirtualizedList extends React.PureComponent<Props, State> {
onScrollEndDrag: this._onScrollEndDrag,
onMomentumScrollEnd: this._onMomentumScrollEnd,
scrollEventThrottle: this.props.scrollEventThrottle, // TODO: Android support
invertStickyHeaders: this.props.invertStickyHeaders !== undefined
? this.props.invertStickyHeaders
: this.props.inverted,
invertStickyHeaders:
this.props.invertStickyHeaders !== undefined
? this.props.invertStickyHeaders
: this.props.inverted,
stickyHeaderIndices,
};
if (inversionStyle) {
/* $FlowFixMe(>=0.70.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.70 was deployed. To see the error delete
* this comment and run Flow. */
scrollProps.style = [inversionStyle, this.props.style];
}
@@ -908,6 +945,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
(this.props.renderScrollComponent || this._defaultRenderScrollComponent)(
scrollProps,
),
// $FlowFixMe Invalid prop usage
{
ref: this._captureScrollRef,
},
@@ -989,9 +1027,11 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
_defaultRenderScrollComponent = props => {
const onRefresh = props.onRefresh;
if (this._isNestedWithSameOrientation()) {
// $FlowFixMe - Typing ReactNativeComponent revealed errors
return <View {...props} />;
} else if (props.onRefresh) {
} else if (onRefresh) {
invariant(
typeof props.refreshing === 'boolean',
'`refreshing` prop must be set as a boolean in order to use `onRefresh`, but got `' +
@@ -999,21 +1039,24 @@ class VirtualizedList extends React.PureComponent<Props, State> {
'`',
);
return (
// $FlowFixMe Invalid prop usage
<ScrollView
{...props}
refreshControl={
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error when upgrading Flow's support for
* React. To see the error delete this comment and run Flow. */
<RefreshControl
refreshing={props.refreshing}
onRefresh={props.onRefresh}
progressViewOffset={props.progressViewOffset}
/>
props.refreshControl == null ? (
<RefreshControl
refreshing={props.refreshing}
onRefresh={onRefresh}
progressViewOffset={props.progressViewOffset}
/>
) : (
props.refreshControl
)
}
/>
);
} else {
// $FlowFixMe Invalid prop usage
return <ScrollView {...props} />;
}
};
@@ -1046,6 +1089,17 @@ class VirtualizedList extends React.PureComponent<Props, State> {
} else {
this._frames[cellKey].inLayout = true;
}
const childListKeys = this._cellKeysToChildListKeys.get(cellKey);
if (childListKeys) {
for (let childKey of childListKeys) {
const childList = this._nestedChildLists.get(childKey);
childList &&
childList.ref &&
childList.ref.measureLayoutRelativeToContainingList();
}
}
this._computeBlankness();
}
@@ -1056,36 +1110,48 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
};
_measureLayoutRelativeToContainingList(): void {
UIManager.measureLayout(
findNodeHandle(this),
findNodeHandle(
this.context.virtualizedList.getOutermostParentListRef(),
),
error => {
console.warn(
"VirtualizedList: Encountered an error while measuring a list's" +
' offset from its containing VirtualizedList.',
);
},
(x, y, width, height) => {
this._offsetFromParentVirtualizedList = this._selectOffset({x, y});
this._scrollMetrics.contentLength = this._selectLength({width, height});
measureLayoutRelativeToContainingList(): void {
// TODO (T35574538): findNodeHandle sometimes crashes with "Unable to find
// node on an unmounted component" during scrolling
try {
UIManager.measureLayout(
ReactNative.findNodeHandle(this),
ReactNative.findNodeHandle(
this.context.virtualizedList.getOutermostParentListRef(),
),
error => {
console.warn(
"VirtualizedList: Encountered an error while measuring a list's" +
' offset from its containing VirtualizedList.',
);
},
(x, y, width, height) => {
this._offsetFromParentVirtualizedList = this._selectOffset({x, y});
this._scrollMetrics.contentLength = this._selectLength({
width,
height,
});
const scrollMetrics = this._convertParentScrollMetrics(
this.context.virtualizedList.getScrollMetrics(),
);
this._scrollMetrics.visibleLength = scrollMetrics.visibleLength;
this._scrollMetrics.offset = scrollMetrics.offset;
},
);
const scrollMetrics = this._convertParentScrollMetrics(
this.context.virtualizedList.getScrollMetrics(),
);
this._scrollMetrics.visibleLength = scrollMetrics.visibleLength;
this._scrollMetrics.offset = scrollMetrics.offset;
},
);
} catch (error) {
console.warn(
'measureLayoutRelativeToContainingList threw an error',
error.stack,
);
}
}
_onLayout = (e: Object) => {
if (this._isNestedWithSameOrientation()) {
// Need to adjust our scroll metrics to be relative to our containing
// VirtualizedList before we can make claims about list item viewability
this._measureLayoutRelativeToContainingList();
this.measureLayoutRelativeToContainingList();
} else {
this._scrollMetrics.visibleLength = this._selectLength(
e.nativeEvent.layout,
@@ -1115,6 +1181,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
const itemCount = this.props.getItemCount(this.props.data);
for (let ii = 0; ii < itemCount; ii++) {
const frame = this._getFrameMetricsApprox(ii);
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
if (frame.inLayout) {
framesInLayout.push(frame);
}
@@ -1323,18 +1392,26 @@ class VirtualizedList extends React.PureComponent<Props, State> {
const {offset, visibleLength, velocity} = this._scrollMetrics;
const itemCount = this.props.getItemCount(this.props.data);
let hiPri = false;
if (first > 0 || last < itemCount - 1) {
const scrollingThreshold =
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.63 was deployed. To see the error delete
* this comment and run Flow. */
(this.props.onEndReachedThreshold * visibleLength) / 2;
// Mark as high priority if we're close to the start of the first item
// But only if there are items before the first rendered item
if (first > 0) {
const distTop = offset - this._getFrameMetricsApprox(first).offset;
hiPri =
hiPri || distTop < 0 || (velocity < -2 && distTop < scrollingThreshold);
}
// Mark as high priority if we're close to the end of the last item
// But only if there are items after the last rendered item
if (last < itemCount - 1) {
const distBottom =
this._getFrameMetricsApprox(last).offset - (offset + visibleLength);
const scrollingThreshold =
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.63 was deployed. To see the error delete
* this comment and run Flow. */
this.props.onEndReachedThreshold * visibleLength / 2;
hiPri =
Math.min(distTop, distBottom) < 0 ||
(velocity < -2 && distTop < scrollingThreshold) ||
hiPri ||
distBottom < 0 ||
(velocity > 2 && distBottom < scrollingThreshold);
}
// Only trigger high-priority updates if we've actually rendered cells,
@@ -1561,7 +1638,7 @@ class CellRenderer extends React.Component<
getItemLayout?: ?Function,
renderItem: renderItemType,
},
prevCellKey: ?string
prevCellKey: ?string,
},
$FlowFixMeState,
> {
@@ -1639,6 +1716,9 @@ class CellRenderer extends React.Component<
separators: this._separators,
});
const onLayout =
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.68 was deployed. To see the error delete this
* comment and run Flow. */
getItemLayout && !parentProps.debug && !fillRateHelper.enabled()
? undefined
: this.props.onLayout;
@@ -1702,15 +1782,6 @@ const styles = StyleSheet.create({
horizontallyInverted: {
transform: [{scaleX: -1}],
},
row: {
flexDirection: 'row'
},
rowReverse: {
flexDirection: 'row-reverse'
},
columnReverse: {
flexDirection: 'column-reverse'
}
});
export default VirtualizedList;

View File

@@ -7,11 +7,13 @@
* @noflow
* @format
*/
'use strict';
import React from 'react';
import View from '../../../exports/View';
import VirtualizedList, { type Props as VirtualizedListProps } from '../VirtualizedList';
import invariant from 'fbjs/lib/invariant';
import type { ViewToken } from '../ViewabilityHelper';
type Item = any;
@@ -33,12 +35,12 @@ type SectionBase = {
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
},
}) => ?React.Element<any>,
ItemSeparatorComponent?: ?React.ComponentType<*>,
ItemSeparatorComponent?: ?React.ComponentType<any>,
keyExtractor?: (item: SectionItem, index: ?number) => string,
// TODO: support more optional/override props
// FooterComponent?: ?ReactClass<*>,
// HeaderComponent?: ?ReactClass<*>,
// FooterComponent?: ?ReactClass<any>,
// HeaderComponent?: ?ReactClass<any>,
// onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void,
};
@@ -50,11 +52,11 @@ type OptionalProps<SectionT: SectionBase> = {
/**
* Rendered after the last item in the last section.
*/
ListFooterComponent?: ?(React.ComponentType<*> | React.Element<any>),
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Rendered at the very beginning of the list.
*/
ListHeaderComponent?: ?(React.ComponentType<*> | React.Element<any>),
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Default renderer for every item in every section.
*/
@@ -80,11 +82,11 @@ type OptionalProps<SectionT: SectionBase> = {
* Rendered at the bottom of every Section, except the very last one, in place of the normal
* ItemSeparatorComponent.
*/
SectionSeparatorComponent?: ?React.ComponentType<*>,
SectionSeparatorComponent?: ?React.ComponentType<any>,
/**
* Rendered at the bottom of every Item except the very last one in the last section.
*/
ItemSeparatorComponent?: ?React.ComponentType<*>,
ItemSeparatorComponent?: ?React.ComponentType<any>,
/**
* Warning: Virtualization can drastically improve memory consumption for long lists, but trashes
* the state of items when they scroll out of the render window, so make sure all relavent data is
@@ -97,7 +99,7 @@ type OptionalProps<SectionT: SectionBase> = {
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
* sure to also set the `refreshing` prop correctly.
*/
onRefresh?: ?Function,
onRefresh?: ?() => void,
/**
* Called when the viewability of rows changes, as defined by the
* `viewabilityConfig` prop.
@@ -130,10 +132,6 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
Props<SectionT>,
State,
> {
props: Props<SectionT>;
state: State;
static defaultProps: DefaultProps = {
...VirtualizedList.defaultProps,
data: [],
@@ -160,6 +158,48 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
return this._listRef;
}
constructor(props: Props<SectionT>, context: Object) {
super(props, context);
this.state = this._computeState(props);
}
UNSAFE_componentWillReceiveProps(nextProps: Props<SectionT>) {
this.setState(this._computeState(nextProps));
}
_computeState(props: Props<SectionT>): State {
const offset = props.ListHeaderComponent ? 1 : 0;
const stickyHeaderIndices = [];
const itemCount = props.sections.reduce((v, section) => {
stickyHeaderIndices.push(v + offset);
return v + section.data.length + 2; // Add two for the section header and footer.
}, 0);
return {
childProps: {
...props,
renderItem: this._renderItem,
ItemSeparatorComponent: undefined, // Rendered with renderItem
data: props.sections,
getItemCount: () => itemCount,
getItem,
keyExtractor: this._keyExtractor,
onViewableItemsChanged: props.onViewableItemsChanged
? this._onViewableItemsChanged
: undefined,
stickyHeaderIndices: props.stickySectionHeadersEnabled
? stickyHeaderIndices
: undefined,
},
};
}
render() {
return (
<VirtualizedList {...this.state.childProps} ref={this._captureRef} />
);
}
_keyExtractor = (item: Item, index: number) => {
const info = this._subExtractor(index);
return (info && info.key) || String(index);
@@ -303,7 +343,7 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
_getSeparatorComponent(
index: number,
info?: ?Object,
): ?React.ComponentType<*> {
): ?React.ComponentType<any> {
info = info || this._subExtractor(index);
if (!info) {
return null;
@@ -322,48 +362,6 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
return null;
}
_computeState(props: Props<SectionT>): State {
const offset = props.ListHeaderComponent ? 1 : 0;
const stickyHeaderIndices = [];
const itemCount = props.sections.reduce((v, section) => {
stickyHeaderIndices.push(v + offset);
return v + section.data.length + 2; // Add two for the section header and footer.
}, 0);
return {
childProps: {
...props,
renderItem: this._renderItem,
ItemSeparatorComponent: undefined, // Rendered with renderItem
data: props.sections,
getItemCount: () => itemCount,
getItem,
keyExtractor: this._keyExtractor,
onViewableItemsChanged: props.onViewableItemsChanged
? this._onViewableItemsChanged
: undefined,
stickyHeaderIndices: props.stickySectionHeadersEnabled
? stickyHeaderIndices
: undefined,
},
};
}
constructor(props: Props<SectionT>, context: Object) {
super(props, context);
this.state = this._computeState(props);
}
UNSAFE_componentWillReceiveProps(nextProps: Props<SectionT>) {
this.setState(this._computeState(nextProps));
}
render() {
return (
<VirtualizedList {...this.state.childProps} ref={this._captureRef} />
);
}
_cellRefs = {};
_listRef: VirtualizedList;
_captureRef = ref => {
@@ -374,25 +372,40 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
};
}
type ItemWithSeparatorProps = {
LeadingSeparatorComponent: ?React.ComponentType<*>,
SeparatorComponent: ?React.ComponentType<*>,
type ItemWithSeparatorCommonProps = $ReadOnly<{|
leadingItem: ?Item,
leadingSection: ?Object,
section: Object,
trailingItem: ?Item,
trailingSection: ?Object,
|}>;
type ItemWithSeparatorProps = $ReadOnly<{|
...ItemWithSeparatorCommonProps,
LeadingSeparatorComponent: ?React.ComponentType<any>,
SeparatorComponent: ?React.ComponentType<any>,
cellKey: string,
index: number,
item: Item,
onUpdateSeparator: (cellKey: string, newProps: Object) => void,
prevCellKey?: ?string,
renderItem: Function,
section: Object,
leadingItem: ?Item,
leadingSection: ?Object,
trailingItem: ?Item,
trailingSection: ?Object,
|}>;
type ItemWithSeparatorState = {
separatorProps: $ReadOnly<{|
highlighted: false,
...ItemWithSeparatorCommonProps,
|}>,
leadingSeparatorProps: $ReadOnly<{|
highlighted: false,
...ItemWithSeparatorCommonProps,
|}>,
};
class ItemWithSeparator extends React.Component<
ItemWithSeparatorProps,
$FlowFixMeState,
ItemWithSeparatorState,
> {
state = {
separatorProps: {
@@ -426,7 +439,7 @@ class ItemWithSeparator extends React.Component<
},
updateProps: (select: 'leading' | 'trailing', newProps: Object) => {
const {LeadingSeparatorComponent, cellKey, prevCellKey} = this.props;
if (select === 'leading' && LeadingSeparatorComponent) {
if (select === 'leading' && LeadingSeparatorComponent != null) {
this.setState(state => ({
leadingSeparatorProps: {...state.leadingSeparatorProps, ...newProps},
}));
@@ -439,10 +452,13 @@ class ItemWithSeparator extends React.Component<
},
};
UNSAFE_componentWillReceiveProps(props: ItemWithSeparatorProps) {
this.setState(state => ({
static getDerivedStateFromProps(
props: ItemWithSeparatorProps,
prevState: ItemWithSeparatorState,
): ?ItemWithSeparatorState {
return {
separatorProps: {
...this.state.separatorProps,
...prevState.separatorProps,
leadingItem: props.item,
leadingSection: props.leadingSection,
section: props.section,
@@ -450,14 +466,14 @@ class ItemWithSeparator extends React.Component<
trailingSection: props.trailingSection,
},
leadingSeparatorProps: {
...this.state.leadingSeparatorProps,
...prevState.leadingSeparatorProps,
leadingItem: props.leadingItem,
leadingSection: props.leadingSection,
section: props.section,
trailingItem: props.item,
trailingSection: props.trailingSection,
},
}));
};
}
updateSeparatorProps(newProps: Object) {

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "website",
"version": "0.9.8",
"version": "0.9.11",
"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.8"
"react-native-web": "0.9.11"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.9.8",
"babel-plugin-react-native-web": "0.9.11",
"url-loader": "^1.0.1",
"webpack": "^4.8.1"
}

View File

@@ -136,16 +136,6 @@ const ImageScreen = () => (
/>
</Section>
<Section title="Properties">
<DocItem
name="static resizeMode"
typeInfo="object"
example={{
code: '<Image resizeMode={Image.resizeMode.contain} />'
}}
/>
</Section>
<Section title="Methods">
<DocItem
name="static getSize"

View File

@@ -101,6 +101,12 @@ const ScrollViewScreen = () => (
]}
/>
<DocItem
name="pagingEnabled"
typeInfo="?boolean = false"
description="When true, the scroll view snaps to individual items in the list when scrolling."
/>
<DocItem
name="scrollEnabled"
typeInfo="?boolean = true"

1488
yarn.lock

File diff suppressed because it is too large Load Diff