mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-31 18:21:38 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4e6147081 | ||
|
|
3e1b68d801 | ||
|
|
1b493c9914 | ||
|
|
6ecdc1a517 | ||
|
|
619079cedf | ||
|
|
bbf7674b43 | ||
|
|
f163e4f16f | ||
|
|
bd8c2d6f24 | ||
|
|
3906b6b41b | ||
|
|
753ef963f6 | ||
|
|
0721245b3e | ||
|
|
b7adfd5f32 | ||
|
|
a9342daee2 | ||
|
|
ed0cafac7c | ||
|
|
6e6fd4b5d0 | ||
|
|
5cd533e6cc | ||
|
|
d5e8d85ce9 | ||
|
|
e234568a34 | ||
|
|
19cf0711bc | ||
|
|
067e3f346f | ||
|
|
2117e44e9d | ||
|
|
902ba22877 | ||
|
|
60c2cd65df | ||
|
|
fde29326f1 | ||
|
|
44d795437e | ||
|
|
03598d869b | ||
|
|
a3e44a5c60 | ||
|
|
02b124eceb |
@@ -1,5 +1,5 @@
|
||||
[version]
|
||||
^0.61.0
|
||||
^0.63.0
|
||||
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/.*/__tests__/.*
|
||||
@@ -14,4 +14,4 @@
|
||||
<PROJECT_ROOT>/types
|
||||
|
||||
[options]
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
|
||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -74,7 +74,7 @@ yarn compile --watch
|
||||
To run the interactive storybook:
|
||||
|
||||
```
|
||||
yarn docs:start
|
||||
yarn website
|
||||
```
|
||||
|
||||
When you're also making changes to the 'react-native-web' source files, run this command in another process:
|
||||
@@ -88,7 +88,7 @@ yarn compile --watch
|
||||
To run the performance benchmarks in a browser (opening `./packages/benchmarks/index.html`):
|
||||
|
||||
```
|
||||
yarn benchmark
|
||||
yarn benchmarks
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
26
README.md
26
README.md
@@ -6,10 +6,11 @@
|
||||
[React Native][react-native-url] to the Web.
|
||||
|
||||
* **High-quality user interfaces**: React Native for Web makes it easy to
|
||||
create [fast](packages/benchmarks/README.md), adaptive web UIs in JavaScript.
|
||||
It provides native-like interactions, support for multiple input modes (touch,
|
||||
mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL
|
||||
layout, built-in accessibility, and integrates with React Dev Tools.
|
||||
create [fast](https://github.com/necolas/react-native-web/blob/master/packages/benchmarks/README.md),
|
||||
adaptive web UIs in JavaScript. It provides native-like interactions, support
|
||||
for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed
|
||||
styles, built-in support for RTL layout, built-in accessibility, and integrates
|
||||
with React Dev Tools.
|
||||
|
||||
* **Write once, render anywhere**: React Native for Web interoperates with
|
||||
existing React DOM components and is compatible with the majority of the
|
||||
@@ -54,17 +55,18 @@ yarn add --dev babel-plugin-react-native-web
|
||||
|
||||
### Guides
|
||||
|
||||
* [Getting started](website/guides/getting-started.md)
|
||||
* [Style](website/guides/style.md)
|
||||
* [Accessibility](website/guides/accessibility.md)
|
||||
* [Internationalization](website/guides/internationalization.md)
|
||||
* [Direct manipulation](website/guides/direct-manipulation.md)
|
||||
* [Advanced use](website/guides/advanced.md)
|
||||
* [Getting started](https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md)
|
||||
* [Style](https://github.com/necolas/react-native-web/blob/master/website/guides/style.md)
|
||||
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/website/guides/accessibility.md)
|
||||
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/website/guides/internationalization.md)
|
||||
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/website/guides/direct-manipulation.md)
|
||||
* [Advanced use](https://github.com/necolas/react-native-web/blob/master/website/guides/advanced.md)
|
||||
|
||||
## Examples
|
||||
|
||||
There are several examples [on the website][website-url] and in the [website's
|
||||
source code](./website). Here is an example to get you started:
|
||||
source code](https://github.com/necolas/react-native-web/blob/master/website).
|
||||
Here is an example to get you started:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
@@ -132,6 +134,6 @@ React Native for Web is [BSD licensed](./LICENSE).
|
||||
[ci-url]: https://travis-ci.org/necolas/react-native-web
|
||||
[website-url]: https://necolas.github.io/react-native-web/storybook/
|
||||
[react-native-url]: https://facebook.github.io/react-native/
|
||||
[contributing-url]: ./.github/CONTRIBUTING.md
|
||||
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/.github/CONTRIBUTING.md
|
||||
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue
|
||||
[code-of-conduct]: https://code.facebook.com/codeofconduct
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"lerna": "2.5.1",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.3",
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": [
|
||||
|
||||
17
package.json
17
package.json
@@ -2,11 +2,12 @@
|
||||
"private": true,
|
||||
"name": "react-native-web-monorepo",
|
||||
"scripts": {
|
||||
"benchmark": "cd packages/benchmarks && yarn benchmark",
|
||||
"clean": "del ./packages/*/dist",
|
||||
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
|
||||
"docs:start": "cd website && yarn start",
|
||||
"docs:release": "cd website && yarn release",
|
||||
"benchmarks": "cd packages/benchmarks && yarn build",
|
||||
"benchmarks:release": "cd packages/benchmarks && yarn release",
|
||||
"website": "cd website && yarn start",
|
||||
"website:release": "cd website && yarn release",
|
||||
"flow": "flow",
|
||||
"fmt": "find packages scripts types website -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
|
||||
"fmt:cmd": "prettier --write",
|
||||
@@ -16,7 +17,7 @@
|
||||
"precommit": "lint-staged",
|
||||
"prerelease": "yarn test && yarn compile",
|
||||
"release": "node ./scripts/release/publish.js",
|
||||
"postrelease": "yarn docs:release",
|
||||
"postrelease": "yarn website:release && yarn benchmarks:release",
|
||||
"test": "yarn flow && yarn lint:check && yarn jest"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -33,18 +34,18 @@
|
||||
"babel-preset-react-native": "^4.0.0",
|
||||
"caniuse-api": "^2.0.0",
|
||||
"del-cli": "^1.1.0",
|
||||
"enzyme": "^3.2.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.0",
|
||||
"enzyme-to-json": "^3.2.2",
|
||||
"eslint": "^4.12.1",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-promise": "^3.6.0",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
"flow-bin": "^0.61.0",
|
||||
"flow-bin": "^0.63.1",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^21.2.1",
|
||||
"lerna": "^2.5.1",
|
||||
"lint-staged": "^4.1.3",
|
||||
"lerna": "^2.6.0",
|
||||
"lint-staged": "^6.0.0",
|
||||
"prettier": "^1.8.2",
|
||||
"raf": "^3.4.0",
|
||||
"react": "^16.2.0",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# babel-plugin-react-native-web
|
||||
|
||||
[![npm version][package-badge]][package-url] [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
A Babel plugin that will alias `react-native` to `react-native-web` and exclude
|
||||
any modules not required by your app (keeping bundle size down).
|
||||
|
||||
@@ -34,6 +36,9 @@ import { StyleSheet, View } from 'react-native';
|
||||
**After**
|
||||
|
||||
```js
|
||||
import StyleSheet from 'react-native-web/dist/apis/StyleSheet';
|
||||
import View from 'react-native-web/dist/components/View';
|
||||
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
|
||||
import View from 'react-native-web/dist/exports/View';
|
||||
```
|
||||
|
||||
[package-badge]: https://img.shields.io/npm/v/babel-plugin-react-native-web.svg?style=flat
|
||||
[package-url]: https://yarnpkg.com/en/package/babel-plugin-react-native-web
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "babel-plugin-react-native-web",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.3",
|
||||
"description": "Babel plugin for React Native for Web",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"babel-plugin-tester": "^4.0.0"
|
||||
"babel-plugin-tester": "^5.0.0"
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -66,6 +66,24 @@ import * as ReactNativeModules from 'react-native-web/dist/index';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`require "react-native" 1`] = `
|
||||
"
|
||||
const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
const ReactNative = require('react-native-web/dist/index');
|
||||
|
||||
const View = require('react-native-web/dist/exports/View');
|
||||
|
||||
const StyleSheet = require('react-native-web/dist/exports/StyleSheet');
|
||||
|
||||
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity');
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`require "react-native-web" 1`] = `
|
||||
"
|
||||
const ReactNative = require('react-native-web');
|
||||
@@ -74,7 +92,7 @@ const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = requ
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
const ReactNative = require('react-native-web');
|
||||
const ReactNative = require('react-native-web/dist/index');
|
||||
|
||||
const createElement = require('react-native-web/dist/exports/createElement');
|
||||
|
||||
|
||||
@@ -30,6 +30,13 @@ export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';`,
|
||||
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'require "react-native"',
|
||||
code: `const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'require "react-native-web"',
|
||||
code: `const ReactNative = require('react-native-web');
|
||||
|
||||
@@ -8,7 +8,7 @@ const isReactNativeRequire = (t, node) => {
|
||||
}
|
||||
const { id, init } = declarations[0];
|
||||
return (
|
||||
t.isObjectPattern(id) &&
|
||||
(t.isObjectPattern(id) || t.isIdentifier(id)) &&
|
||||
t.isCallExpression(init) &&
|
||||
t.isIdentifier(init.callee) &&
|
||||
init.callee.name === 'require' &&
|
||||
@@ -84,21 +84,35 @@ module.exports = function({ types: t }) {
|
||||
VariableDeclaration(path, state) {
|
||||
if (isReactNativeRequire(t, path.node)) {
|
||||
const { id } = path.node.declarations[0];
|
||||
const imports = id.properties
|
||||
.map(identifier => {
|
||||
const distLocation = getDistLocation(identifier.key.name);
|
||||
if (distLocation) {
|
||||
return t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
t.identifier(identifier.value.name),
|
||||
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
|
||||
)
|
||||
]);
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
if (t.isObjectPattern(id)) {
|
||||
const imports = id.properties
|
||||
.map(identifier => {
|
||||
const distLocation = getDistLocation(identifier.key.name);
|
||||
if (distLocation) {
|
||||
return t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
t.identifier(identifier.value.name),
|
||||
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
|
||||
)
|
||||
]);
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
path.replaceWithMultiple(imports);
|
||||
path.replaceWithMultiple(imports);
|
||||
} else if (t.isIdentifier(id)) {
|
||||
const name = id.name;
|
||||
const importIndex = t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
t.identifier(name),
|
||||
t.callExpression(t.identifier('require'), [
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
])
|
||||
)
|
||||
]);
|
||||
|
||||
path.replaceWith(importIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,92 @@
|
||||
# benchmarks
|
||||
|
||||
To run these benchmarks:
|
||||
Try the [benchmarks app](https://necolas.github.io/react-native-web/benchmarks) online.
|
||||
|
||||
To run the benchmarks locally:
|
||||
|
||||
```
|
||||
yarn benchmark
|
||||
open ./packages/benchmarks/dist/index.html
|
||||
```
|
||||
|
||||
To run benchmarks for individual implementations append `?<name>,<name>` to the
|
||||
URL, e.g., `?css-modules,react-native-web`.
|
||||
Develop against these benchmarks:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
yarn benchmark --watch
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
These benchmarks are crude approximations of extreme cases that libraries may
|
||||
These benchmarks are approximations of extreme cases that libraries may
|
||||
encounter. The deep and wide tree cases look at the performance of mounting and
|
||||
rendering large trees of styled elements. The Triangle case looks at the
|
||||
rendering large trees of styled elements. The dynamic case looks at the
|
||||
performance of repeated style updates to a large mounted tree. Some libraries
|
||||
must inject new styles for each "dynamic style", whereas others may not.
|
||||
Libraries without support for dynamic styles (i.e., they rely on user-authored
|
||||
inline styles) do not include the `SierpinskiTriangle` benchmark.
|
||||
inline styles) do not include a corresponding benchmark.
|
||||
|
||||
The components used in the render benchmarks are simple enough to be
|
||||
implemented by multiple UI or style libraries. The benchmark implementations
|
||||
and the features of the style libraries are _only approximately equivalent in
|
||||
functionality_.
|
||||
|
||||
## Results
|
||||
No benchmark will run for more than 20 seconds.
|
||||
|
||||
Typical render timings*: mean ± two standard deviations.
|
||||
## Example results
|
||||
|
||||
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
|
||||
### MacBook Pro (2011)
|
||||
|
||||
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63.
|
||||
|
||||
Typical render timings*: mean ± standard deviations.
|
||||
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `react-native-web@0.2.2` | `89.67` `±28.51` | `167.46` `±27.03` | `65.40` `±19.50` |
|
||||
| `css-modules` | `77.42` `±45.50` | `141.44` `±33.96` | - |
|
||||
| `inline-styles` | `236.25` `±95.57` | `477.01` `±88.30` | `40.95` `±23.53` |
|
||||
| `css-modules` | `15.23` `±04.31` | `21.27` `±07.03` | - |
|
||||
| `react-native-web@0.3.2` | `17.52` `±04.44` | `24.14` `±04.39` | `15.03` `±02.22` |
|
||||
| `inline-styles` | `50.06` `±06.70` | `76.38` `±09.58` | `06.43` `±02.02` |
|
||||
|
||||
Other libraries
|
||||
|
||||
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `styletron@3.0.0-rc.5` | `83.53` `±33.55` | `153.12` `±39.13` | `56.47` `±24.22` |
|
||||
| `aphrodite@1.2.5` | `88.23` `±31.22` | `164.03` `±34.70` | - |
|
||||
| `glamor@2.20.40` | `110.09` `±34.20` | `182.06` `±50.39` | ‡ |
|
||||
| `emotion@8.0.12` | `103.44` `±32.12` | `204.45` `±41.00` | `110.28` `±26.94` |
|
||||
| `react-jss@8.2.0` | `136.17` `±59.23` | `270.51` `±69.20` | - |
|
||||
| `styled-components@2.3.2` | `217.57` `±51.90` | `437.57` `±65.74` | `76.99` `±41.79` |
|
||||
| `reactxp@0.46.6` | `240.88` `±79.82` | `467.32` `±74.42` | `70.95` `±32.90`|
|
||||
| `radium@0.19.6` | `400.19` `±94.58` | `816.59` `±91.10` | `71.13` `±27.22` |
|
||||
| `aphrodite@1.2.5` | `17.27` `±05.96` | `24.89` `±08.36` | - |
|
||||
| `glamor@2.20.40` | `21.59` `±05.38` | `27.93` `±07.56` | ‡ |
|
||||
| `emotion@8.0.12` | `21.07` `±04.16` | `31.40` `±09.40` | ‡ `19.80` `±13.56` |
|
||||
| `styletron-react@3.0.3` | `23.55` `±05.14` | `34.26` `±07.58` | `10.39` `±02.94` |
|
||||
| `react-fela@5.0.0` | `27.58` `±04.26` | `39.54` `±05.46` | `10.93` `±01.69` |
|
||||
| `react-jss@8.2.1` | `27.31` `±07.87` | `40.74` `±10.67` | - |
|
||||
| `styled-jsx@2.2.1` | `27.46` `±07.85` | `41.47` `±11.53` | `29.16` `±09.98` |
|
||||
| `styled-components@2.4.0` | `43.89` `±06.99` | `63.26` `±09.02` | `16.17` `±03.71` |
|
||||
| `reactxp@0.51.0-alpha.9` | `51.86` `±07.21` | `78.80` `±11.85` | `15.04` `±03.92` |
|
||||
| `radium@0.21.0` | `101.06` `±13.00` | `144.46` `±16.94` | `17.44` `±03.59` |
|
||||
|
||||
These results indicate that render times when using `react-native-web`,
|
||||
`css-modules`, `aphrodite`, and `styletron` are roughly equivalent and
|
||||
significantly faster than alternatives.
|
||||
### Moto G4
|
||||
|
||||
*MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3. Google Chrome 62.
|
||||
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63
|
||||
|
||||
‡Glamor essentially crashes the browser tab.
|
||||
Typical render timings*: mean ± standard deviations.
|
||||
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `css-modules` | `56.18` `±19.54` | `75.95` `±23.55` | - |
|
||||
| `react-native-web@0.3.2` | `68.53` `±21.00` | `101.03` `±25.32` | `60.57` `±09.07` |
|
||||
| `inline-styles` | `140.32` `±23.91` | `208.55` `±35.25` | `20.36` `±04.92` |
|
||||
|
||||
Other libraries
|
||||
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `aphrodite@1.2.5` | `58.77` `±19.73` | `85.83` `±24.64` | - |
|
||||
| `glamor@2.20.40` | `81.05` `±15.87` | `104.02` `±20.92` | ‡ |
|
||||
| `emotion@8.0.12` | `77.12` `±19.61` | `112.04` `±24.43` | ‡ `80.40` `±40.56` |
|
||||
| `styletron-react@3.0.3` | `91.00` `±17.95` | `130.49` `±20.06` | `39.70` `±06.85` |
|
||||
| `react-fela@5.0.0` | `101.36` `±19.55` | `142.18` `±21.87` | `43.64` `±12.24` |
|
||||
| `styled-jsx@2.2.1` | `101.60` `±25.26` | `144.12` `±30.79` | `115.63` `±32.77` |
|
||||
| `react-jss@8.2.1` | `112.46` `±32.07` | `165.96` `±42.54` | - |
|
||||
| `styled-components@2.4.0` | `159.85` `±24.30` | `231.00` `±31.34` | `53.86` `±13.40` |
|
||||
| `reactxp@0.51.0-alpha.9` | `182.05` `±30.72` | `261.25` `±35.54` | `58.20` `±08.62` |
|
||||
| `radium@0.21.0` | `323.93` `±41.46` | `464.70` `±53.93` | `59.13` `±09.76` |
|
||||
|
||||
‡Glamor essentially crashes the browser tab. Emotion gets slower every iteration.
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Performance tests</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
html, body { height: 100%; width: 100%; overflow: hidden; }
|
||||
.root { height: 100%; overflow: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="root"></div>
|
||||
<script src="dist/performance.bundle.js"></script>
|
||||
<script src="./bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "benchmarks",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.3",
|
||||
"scripts": {
|
||||
"benchmark": "webpack --config ./webpack.config.js && open index.html"
|
||||
"build": "mkdir -p dist && cp -f index.html dist/index.html && webpack --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 -"
|
||||
},
|
||||
"dependencies": {
|
||||
"aphrodite": "^1.2.5",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"aphrodite": "1.2.5",
|
||||
"classnames": "^2.2.5",
|
||||
"d3-scale-chromatic": "^1.1.1",
|
||||
"emotion": "^8.0.12",
|
||||
"glamor": "^2.20.40",
|
||||
"marky": "^1.2.0",
|
||||
"radium": "^0.19.6",
|
||||
"emotion": "8.0.12",
|
||||
"fela": "5.0.0",
|
||||
"glamor": "2.20.40",
|
||||
"radium": "0.21.0",
|
||||
"react": "^16.2.0",
|
||||
"react-component-benchmark": "^0.0.4",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-jss": "^8.2.0",
|
||||
"react-native-web": "^0.3.0",
|
||||
"reactxp": "^0.46.6",
|
||||
"styled-components": "^2.3.2",
|
||||
"styletron-client": "^3.0.0-rc.5",
|
||||
"styletron-utils": "^3.0.0-rc.3"
|
||||
"react-fela": "5.0.0",
|
||||
"react-jss": "8.2.1",
|
||||
"react-native-web": "^0.3.2",
|
||||
"reactxp": "0.51.0-alpha.9",
|
||||
"styled-components": "2.4.0",
|
||||
"styled-jsx": "2.2.1",
|
||||
"styletron-client": "3.0.2",
|
||||
"styletron-react": "3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "^0.3.0",
|
||||
"css-loader": "^0.28.7",
|
||||
"babel-plugin-react-native-web": "^0.3.3",
|
||||
"css-loader": "^0.28.9",
|
||||
"style-loader": "^0.19.1",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-bundle-analyzer": "^2.9.1"
|
||||
"webpack-bundle-analyzer": "^2.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
293
packages/benchmarks/src/app/App.js
Normal file
293
packages/benchmarks/src/app/App.js
Normal file
@@ -0,0 +1,293 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
import Benchmark from './Benchmark';
|
||||
import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import Button from './Button';
|
||||
import { IconClear, IconEye } from './Icons';
|
||||
import ReportCard from './ReportCard';
|
||||
import Text from './Text';
|
||||
import Layout from './Layout';
|
||||
import { colors } from './theme';
|
||||
|
||||
const Overlay = () => <View style={[StyleSheet.absoluteFill, { zIndex: 2 }]} />;
|
||||
|
||||
export default class App extends Component {
|
||||
static displayName = '@app/App';
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
const currentBenchmarkName = Object.keys(props.tests)[0];
|
||||
this.state = {
|
||||
currentBenchmarkName,
|
||||
currentLibraryName: 'react-native-web',
|
||||
status: 'idle',
|
||||
results: []
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tests } = this.props;
|
||||
const { currentBenchmarkName, status, currentLibraryName, results } = this.state;
|
||||
const currentImplementation = tests[currentBenchmarkName][currentLibraryName];
|
||||
const { Component, Provider, getComponentProps, sampleCount } = currentImplementation;
|
||||
|
||||
return (
|
||||
<Layout
|
||||
actionPanel={
|
||||
<View>
|
||||
<View style={styles.pickers}>
|
||||
<View style={styles.pickerContainer}>
|
||||
<Text style={styles.pickerTitle}>Library</Text>
|
||||
<Text style={{ fontWeight: 'bold' }}>{currentLibraryName}</Text>
|
||||
|
||||
<Picker
|
||||
enabled={status !== 'running'}
|
||||
onValueChange={this._handleChangeLibrary}
|
||||
selectedValue={currentLibraryName}
|
||||
style={styles.picker}
|
||||
>
|
||||
{Object.keys(tests[currentBenchmarkName]).map(libraryName => (
|
||||
<Picker.Item key={libraryName} label={libraryName} value={libraryName} />
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{ width: 1, backgroundColor: colors.fadedGray }} />
|
||||
<View style={styles.pickerContainer}>
|
||||
<Text style={styles.pickerTitle}>Benchmark</Text>
|
||||
<Text>{currentBenchmarkName}</Text>
|
||||
<Picker
|
||||
enabled={status !== 'running'}
|
||||
onValueChange={this._handleChangeBenchmark}
|
||||
selectedValue={currentBenchmarkName}
|
||||
style={styles.picker}
|
||||
>
|
||||
{Object.keys(tests).map(test => (
|
||||
<Picker.Item key={test} label={test} value={test} />
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ flexDirection: 'row', height: 50 }}>
|
||||
<View style={styles.grow}>
|
||||
<Button
|
||||
onPress={this._handleStart}
|
||||
style={styles.button}
|
||||
title={status === 'running' ? 'Running…' : 'Run'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
listPanel={
|
||||
<View style={styles.listPanel}>
|
||||
<View style={styles.grow}>
|
||||
<View style={styles.listBar}>
|
||||
<View style={styles.iconClearContainer}>
|
||||
<TouchableOpacity onPress={this._handleClear}>
|
||||
<IconClear />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<ScrollView ref={this._setScrollRef} style={styles.grow}>
|
||||
{results.map((r, i) => (
|
||||
<ReportCard
|
||||
benchmarkName={r.benchmarkName}
|
||||
key={i}
|
||||
libraryName={r.libraryName}
|
||||
libraryVersion={r.libraryVersion}
|
||||
mean={r.mean}
|
||||
sampleCount={r.sampleCount}
|
||||
stdDev={r.stdDev}
|
||||
/>
|
||||
))}
|
||||
{status === 'running' ? (
|
||||
<ReportCard
|
||||
benchmarkName={currentBenchmarkName}
|
||||
libraryName={currentLibraryName}
|
||||
/>
|
||||
) : null}
|
||||
</ScrollView>
|
||||
</View>
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
viewPanel={
|
||||
<View style={styles.viewPanel}>
|
||||
<View style={styles.iconEyeContainer}>
|
||||
<TouchableOpacity onPress={this._handleVisuallyHideBenchmark}>
|
||||
<IconEye style={styles.iconEye} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<Provider>
|
||||
{status === 'running' ? (
|
||||
<React.Fragment>
|
||||
<View ref={this._setBenchWrapperRef}>
|
||||
<Benchmark
|
||||
component={Component}
|
||||
getComponentProps={getComponentProps}
|
||||
onComplete={this._createHandleComplete({
|
||||
sampleCount,
|
||||
benchmarkName: currentBenchmarkName,
|
||||
libraryName: currentLibraryName
|
||||
})}
|
||||
ref={this._setBenchRef}
|
||||
sampleCount={sampleCount}
|
||||
timeout={20000}
|
||||
type={Component.benchmarkType}
|
||||
/>
|
||||
</View>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Component {...getComponentProps({ cycle: 10 })} />
|
||||
)}
|
||||
</Provider>
|
||||
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_handleChangeBenchmark = value => {
|
||||
this.setState(() => ({ currentBenchmarkName: value }));
|
||||
};
|
||||
|
||||
_handleChangeLibrary = value => {
|
||||
this.setState(() => ({ currentLibraryName: value }));
|
||||
};
|
||||
|
||||
_handleStart = () => {
|
||||
this.setState(
|
||||
() => ({ status: 'running' }),
|
||||
() => {
|
||||
if (this._shouldHideBenchmark && this._benchWrapperRef) {
|
||||
this._benchWrapperRef.setNativeProps({ style: { opacity: 0 } });
|
||||
}
|
||||
this._benchmarkRef.start();
|
||||
this._scrollToEnd();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// hide the benchmark as it is performed (no flashing on screen)
|
||||
_handleVisuallyHideBenchmark = () => {
|
||||
this._shouldHideBenchmark = !this._shouldHideBenchmark;
|
||||
if (this._benchWrapperRef) {
|
||||
this._benchWrapperRef.setNativeProps({
|
||||
style: { opacity: this._shouldHideBenchmark ? 0 : 1 }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_createHandleComplete = ({ benchmarkName, libraryName, sampleCount }) => results => {
|
||||
this.setState(
|
||||
state => ({
|
||||
results: state.results.concat([
|
||||
{
|
||||
...results,
|
||||
benchmarkName,
|
||||
libraryName,
|
||||
libraryVersion: this.props.tests[benchmarkName][libraryName].version
|
||||
}
|
||||
]),
|
||||
status: 'complete'
|
||||
}),
|
||||
this._scrollToEnd
|
||||
);
|
||||
// console.log(results);
|
||||
// console.log(results.samples.map(sample => sample.elapsed.toFixed(1)).join('\n'));
|
||||
};
|
||||
|
||||
_handleClear = () => {
|
||||
this.setState(() => ({ results: [] }));
|
||||
};
|
||||
|
||||
_setBenchRef = ref => {
|
||||
this._benchmarkRef = ref;
|
||||
};
|
||||
|
||||
_setBenchWrapperRef = ref => {
|
||||
this._benchWrapperRef = ref;
|
||||
};
|
||||
|
||||
_setScrollRef = ref => {
|
||||
this._scrollRef = ref;
|
||||
};
|
||||
|
||||
// scroll the most recent result into view
|
||||
_scrollToEnd = () => {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (this._scrollRef) {
|
||||
this._scrollRef.scrollToEnd();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
viewPanel: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'black'
|
||||
},
|
||||
iconEye: {
|
||||
color: 'white',
|
||||
height: 32
|
||||
},
|
||||
iconEyeContainer: {
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
zIndex: 1
|
||||
},
|
||||
iconClearContainer: {
|
||||
height: '100%',
|
||||
marginLeft: 5
|
||||
},
|
||||
grow: {
|
||||
flex: 1
|
||||
},
|
||||
listPanel: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
marginHorizontal: 'auto'
|
||||
},
|
||||
listBar: {
|
||||
padding: 5,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
backgroundColor: colors.fadedGray,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.mediumGray,
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
pickers: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
pickerContainer: {
|
||||
flex: 1,
|
||||
padding: 5
|
||||
},
|
||||
pickerTitle: {
|
||||
fontSize: 12,
|
||||
color: colors.deepGray
|
||||
},
|
||||
picker: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
opacity: 0,
|
||||
width: '100%'
|
||||
},
|
||||
button: {
|
||||
borderRadius: 0,
|
||||
fontSize: 32,
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
221
packages/benchmarks/src/app/Benchmark/index.js
Normal file
221
packages/benchmarks/src/app/Benchmark/index.js
Normal file
@@ -0,0 +1,221 @@
|
||||
// @flow
|
||||
/* global $Values */
|
||||
import * as Timing from './timing';
|
||||
import React, { Component } from 'react';
|
||||
import { getMean, getMedian, getStdDev } from './math';
|
||||
|
||||
import type { BenchResultsType, FullSampleTimingType, SampleTimingType } from './types';
|
||||
|
||||
export const BenchmarkType = {
|
||||
MOUNT: 'mount',
|
||||
UPDATE: 'update',
|
||||
UNMOUNT: 'unmount'
|
||||
};
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
|
||||
switch (type) {
|
||||
// Render every odd iteration (first, third, etc)
|
||||
// Mounts and unmounts the component
|
||||
case BenchmarkType.MOUNT:
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return !((cycle + 1) % 2);
|
||||
// Render every iteration (updates previously rendered module)
|
||||
case BenchmarkType.UPDATE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const shouldRecord = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
|
||||
switch (type) {
|
||||
// Record every odd iteration (when mounted: first, third, etc)
|
||||
case BenchmarkType.MOUNT:
|
||||
return !((cycle + 1) % 2);
|
||||
// Record every iteration
|
||||
case BenchmarkType.UPDATE:
|
||||
return true;
|
||||
// Record every even iteration (when unmounted)
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return !(cycle % 2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const isDone = (
|
||||
cycle: number,
|
||||
sampleCount: number,
|
||||
type: $Values<typeof BenchmarkType>
|
||||
): boolean => {
|
||||
switch (type) {
|
||||
case BenchmarkType.MOUNT:
|
||||
return cycle >= sampleCount * 2 - 1;
|
||||
case BenchmarkType.UPDATE:
|
||||
return cycle >= sampleCount - 1;
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return cycle >= sampleCount * 2;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const sortNumbers = (a: number, b: number): number => a - b;
|
||||
|
||||
type BenchmarkPropsType = {
|
||||
component: typeof React.Component,
|
||||
getComponentProps?: Function,
|
||||
onComplete: (x: BenchResultsType) => void,
|
||||
sampleCount: number,
|
||||
timeout: number,
|
||||
type: $Values<typeof BenchmarkType>
|
||||
};
|
||||
|
||||
type BenchmarkStateType = {
|
||||
getComponentProps: Function,
|
||||
cycle: number,
|
||||
running: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Benchmark
|
||||
* TODO: documentation
|
||||
*/
|
||||
export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkStateType> {
|
||||
_startTime: number;
|
||||
_samples: Array<SampleTimingType>;
|
||||
|
||||
static displayName = 'Benchmark';
|
||||
|
||||
static defaultProps = {
|
||||
getComponentProps: () => emptyObject,
|
||||
sampleCount: 50,
|
||||
timeout: 10000, // 10 seconds
|
||||
type: BenchmarkType.MOUNT
|
||||
};
|
||||
|
||||
static Type = BenchmarkType;
|
||||
|
||||
constructor(props: BenchmarkPropsType, context?: {}) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
getComponentProps: props.getComponentProps,
|
||||
cycle: 0,
|
||||
running: false
|
||||
};
|
||||
this._startTime = 0;
|
||||
this._samples = [];
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: BenchmarkPropsType) {
|
||||
this.setState(state => ({ getComponentProps: nextProps.getComponentProps }));
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps: BenchmarkPropsType, nextState: BenchmarkStateType) {
|
||||
if (nextState.running && !this.state.running) {
|
||||
this._startTime = Timing.now();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { sampleCount, timeout, type } = this.props;
|
||||
const { cycle, running } = this.state;
|
||||
|
||||
if (running && shouldRecord(cycle, type)) {
|
||||
this._samples[cycle].end = Timing.now();
|
||||
}
|
||||
|
||||
if (running) {
|
||||
const now = Timing.now();
|
||||
if (!isDone(cycle, sampleCount, type) && now - this._startTime < timeout) {
|
||||
this._handleCycleComplete();
|
||||
} else {
|
||||
this._handleComplete(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.raf) {
|
||||
window.cancelAnimationFrame(this.raf);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { component: Component, type } = this.props;
|
||||
const { getComponentProps, cycle, running } = this.state;
|
||||
if (running && shouldRecord(cycle, type)) {
|
||||
this._samples[cycle] = { start: Timing.now() };
|
||||
}
|
||||
return running && shouldRender(cycle, type) ? (
|
||||
<Component {...getComponentProps({ cycle })} />
|
||||
) : null;
|
||||
}
|
||||
|
||||
start() {
|
||||
this._samples = [];
|
||||
this.setState(() => ({ running: true, cycle: 0 }));
|
||||
}
|
||||
|
||||
_handleCycleComplete() {
|
||||
const { getComponentProps, type } = this.props;
|
||||
const { cycle } = this.state;
|
||||
|
||||
// Calculate the component props outside of the time recording (render)
|
||||
// so that it doesn't skew results
|
||||
const getNextProps =
|
||||
type === BenchmarkType.UPDATE
|
||||
? obj => ({ ...getComponentProps(obj), 'data-test': cycle })
|
||||
: getComponentProps;
|
||||
|
||||
this.raf = window.requestAnimationFrame(() => {
|
||||
this.setState((state: BenchmarkStateType) => ({
|
||||
getComponentProps: getNextProps,
|
||||
cycle: state.cycle + 1
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
getSamples(): Array<FullSampleTimingType> {
|
||||
return this._samples.reduce(
|
||||
(
|
||||
memo: Array<FullSampleTimingType>,
|
||||
{ start, end: endTime }: SampleTimingType
|
||||
): Array<FullSampleTimingType> => {
|
||||
const end = endTime || 0;
|
||||
memo.push({ start, end, elapsed: end - start });
|
||||
return memo;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
_handleComplete(endTime: number) {
|
||||
const { onComplete } = this.props;
|
||||
const samples = this.getSamples();
|
||||
|
||||
this.setState(() => ({ running: false, cycle: 0 }));
|
||||
|
||||
const runTime = endTime - this._startTime;
|
||||
const sortedElapsedTimes = samples
|
||||
.map(({ elapsed }: { elapsed: number }): number => elapsed)
|
||||
.sort(sortNumbers);
|
||||
const mean = getMean(sortedElapsedTimes);
|
||||
const stdDev = getStdDev(sortedElapsedTimes);
|
||||
|
||||
onComplete({
|
||||
startTime: this._startTime,
|
||||
endTime,
|
||||
runTime,
|
||||
sampleCount: samples.length,
|
||||
samples: samples,
|
||||
max: sortedElapsedTimes[sortedElapsedTimes.length - 1],
|
||||
min: sortedElapsedTimes[0],
|
||||
median: getMedian(sortedElapsedTimes),
|
||||
mean,
|
||||
stdDev
|
||||
});
|
||||
}
|
||||
}
|
||||
27
packages/benchmarks/src/app/Benchmark/math.js
Normal file
27
packages/benchmarks/src/app/Benchmark/math.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
type ValuesType = Array<number>;
|
||||
|
||||
export const getStdDev = (values: ValuesType): number => {
|
||||
const avg = getMean(values);
|
||||
|
||||
const squareDiffs = values.map((value: number) => {
|
||||
const diff = value - avg;
|
||||
return diff * diff;
|
||||
});
|
||||
|
||||
return Math.sqrt(getMean(squareDiffs));
|
||||
};
|
||||
|
||||
export const getMean = (values: ValuesType): number => {
|
||||
const sum = values.reduce((sum: number, value: number) => sum + value, 0);
|
||||
return sum / values.length;
|
||||
};
|
||||
|
||||
export const getMedian = (values: ValuesType): number => {
|
||||
if (values.length === 1) {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
const numbers = values.sort((a: number, b: number) => a - b);
|
||||
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
|
||||
};
|
||||
18
packages/benchmarks/src/app/Benchmark/timing.js
Normal file
18
packages/benchmarks/src/app/Benchmark/timing.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
|
||||
const NS_PER_MS = 1e6;
|
||||
const MS_PER_S = 1e3;
|
||||
|
||||
// Returns a high resolution time (if possible) in milliseconds
|
||||
export function now(): number {
|
||||
if (window && window.performance) {
|
||||
return window.performance.now();
|
||||
} else if (process && process.hrtime) {
|
||||
const [seconds, nanoseconds] = process.hrtime();
|
||||
const secInMS = seconds * MS_PER_S;
|
||||
const nSecInMS = nanoseconds / NS_PER_MS;
|
||||
return secInMS + nSecInMS;
|
||||
} else {
|
||||
return Date.now();
|
||||
}
|
||||
}
|
||||
28
packages/benchmarks/src/app/Benchmark/types.js
Normal file
28
packages/benchmarks/src/app/Benchmark/types.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// @flow
|
||||
export type BenchResultsType = {
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
runTime: number,
|
||||
sampleCount: number,
|
||||
samples: Array<FullSampleTimingType>,
|
||||
max: number,
|
||||
min: number,
|
||||
median: number,
|
||||
mean: number,
|
||||
stdDev: number,
|
||||
p70: number,
|
||||
p95: number,
|
||||
p99: number
|
||||
};
|
||||
|
||||
export type SampleTimingType = {
|
||||
start: number,
|
||||
end?: number,
|
||||
elapsed?: number
|
||||
};
|
||||
|
||||
export type FullSampleTimingType = {
|
||||
start: number,
|
||||
end: number,
|
||||
elapsed: number
|
||||
};
|
||||
70
packages/benchmarks/src/app/Button.js
Normal file
70
packages/benchmarks/src/app/Button.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ColorPropType, StyleSheet, TouchableHighlight, Text } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import { bool, func, string } from 'prop-types';
|
||||
|
||||
export default class Button extends Component<*> {
|
||||
static displayName = '@app/Button';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: string,
|
||||
color: ColorPropType,
|
||||
disabled: bool,
|
||||
onPress: func.isRequired,
|
||||
style: TouchableHighlight.propTypes.style,
|
||||
testID: string,
|
||||
textStyle: Text.propTypes.style,
|
||||
title: string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
accessibilityLabel,
|
||||
color,
|
||||
disabled,
|
||||
onPress,
|
||||
style,
|
||||
textStyle,
|
||||
testID,
|
||||
title
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole="button"
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
styles.button,
|
||||
style,
|
||||
color && { backgroundColor: color },
|
||||
disabled && styles.buttonDisabled
|
||||
]}
|
||||
testID={testID}
|
||||
>
|
||||
<Text style={[styles.text, textStyle, disabled && styles.textDisabled]}>{title}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
backgroundColor: '#2196F3',
|
||||
borderRadius: 0,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
text: {
|
||||
color: '#fff',
|
||||
fontWeight: '500',
|
||||
padding: 8,
|
||||
textAlign: 'center',
|
||||
textTransform: 'uppercase'
|
||||
},
|
||||
buttonDisabled: {
|
||||
backgroundColor: '#dfdfdf'
|
||||
},
|
||||
textDisabled: {
|
||||
color: '#a1a1a1'
|
||||
}
|
||||
});
|
||||
55
packages/benchmarks/src/app/Icons.js
Normal file
55
packages/benchmarks/src/app/Icons.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { createElement, StyleSheet, Text } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
display: 'inline-block',
|
||||
fill: 'currentcolor',
|
||||
height: '1.25em',
|
||||
maxWidth: '100%',
|
||||
position: 'relative',
|
||||
userSelect: 'none',
|
||||
textAlignVertical: 'text-bottom'
|
||||
}
|
||||
});
|
||||
|
||||
const createIcon = children => {
|
||||
const Icon = props =>
|
||||
createElement(
|
||||
'svg',
|
||||
{
|
||||
style: StyleSheet.compose(styles.root, props.style),
|
||||
width: 24,
|
||||
height: 24,
|
||||
viewBox: '0 0 24 24'
|
||||
},
|
||||
children
|
||||
);
|
||||
Icon.propTypes = {
|
||||
style: Text.propTypes.style
|
||||
};
|
||||
return Icon;
|
||||
};
|
||||
|
||||
export const IconClear = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M12 1.25C6.072 1.25 1.25 6.072 1.25 12S6.072 22.75 12 22.75 22.75 17.928 22.75 12 17.928 1.25 12 1.25zm0 1.5c2.28 0 4.368.834 5.982 2.207L4.957 17.982C3.584 16.368 2.75 14.282 2.75 12c0-5.1 4.15-9.25 9.25-9.25zm0 18.5c-2.28 0-4.368-.834-5.982-2.207L19.043 6.018c1.373 1.614 2.207 3.7 2.207 5.982 0 5.1-4.15 9.25-9.25 9.25z" />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export const IconEye = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M14.548 11.634c-1.207 0-2.188-.98-2.188-2.188 0-.664.302-1.25.77-1.653-.363-.097-.736-.165-1.13-.165-2.416 0-4.375 1.96-4.375 4.376S9.585 16.38 12 16.38c2.418 0 4.377-1.96 4.377-4.376 0-.4-.07-.78-.17-1.146-.402.47-.992.776-1.66.776z" />
|
||||
<path d="M12 19.79c-7.228 0-10.12-6.724-10.24-7.01-.254-.466-.254-1.105.035-1.642C1.88 10.923 4.772 4.2 12 4.2s10.12 6.723 10.24 7.01c.254.465.254 1.104-.035 1.64-.085.216-2.977 6.94-10.205 6.94zm0-14c-6.154 0-8.668 5.787-8.772 6.033-.068.135-.068.208-.033.273.137.316 2.65 6.104 8.805 6.104 6.18 0 8.747-5.973 8.772-6.033.07-.136.07-.21.034-.274-.138-.316-2.652-6.103-8.806-6.103z" />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export const IconCopy = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M11.47 14.53c.146.146.338.22.53.22s.384-.073.53-.22l5-5c.293-.293.293-.768 0-1.06s-.768-.294-1.06 0l-3.72 3.72V2c0-.414-.337-.75-.75-.75s-.75.336-.75.75v10.19L7.53 8.47c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l5 5z" />
|
||||
<path d="M21.25 13.25c-.414 0-.75.336-.75.75v5.652c0 .437-.355.792-.792.792H4.292c-.437 0-.792-.355-.792-.792V14c0-.414-.336-.75-.75-.75S2 13.586 2 14v5.652c0 1.264 1.028 2.292 2.292 2.292h15.416c1.264 0 2.292-1.028 2.292-2.292V14c0-.414-.336-.75-.75-.75z" />
|
||||
</Fragment>
|
||||
);
|
||||
63
packages/benchmarks/src/app/Layout.js
Normal file
63
packages/benchmarks/src/app/Layout.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { colors } from './theme';
|
||||
import { element } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
export default class Layout extends Component {
|
||||
static propTypes = {
|
||||
actionPanel: element,
|
||||
listPanel: element,
|
||||
viewPanel: element
|
||||
};
|
||||
|
||||
state = {
|
||||
widescreen: false
|
||||
};
|
||||
|
||||
render() {
|
||||
const { viewPanel, actionPanel, listPanel } = this.props;
|
||||
const { widescreen } = this.state;
|
||||
return (
|
||||
<View onLayout={this._handleLayout} style={[styles.root, widescreen && styles.row]}>
|
||||
<View style={widescreen ? styles.grow : styles.stackPanel}>{viewPanel}</View>
|
||||
<View style={styles.grow}>
|
||||
<View style={styles.grow}>{listPanel}</View>
|
||||
<View style={styles.divider} />
|
||||
<View>{actionPanel}</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleLayout = ({ nativeEvent }) => {
|
||||
const { layout } = nativeEvent;
|
||||
const { width } = layout;
|
||||
if (width >= 740) {
|
||||
this.setState(() => ({ widescreen: true }));
|
||||
} else {
|
||||
this.setState(() => ({ widescreen: false }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
divider: {
|
||||
height: 10,
|
||||
backgroundColor: colors.fadedGray,
|
||||
borderBottomWidth: 1,
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.mediumGray
|
||||
},
|
||||
grow: {
|
||||
flex: 1
|
||||
},
|
||||
stackPanel: {
|
||||
height: '33.33%'
|
||||
}
|
||||
});
|
||||
71
packages/benchmarks/src/app/ReportCard.js
Normal file
71
packages/benchmarks/src/app/ReportCard.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import Text from './Text';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
const fmt = (time: number) => {
|
||||
const i = Number(Math.round(time + 'e2') + 'e-2').toFixed(2);
|
||||
return 10 / i > 1 ? `0${i}` : i;
|
||||
};
|
||||
|
||||
const ReportCard = ({ benchmarkName, libraryName, sampleCount, mean, stdDev, libraryVersion }) => {
|
||||
const sampleCountText = sampleCount != null ? `(${sampleCount})` : '';
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<View style={styles.left}>
|
||||
<Text numberOfLines={1} style={styles.bold}>
|
||||
{`${libraryName}${libraryVersion ? '@' + libraryVersion : ''}`}
|
||||
</Text>
|
||||
<Text numberOfLines={1}>
|
||||
{benchmarkName} {sampleCountText}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.right}>
|
||||
{mean ? (
|
||||
<Fragment>
|
||||
<Text style={[styles.bold, styles.monoFont]}>
|
||||
{fmt(mean)} ±{fmt(stdDev)} ms
|
||||
</Text>
|
||||
<Text style={[styles.monoFont, styles.centerText]}>
|
||||
<Text style={styles.smallText}>Σ = </Text>
|
||||
<Text>{Math.round(mean * sampleCount)} ms</Text>
|
||||
</Text>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Text style={styles.bold}>In progress…</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 5,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#eee'
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
smallText: { fontSize: 12 },
|
||||
monoFont: {
|
||||
fontFamily: 'monospace'
|
||||
},
|
||||
centerText: {
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
left: {
|
||||
width: '50%'
|
||||
},
|
||||
right: {
|
||||
flex: 1,
|
||||
alignItems: 'flex-end'
|
||||
}
|
||||
});
|
||||
|
||||
export default ReportCard;
|
||||
34
packages/benchmarks/src/app/Text.js
Normal file
34
packages/benchmarks/src/app/Text.js
Normal file
@@ -0,0 +1,34 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { bool } from 'prop-types';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { colors } from './theme';
|
||||
|
||||
class AppText extends React.Component {
|
||||
static displayName = '@app/Text';
|
||||
|
||||
static contextTypes = {
|
||||
isInAParentText: bool
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style, ...rest } = this.props;
|
||||
const { isInAParentText } = this.context;
|
||||
return <Text {...rest} style={[!isInAParentText && styles.baseText, style]} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
baseText: {
|
||||
color: colors.textBlack,
|
||||
fontSize: '1rem',
|
||||
lineHeight: '1.3125em'
|
||||
}
|
||||
});
|
||||
|
||||
export default AppText;
|
||||
101
packages/benchmarks/src/app/theme.js
Normal file
101
packages/benchmarks/src/app/theme.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
const baseFontSize = 14;
|
||||
const baseUnit = 1.3125;
|
||||
|
||||
const createPlatformLength = multiplier =>
|
||||
Platform.select({ web: `${multiplier}rem`, default: multiplier * baseFontSize });
|
||||
|
||||
/**
|
||||
* Exported variables
|
||||
*/
|
||||
|
||||
export const borderRadii = {
|
||||
normal: Platform.select({ web: '0.35rem', default: 5 }),
|
||||
infinite: '9999px'
|
||||
};
|
||||
|
||||
export const breakpoints = {
|
||||
small: 360,
|
||||
medium: 600,
|
||||
large: 800,
|
||||
xLarge: 1100
|
||||
};
|
||||
|
||||
/**
|
||||
* Color palette
|
||||
* DO NOT add new colors unless they are part of @design's color palette.
|
||||
* DO NOT use colors that are not specified here.
|
||||
* source: go/uicolors
|
||||
*/
|
||||
export const colors = {
|
||||
// Primary
|
||||
blue: '#1DA1F2',
|
||||
purple: '#794BC4',
|
||||
green: '#17BF63',
|
||||
yellow: '#FFAD1F',
|
||||
orange: '#F45D22',
|
||||
red: '#E0245E',
|
||||
// Text and interface grays
|
||||
textBlack: '#14171A',
|
||||
deepGray: '#657786',
|
||||
mediumGray: '#AAB8C2',
|
||||
lightGray: '#CCD6DD',
|
||||
fadedGray: '#E6ECF0',
|
||||
faintGray: '#F5F8FA',
|
||||
white: '#FFF',
|
||||
textBlue: '#1B95E0'
|
||||
};
|
||||
|
||||
export const fontFamilies = {
|
||||
normal: 'System',
|
||||
japan: Platform.select({
|
||||
web:
|
||||
'Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif',
|
||||
default: 'System'
|
||||
}),
|
||||
rtl: Platform.select({ web: 'Tahoma, Arial, sans-serif', default: 'System' })
|
||||
};
|
||||
|
||||
export const fontSizes = {
|
||||
// font scale
|
||||
small: createPlatformLength(0.85),
|
||||
normal: createPlatformLength(1),
|
||||
large: createPlatformLength(1.25),
|
||||
xLarge: createPlatformLength(1.5),
|
||||
jumbo: createPlatformLength(2)
|
||||
};
|
||||
|
||||
export const lineHeight = Platform.select({ web: `${baseUnit}` });
|
||||
|
||||
export const spaces = {
|
||||
// This set of space variables should be used for margin, padding
|
||||
micro: createPlatformLength(baseUnit * 0.125),
|
||||
xxSmall: createPlatformLength(baseUnit * 0.25),
|
||||
xSmall: createPlatformLength(baseUnit * 0.5),
|
||||
small: createPlatformLength(baseUnit * 0.75),
|
||||
medium: createPlatformLength(baseUnit),
|
||||
large: createPlatformLength(baseUnit * 1.5),
|
||||
xLarge: createPlatformLength(baseUnit * 2),
|
||||
xxLarge: createPlatformLength(baseUnit * 2.5),
|
||||
jumbo: createPlatformLength(baseUnit * 3)
|
||||
};
|
||||
|
||||
// On web, change the root font-size at specific breakpoints to scale the UI
|
||||
// for larger viewports.
|
||||
if (Platform.OS === 'web' && canUseDOM) {
|
||||
const { medium, large } = breakpoints;
|
||||
const htmlElement = document.documentElement;
|
||||
const setFontSize = width => {
|
||||
const fontSize = width > medium ? (width > large ? '18px' : '17px') : '16px';
|
||||
if (htmlElement) {
|
||||
htmlElement.style.fontSize = fontSize;
|
||||
}
|
||||
};
|
||||
|
||||
setFontSize(Dimensions.get('window').width);
|
||||
Dimensions.addEventListener('change', dimensions => {
|
||||
setFontSize(dimensions.window.width);
|
||||
});
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import * as marky from 'marky';
|
||||
|
||||
const fmt = time => `${Math.round(time * 100) / 100}ms`;
|
||||
|
||||
const measure = (name, fn) => {
|
||||
marky.mark(name);
|
||||
fn();
|
||||
const performanceMeasure = marky.stop(name);
|
||||
return performanceMeasure.duration;
|
||||
};
|
||||
|
||||
const mean = values => {
|
||||
const sum = values.reduce((sum, value) => sum + value, 0);
|
||||
return sum / values.length;
|
||||
};
|
||||
|
||||
const median = values => {
|
||||
if (!Array.isArray(values)) {
|
||||
return 0;
|
||||
}
|
||||
if (values.length === 1) {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
const numbers = [...values].sort((a, b) => a - b);
|
||||
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
|
||||
};
|
||||
|
||||
const standardDeviation = values => {
|
||||
const avg = mean(values);
|
||||
|
||||
const squareDiffs = values.map(value => {
|
||||
const diff = value - avg;
|
||||
return diff * diff;
|
||||
});
|
||||
|
||||
const meanSquareDiff = mean(squareDiffs);
|
||||
return Math.sqrt(meanSquareDiff);
|
||||
};
|
||||
|
||||
export const log = (name, description, durations) => {
|
||||
const stdDev = standardDeviation(durations);
|
||||
const formattedMean = fmt(mean(durations));
|
||||
const formattedMedian = fmt(median(durations));
|
||||
const formattedStdDev = fmt(stdDev);
|
||||
|
||||
console.groupCollapsed(`${name}\n${formattedMean} ±${fmt(2 * stdDev)}`);
|
||||
description && console.log(description);
|
||||
console.log(`Median: ${formattedMedian}`);
|
||||
console.log(`Mean: ${formattedMean}`);
|
||||
console.log(`Standard deviation: ${formattedStdDev}`);
|
||||
console.log(durations);
|
||||
console.groupEnd();
|
||||
};
|
||||
|
||||
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
|
||||
return new Promise(resolve => {
|
||||
const durations = [];
|
||||
let i = 0;
|
||||
|
||||
setup();
|
||||
teardown();
|
||||
|
||||
const done = () => {
|
||||
log(name, description, durations);
|
||||
resolve();
|
||||
};
|
||||
|
||||
const a = () => {
|
||||
setup();
|
||||
window.requestAnimationFrame(b);
|
||||
};
|
||||
|
||||
const b = () => {
|
||||
const duration = measure('mean', task);
|
||||
durations.push(duration);
|
||||
window.requestAnimationFrame(c);
|
||||
};
|
||||
|
||||
const c = () => {
|
||||
teardown();
|
||||
window.requestAnimationFrame(d);
|
||||
};
|
||||
|
||||
const d = () => {
|
||||
i += 1;
|
||||
if (i < runs) {
|
||||
window.requestAnimationFrame(a);
|
||||
} else {
|
||||
window.requestAnimationFrame(done);
|
||||
}
|
||||
};
|
||||
|
||||
window.requestAnimationFrame(a);
|
||||
});
|
||||
};
|
||||
|
||||
export default benchmark;
|
||||
@@ -1,58 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class DeepTree extends Component {
|
||||
static propTypes = {
|
||||
breadth: PropTypes.number.isRequired,
|
||||
components: PropTypes.object,
|
||||
depth: PropTypes.number.isRequired,
|
||||
id: PropTypes.number.isRequired,
|
||||
wrap: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
/* necessary for reactxp to work without errors */
|
||||
static childContextTypes = {
|
||||
focusManager: PropTypes.object
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
focusManager: {
|
||||
addFocusableComponent() {},
|
||||
removeFocusableComponent() {},
|
||||
restrictFocusWithin() {},
|
||||
removeFocusRestriction() {},
|
||||
limitFocusWithin() {},
|
||||
removeFocusLimitation() {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { breadth, components, depth, id, wrap } = this.props;
|
||||
const { Box } = components;
|
||||
|
||||
let result = (
|
||||
<Box color={id % 3} components={components} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
|
||||
{depth === 0 && <Box color={id % 3 + 3} components={components} fixed />}
|
||||
{depth !== 0 &&
|
||||
Array.from({ length: breadth }).map((el, i) => (
|
||||
<DeepTree
|
||||
breadth={breadth}
|
||||
components={components}
|
||||
depth={depth - 1}
|
||||
id={i}
|
||||
key={i}
|
||||
wrap={wrap}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
for (let i = 0; i < wrap; i++) {
|
||||
result = <Box components={components}>{result}</Box>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default DeepTree;
|
||||
@@ -1,17 +1,22 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { BenchmarkType } from 'react-component-benchmark';
|
||||
import { number, object } from 'prop-types';
|
||||
import React from 'react';
|
||||
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
|
||||
|
||||
const targetSize = 25;
|
||||
const targetSize = 10;
|
||||
|
||||
class SierpinskiTriangle extends React.Component {
|
||||
static displayName = 'SierpinskiTriangle';
|
||||
|
||||
static benchmarkType = BenchmarkType.UPDATE;
|
||||
|
||||
static propTypes = {
|
||||
Dot: PropTypes.node,
|
||||
depth: PropTypes.number,
|
||||
renderCount: PropTypes.number,
|
||||
s: PropTypes.number,
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number
|
||||
components: object,
|
||||
depth: number,
|
||||
renderCount: number,
|
||||
s: number,
|
||||
x: number,
|
||||
y: number
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -20,64 +25,66 @@ class SierpinskiTriangle extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { x, y, depth, renderCount, Dot } = this.props;
|
||||
const { components, x, y, depth, renderCount } = this.props;
|
||||
let { s } = this.props;
|
||||
const { Dot } = components;
|
||||
|
||||
if (s <= targetSize) {
|
||||
let fn;
|
||||
switch (depth) {
|
||||
case 1:
|
||||
fn = interpolatePurples;
|
||||
break;
|
||||
case 2:
|
||||
fn = interpolateBuPu;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
fn = interpolateRdPu;
|
||||
if (Dot) {
|
||||
if (s <= targetSize) {
|
||||
let fn;
|
||||
switch (depth) {
|
||||
case 1:
|
||||
fn = interpolatePurples;
|
||||
break;
|
||||
case 2:
|
||||
fn = interpolateBuPu;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
fn = interpolateRdPu;
|
||||
}
|
||||
|
||||
// introduce randomness to ensure that repeated runs don't produce the same colors
|
||||
const color = fn(renderCount * Math.random() / 20);
|
||||
return (
|
||||
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dot
|
||||
color={fn(renderCount / 20)}
|
||||
size={targetSize}
|
||||
x={x - targetSize / 2}
|
||||
y={y - targetSize / 2}
|
||||
s /= 2;
|
||||
|
||||
return [
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={1}
|
||||
key={1}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x}
|
||||
y={y - s / 2}
|
||||
/>,
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={2}
|
||||
key={2}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x - s}
|
||||
y={y + s / 2}
|
||||
/>,
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={3}
|
||||
key={3}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x + s}
|
||||
y={y + s / 2}
|
||||
/>
|
||||
);
|
||||
];
|
||||
} else {
|
||||
return <span style={{ color: 'white' }}>No implementation available</span>;
|
||||
}
|
||||
|
||||
s /= 2;
|
||||
|
||||
return [
|
||||
<SierpinskiTriangle
|
||||
Dot={Dot}
|
||||
depth={1}
|
||||
key={1}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x}
|
||||
y={y - s / 2}
|
||||
/>,
|
||||
<SierpinskiTriangle
|
||||
Dot={Dot}
|
||||
depth={2}
|
||||
key={2}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x - s}
|
||||
y={y + s / 2}
|
||||
/>,
|
||||
<SierpinskiTriangle
|
||||
Dot={Dot}
|
||||
depth={3}
|
||||
key={3}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x + s}
|
||||
y={y + s / 2}
|
||||
/>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
packages/benchmarks/src/cases/Tree.js
Normal file
45
packages/benchmarks/src/cases/Tree.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { BenchmarkType } from 'react-component-benchmark';
|
||||
import { number, object } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class Tree extends Component {
|
||||
static displayName = 'Tree';
|
||||
|
||||
static benchmarkType = BenchmarkType.MOUNT;
|
||||
|
||||
static propTypes = {
|
||||
breadth: number.isRequired,
|
||||
components: object,
|
||||
depth: number.isRequired,
|
||||
id: number.isRequired,
|
||||
wrap: number.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { breadth, components, depth, id, wrap } = this.props;
|
||||
const { Box } = components;
|
||||
|
||||
let result = (
|
||||
<Box color={id % 3} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
|
||||
{depth === 0 && <Box color={id % 3 + 3} fixed />}
|
||||
{depth !== 0 &&
|
||||
Array.from({ length: breadth }).map((el, i) => (
|
||||
<Tree
|
||||
breadth={breadth}
|
||||
components={components}
|
||||
depth={depth - 1}
|
||||
id={i}
|
||||
key={i}
|
||||
wrap={wrap}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
for (let i = 0; i < wrap; i++) {
|
||||
result = <Box>{result}</Box>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default Tree;
|
||||
@@ -1,14 +0,0 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import NestedTree from './NestedTree';
|
||||
import React from 'react';
|
||||
|
||||
const renderDeepTree = (label, components) =>
|
||||
createRenderBenchmark({
|
||||
name: `[${label}] Deep tree`,
|
||||
runs: 20,
|
||||
getElement() {
|
||||
return <NestedTree breadth={3} components={components} depth={6} id={0} wrap={1} />;
|
||||
}
|
||||
});
|
||||
|
||||
export default renderDeepTree;
|
||||
@@ -1,112 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import SierpinskiTriangle from './SierpinskiTriangle';
|
||||
import { log } from '../benchmark';
|
||||
|
||||
const node = document.querySelector('.root');
|
||||
|
||||
let runs = 20;
|
||||
|
||||
class Speedometer extends React.Component {
|
||||
/* necessary for reactxp to work without errors */
|
||||
static childContextTypes = {
|
||||
focusManager: PropTypes.object
|
||||
};
|
||||
getChildContext() {
|
||||
return {
|
||||
focusManager: {
|
||||
addFocusableComponent() {},
|
||||
removeFocusableComponent() {},
|
||||
restrictFocusWithin() {},
|
||||
removeFocusRestriction() {},
|
||||
limitFocusWithin() {},
|
||||
removeFocusLimitation() {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
Dot: PropTypes.node.isRequired,
|
||||
description: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
onComplete: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
state = { renderCount: -1 };
|
||||
|
||||
async componentDidMount() {
|
||||
const durations = [];
|
||||
|
||||
while ((runs -= 1)) {
|
||||
const prev = window.performance.now();
|
||||
await new Promise(resolve => {
|
||||
this.raf = window.requestAnimationFrame(() => {
|
||||
this.setState({ renderCount: this.state.renderCount + 1 }, () => {
|
||||
const now = window.performance.now();
|
||||
durations.push(now - prev);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const { description, name } = this.props;
|
||||
log(name, description, durations);
|
||||
|
||||
runs = 20;
|
||||
this.props.onComplete();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.cancelAnimationFrame(this.raf);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={styles.wrapper}>
|
||||
<SierpinskiTriangle
|
||||
Dot={this.props.Dot}
|
||||
renderCount={this.state.renderCount}
|
||||
s={1000}
|
||||
x={0}
|
||||
y={0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
transformOrigin: '0 0',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
width: '10px',
|
||||
height: '10px',
|
||||
backgroundColor: '#eee',
|
||||
transform: 'scale(0.33)'
|
||||
}
|
||||
};
|
||||
|
||||
const renderSierpinskiTriangle = (name, { Dot }) => () => {
|
||||
return new Promise(resolve => {
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
ReactDOM.render(
|
||||
<Speedometer
|
||||
Dot={Dot}
|
||||
description="Dynamic styles"
|
||||
name={`[${name}] Triangle`}
|
||||
onComplete={() => {
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
resolve();
|
||||
}}
|
||||
/>,
|
||||
node
|
||||
);
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
});
|
||||
};
|
||||
|
||||
export default renderSierpinskiTriangle;
|
||||
@@ -1,112 +0,0 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import React from 'react';
|
||||
|
||||
const tweet1 = {
|
||||
favorite_count: 30,
|
||||
favorited: true,
|
||||
id: '834889712556875776',
|
||||
lang: 'en',
|
||||
retweet_count: 6,
|
||||
retweeted: false,
|
||||
textParts: [
|
||||
{
|
||||
prefix: '',
|
||||
text: 'Living burrito to burrito '
|
||||
},
|
||||
{
|
||||
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
|
||||
isEmoji: true,
|
||||
prefix: '',
|
||||
text: '🌯'
|
||||
},
|
||||
{
|
||||
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
|
||||
isEmoji: true,
|
||||
prefix: '',
|
||||
text: '🌯'
|
||||
},
|
||||
{
|
||||
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
|
||||
isEmoji: true,
|
||||
prefix: '',
|
||||
text: '🌯'
|
||||
}
|
||||
],
|
||||
timestamp: 'Feb 23',
|
||||
user: {
|
||||
fullName: 'Nicolas',
|
||||
screenName: 'necolas',
|
||||
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
|
||||
}
|
||||
};
|
||||
|
||||
const tweet2 = {
|
||||
favorite_count: 84,
|
||||
favorited: false,
|
||||
id: '730896800060579840',
|
||||
lang: 'en',
|
||||
media: {
|
||||
source: {
|
||||
uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg',
|
||||
width: 600,
|
||||
height: 338
|
||||
}
|
||||
},
|
||||
retweet_count: 4,
|
||||
retweeted: true,
|
||||
textParts: [
|
||||
{
|
||||
prefix: '',
|
||||
text: 'Presenting '
|
||||
},
|
||||
{
|
||||
displayUrl: 'mobile.twitter.com',
|
||||
expandedUrl: 'https://mobile.twitter.com',
|
||||
isEntity: true,
|
||||
isUrl: true,
|
||||
linkRelation: 'nofollow',
|
||||
prefix: '',
|
||||
text: '',
|
||||
textDirection: 'ltr',
|
||||
url: 'https://t.co/4hRCAxiUUG'
|
||||
},
|
||||
{
|
||||
prefix: '',
|
||||
text: ' with '
|
||||
},
|
||||
{
|
||||
isEntity: true,
|
||||
isMention: true,
|
||||
prefix: '@',
|
||||
text: 'davidbellona',
|
||||
textDirection: 'ltr',
|
||||
url: '/davidbellona'
|
||||
},
|
||||
{
|
||||
prefix: '',
|
||||
text: " at Twitter's all hands meeting "
|
||||
}
|
||||
],
|
||||
timestamp: 'May 12',
|
||||
user: {
|
||||
fullName: 'Nicolas',
|
||||
screenName: 'necolas',
|
||||
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
|
||||
}
|
||||
};
|
||||
|
||||
const renderTweet = (label, { Tweet }) =>
|
||||
createRenderBenchmark({
|
||||
name: `[${label}] Tweet`,
|
||||
runs: 10,
|
||||
getElement() {
|
||||
return (
|
||||
<div style={{ width: 500 }}>
|
||||
<Tweet tweet={tweet1} />
|
||||
<Tweet tweet={tweet2} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default renderTweet;
|
||||
@@ -1,14 +0,0 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import NestedTree from './NestedTree';
|
||||
import React from 'react';
|
||||
|
||||
const renderWideTree = (label, components) =>
|
||||
createRenderBenchmark({
|
||||
name: `[${label}] Wide tree`,
|
||||
runs: 20,
|
||||
getElement() {
|
||||
return <NestedTree breadth={10} components={components} depth={3} id={0} wrap={4} />;
|
||||
}
|
||||
});
|
||||
|
||||
export default renderWideTree;
|
||||
@@ -1,24 +0,0 @@
|
||||
import benchmark from './benchmark';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const node = document.querySelector('.root');
|
||||
|
||||
const createRenderBenchmark = ({ description, getElement, name, runs }) => () => {
|
||||
const setup = () => {};
|
||||
const teardown = () => {
|
||||
ReactDOM.unmountComponentAtNode(node);
|
||||
};
|
||||
|
||||
return benchmark({
|
||||
name,
|
||||
description,
|
||||
runs,
|
||||
setup,
|
||||
teardown,
|
||||
task: () => {
|
||||
ReactDOM.render(getElement(), node);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default createRenderBenchmark;
|
||||
38
packages/benchmarks/src/impl.js
Normal file
38
packages/benchmarks/src/impl.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* @flow */
|
||||
|
||||
import { type Component } from 'react';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const context = require.context('./implementations/', true, /index\.js$/);
|
||||
const { dependencies } = packageJson;
|
||||
|
||||
type ComponentsType = {
|
||||
Box: Component,
|
||||
Dot: Component,
|
||||
Provider: Component,
|
||||
View: Component
|
||||
};
|
||||
|
||||
type ImplementationType = {
|
||||
components: ComponentsType,
|
||||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
const toImplementations = (context: Object): Array<ImplementationType> =>
|
||||
context.keys().map(path => {
|
||||
const components = context(path).default;
|
||||
const name = path.split('/')[1];
|
||||
const version = dependencies[name] || '';
|
||||
return { components, name, version };
|
||||
});
|
||||
|
||||
const toObject = (impls: Array<ImplementationType>): Object =>
|
||||
impls.reduce((acc, impl) => {
|
||||
acc[impl.name] = impl;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const map = toObject(toImplementations(context));
|
||||
|
||||
export default map;
|
||||
@@ -17,32 +17,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,7 +1,9 @@
|
||||
import Box from './Box';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,4 +1,5 @@
|
||||
.outer {
|
||||
align-self: flex-start;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@@ -7,30 +8,30 @@
|
||||
}
|
||||
|
||||
.color0 {
|
||||
background-color: #222;
|
||||
background-color: #14171A;
|
||||
}
|
||||
|
||||
.color1 {
|
||||
background-color: #666;
|
||||
background-color: #AAB8C2;
|
||||
}
|
||||
|
||||
.color2 {
|
||||
background-color: #999;
|
||||
background-color: #E6ECF0;
|
||||
}
|
||||
|
||||
.color3 {
|
||||
background-color: blue;
|
||||
background-color: #FFAD1F;
|
||||
}
|
||||
|
||||
.color4 {
|
||||
background-color: orange;
|
||||
background-color: #F45D22;
|
||||
}
|
||||
|
||||
.color5 {
|
||||
background-color: red;
|
||||
background-color: #E0245E;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Box from './Box';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ const Dot = ({ size, x, y, children, color }) => (
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
@@ -25,7 +25,8 @@ const styles = {
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ const Dot = ({ size, x, y, children, color }) => (
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
@@ -25,7 +25,8 @@ const styles = {
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ const Dot = ({ size, x, y, children, color }) => (
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -27,7 +27,8 @@ const styles = {
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -17,32 +17,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ const Dot = ({ size, x, y, children, color }) => (
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
}
|
||||
]}
|
||||
>
|
||||
@@ -28,7 +28,8 @@ const styles = {
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
46
packages/benchmarks/src/implementations/react-fela/Box.js
vendored
Normal file
46
packages/benchmarks/src/implementations/react-fela/Box.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import { createComponent } from 'react-fela';
|
||||
import View from './View';
|
||||
|
||||
const Box = createComponent(
|
||||
({ color, fixed = false, layout = 'column', outer = false }) => ({
|
||||
...styles[`color${color}`],
|
||||
...(fixed && styles.fixed),
|
||||
...(layout === 'row' && styles.row),
|
||||
...(outer && styles.outer)
|
||||
}),
|
||||
View
|
||||
);
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: '4px'
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: '6px',
|
||||
height: '6px'
|
||||
}
|
||||
};
|
||||
|
||||
export default Box;
|
||||
28
packages/benchmarks/src/implementations/react-fela/Dot.js
vendored
Normal file
28
packages/benchmarks/src/implementations/react-fela/Dot.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { createComponent } from 'react-fela';
|
||||
|
||||
const Dot = createComponent(
|
||||
({ size, x, y, children, color }) => ({
|
||||
...staticStyle,
|
||||
borderBottomColor: color,
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
}),
|
||||
'div'
|
||||
);
|
||||
|
||||
const staticStyle = {
|
||||
position: 'absolute',
|
||||
cursor: 'pointer',
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
};
|
||||
|
||||
export default Dot;
|
||||
19
packages/benchmarks/src/implementations/react-fela/Provider.js
vendored
Normal file
19
packages/benchmarks/src/implementations/react-fela/Provider.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { createRenderer } from 'fela';
|
||||
import { Provider as FelaProvider } from 'react-fela';
|
||||
import View from './View';
|
||||
|
||||
const renderer = createRenderer();
|
||||
|
||||
class Provider extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<FelaProvider renderer={renderer}>
|
||||
<View>{this.props.children}</View>
|
||||
</FelaProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Provider;
|
||||
24
packages/benchmarks/src/implementations/react-fela/View.js
vendored
Normal file
24
packages/benchmarks/src/implementations/react-fela/View.js
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { createComponent } from 'react-fela';
|
||||
|
||||
const View = createComponent(
|
||||
() => ({
|
||||
alignItems: 'stretch',
|
||||
borderWidth: '0px',
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: '0',
|
||||
margin: '0px',
|
||||
padding: '0px',
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: '0px',
|
||||
minWidth: '0px'
|
||||
}),
|
||||
'div'
|
||||
);
|
||||
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
@@ -18,32 +18,33 @@ const Box = ({ classes, color, fixed = false, layout = 'column', outer = false,
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
2
packages/benchmarks/src/implementations/react-jss/Provider.js
vendored
Normal file
2
packages/benchmarks/src/implementations/react-jss/Provider.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,7 +1,9 @@
|
||||
import Box from './Box';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ const Dot = ({ size, x, y, children, color }) =>
|
||||
borderRightWidth: size / 2,
|
||||
borderBottomWidth: size / 2,
|
||||
borderLeftWidth: size / 2,
|
||||
left: x,
|
||||
top: y
|
||||
marginLeft: x,
|
||||
marginTop: y
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -25,7 +25,8 @@ const styles = StyleSheet.create({
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: [{ translateX: '50%' }, { translateY: '50%' }]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
2
packages/benchmarks/src/implementations/react-native-web/Provider.js
vendored
Normal file
2
packages/benchmarks/src/implementations/react-native-web/Provider.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import { View } from 'react-native';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import { View } from 'react-native';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
|
||||
|
||||
const styles = {
|
||||
outer: Styles.createViewStyle({
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
}),
|
||||
row: Styles.createViewStyle({
|
||||
flexDirection: 'row'
|
||||
}),
|
||||
color0: Styles.createViewStyle({
|
||||
backgroundColor: '#222'
|
||||
backgroundColor: '#14171A'
|
||||
}),
|
||||
color1: Styles.createViewStyle({
|
||||
backgroundColor: '#666'
|
||||
backgroundColor: '#AAB8C2'
|
||||
}),
|
||||
color2: Styles.createViewStyle({
|
||||
backgroundColor: '#999'
|
||||
backgroundColor: '#E6ECF0'
|
||||
}),
|
||||
color3: Styles.createViewStyle({
|
||||
backgroundColor: 'blue'
|
||||
backgroundColor: '#FFAD1F'
|
||||
}),
|
||||
color4: Styles.createViewStyle({
|
||||
backgroundColor: 'orange'
|
||||
backgroundColor: '#F45D22'
|
||||
}),
|
||||
color5: Styles.createViewStyle({
|
||||
backgroundColor: 'red'
|
||||
backgroundColor: '#E0245E'
|
||||
}),
|
||||
fixed: Styles.createViewStyle({
|
||||
width: 20,
|
||||
height: 20
|
||||
width: 6,
|
||||
height: 6
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ const Dot = ({ size, x, y, children, color }) => (
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -27,7 +27,8 @@ const styles = {
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
31
packages/benchmarks/src/implementations/reactxp/Provider.js
Normal file
31
packages/benchmarks/src/implementations/reactxp/Provider.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { object } from 'prop-types';
|
||||
import { View } from 'reactxp';
|
||||
|
||||
class Provider extends React.Component {
|
||||
/* this mock context is necessary for reactxp to work without errors… ¯\_(ツ)_/¯ */
|
||||
|
||||
static childContextTypes = {
|
||||
focusManager: object
|
||||
};
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
focusManager: {
|
||||
addFocusableComponent() {},
|
||||
removeFocusableComponent() {},
|
||||
restrictFocusWithin() {},
|
||||
removeFocusRestriction() {},
|
||||
limitFocusWithin() {},
|
||||
removeFocusLimitation() {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <View style={{ overflow: 'visible' }}>{this.props.children}</View>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Provider;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import { View } from 'reactxp';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
@@ -4,28 +4,28 @@ import View from './View';
|
||||
const getColor = color => {
|
||||
switch (color) {
|
||||
case 0:
|
||||
return '#222';
|
||||
return '#14171A';
|
||||
case 1:
|
||||
return '#666';
|
||||
return '#AAB8C2';
|
||||
case 2:
|
||||
return '#999';
|
||||
return '#E6ECF0';
|
||||
case 3:
|
||||
return 'blue';
|
||||
return '#FFAD1F';
|
||||
case 4:
|
||||
return 'orange';
|
||||
return '#F45D22';
|
||||
case 5:
|
||||
return 'red';
|
||||
return '#E0245E';
|
||||
default:
|
||||
return 'transparent';
|
||||
}
|
||||
};
|
||||
|
||||
const Box = styled(View)`
|
||||
align-self: flex-start;
|
||||
flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
|
||||
padding: ${props => (props.outer ? '4px' : '0')};
|
||||
height: ${props => (props.fixed ? '20px' : 'auto')};
|
||||
width: ${props => (props.fixed ? '20px' : 'auto')};
|
||||
background-color: ${props => getColor(props.color)};
|
||||
${props => props.fixed && 'height:6px;'} ${props =>
|
||||
props.fixed && 'width:6px;'} background-color: ${props => getColor(props.color)};
|
||||
`;
|
||||
|
||||
export default Box;
|
||||
|
||||
@@ -4,8 +4,8 @@ import View from './View';
|
||||
|
||||
const Dot = styled(View).attrs({
|
||||
style: props => ({
|
||||
left: `${props.x}px`,
|
||||
top: `${props.y}px`,
|
||||
marginLeft: `${props.x}px`,
|
||||
marginTop: `${props.y}px`,
|
||||
borderRightWidth: `${props.size / 2}px`,
|
||||
borderBottomWidth: `${props.size / 2}px`,
|
||||
borderLeftWidth: `${props.size / 2}px`,
|
||||
@@ -19,6 +19,7 @@ const Dot = styled(View).attrs({
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-top-width: 0;
|
||||
transform: translate(50%, 50%);
|
||||
`;
|
||||
|
||||
export default Dot;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
|
||||
46
packages/benchmarks/src/implementations/styled-jsx/Box.js
Normal file
46
packages/benchmarks/src/implementations/styled-jsx/Box.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import View from './View';
|
||||
|
||||
const getColor = color => {
|
||||
switch (color) {
|
||||
case 0:
|
||||
return '#14171A';
|
||||
case 1:
|
||||
return '#AAB8C2';
|
||||
case 2:
|
||||
return '#E6ECF0';
|
||||
case 3:
|
||||
return '#FFAD1F';
|
||||
case 4:
|
||||
return '#F45D22';
|
||||
case 5:
|
||||
return '#E0245E';
|
||||
default:
|
||||
return 'transparent';
|
||||
}
|
||||
};
|
||||
|
||||
const Box = props => {
|
||||
const { className, children: styles } = (
|
||||
<scope className={classnames('Box', props.fixed && 'fixed')}>
|
||||
<style jsx>{`
|
||||
.Box {
|
||||
align-self: flex-start;
|
||||
flex-direction: ${props.layout === 'column' ? 'column' : 'row'};
|
||||
padding: ${props.outer ? '4px' : '0'};
|
||||
background-color: ${getColor(props.color)};
|
||||
}
|
||||
.fixed {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
}
|
||||
`}</style>
|
||||
</scope>
|
||||
).props;
|
||||
|
||||
return <View className={className}>{[props.children, styles]}</View>;
|
||||
};
|
||||
|
||||
export default Box;
|
||||
36
packages/benchmarks/src/implementations/styled-jsx/Dot.js
Normal file
36
packages/benchmarks/src/implementations/styled-jsx/Dot.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import View from './View';
|
||||
|
||||
const Dot = props => {
|
||||
const { className, children: styles } = (
|
||||
<scope className="Dot">
|
||||
<style jsx>{`
|
||||
.Dot {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-top-width: 0;
|
||||
transform: translate(50%, 50%);
|
||||
}
|
||||
`}</style>
|
||||
<style jsx>{`
|
||||
.Dot {
|
||||
margin-left: ${props.x}px;
|
||||
margin-top: ${props.y}px;
|
||||
border-right-width: ${props.size / 2}px;
|
||||
border-bottom-width: ${props.size / 2}px;
|
||||
border-left-width: ${props.size / 2}px;
|
||||
border-bottom-color: ${props.color};
|
||||
}
|
||||
`}</style>
|
||||
</scope>
|
||||
).props;
|
||||
|
||||
return <View className={className}>{[props.children, styles]}</View>;
|
||||
};
|
||||
|
||||
export default Dot;
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
32
packages/benchmarks/src/implementations/styled-jsx/View.js
Normal file
32
packages/benchmarks/src/implementations/styled-jsx/View.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { children, className, ...props } = this.props;
|
||||
return (
|
||||
<div {...props} className={`initial ${className}`}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
.initial {
|
||||
align-items: stretch;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-basis: auto;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default View;
|
||||
11
packages/benchmarks/src/implementations/styled-jsx/index.js
Normal file
11
packages/benchmarks/src/implementations/styled-jsx/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { styled } from 'styletron-react';
|
||||
import View from './View';
|
||||
|
||||
const Box = styled(
|
||||
View,
|
||||
({ color, fixed = false, layout = 'column', outer = false, ...other }) => ({
|
||||
...styles[`color${color}`],
|
||||
...(fixed && styles.fixed),
|
||||
...(layout === 'row' && styles.row),
|
||||
...(outer && styles.outer)
|
||||
})
|
||||
);
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: '4px'
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: '6px',
|
||||
height: '6px'
|
||||
}
|
||||
};
|
||||
|
||||
export default Box;
|
||||
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { styled } from 'styletron-react';
|
||||
|
||||
const Dot = styled('div', ({ size, x, y, children, color }) => ({
|
||||
...staticStyle,
|
||||
borderBottomColor: color,
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
marginLeft: `${x}px`,
|
||||
marginTop: `${y}px`
|
||||
}));
|
||||
|
||||
const staticStyle = {
|
||||
position: 'absolute',
|
||||
cursor: 'pointer',
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0,
|
||||
transform: 'translate(50%, 50%)'
|
||||
};
|
||||
|
||||
export default Dot;
|
||||
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import Styletron from 'styletron-client';
|
||||
import { StyletronProvider } from 'styletron-react';
|
||||
import View from './View';
|
||||
|
||||
const styletron = new Styletron();
|
||||
|
||||
class Provider extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<StyletronProvider styletron={styletron}>
|
||||
<View>{this.props.children}</View>
|
||||
</StyletronProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Provider;
|
||||
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { styled } from 'styletron-react';
|
||||
|
||||
const View = styled('div', ({ style }) => ({
|
||||
...viewStyle,
|
||||
style
|
||||
}));
|
||||
|
||||
const viewStyle = {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: '0px',
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: '0',
|
||||
margin: '0px',
|
||||
padding: '0px',
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: '0px',
|
||||
minWidth: '0px'
|
||||
};
|
||||
|
||||
export default View;
|
||||
@@ -0,0 +1,11 @@
|
||||
import Box from './Box';
|
||||
import Dot from './Dot';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Dot,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { injectStylePrefixed } from 'styletron-utils';
|
||||
import React from 'react';
|
||||
import View, { styletron } 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: injectStylePrefixed(styletron, {
|
||||
padding: '4px'
|
||||
}),
|
||||
row: injectStylePrefixed(styletron, {
|
||||
flexDirection: 'row'
|
||||
}),
|
||||
color0: injectStylePrefixed(styletron, {
|
||||
backgroundColor: '#222'
|
||||
}),
|
||||
color1: injectStylePrefixed(styletron, {
|
||||
backgroundColor: '#666'
|
||||
}),
|
||||
color2: injectStylePrefixed(styletron, {
|
||||
backgroundColor: '#999'
|
||||
}),
|
||||
color3: injectStylePrefixed(styletron, {
|
||||
backgroundColor: 'blue'
|
||||
}),
|
||||
color4: injectStylePrefixed(styletron, {
|
||||
backgroundColor: 'orange'
|
||||
}),
|
||||
color5: injectStylePrefixed(styletron, {
|
||||
backgroundColor: 'red'
|
||||
}),
|
||||
fixed: injectStylePrefixed(styletron, {
|
||||
width: '20px',
|
||||
height: '20px'
|
||||
})
|
||||
};
|
||||
|
||||
export default Box;
|
||||
@@ -1,37 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import { injectStylePrefixed } from 'styletron-utils';
|
||||
import { styletron } from './View';
|
||||
|
||||
const Dot = ({ size, x, y, children, color }) => (
|
||||
<div
|
||||
className={classnames(
|
||||
styles.root,
|
||||
injectStylePrefixed(styletron, {
|
||||
borderBottomColor: color,
|
||||
borderRightWidth: `${size / 2}px`,
|
||||
borderBottomWidth: `${size / 2}px`,
|
||||
borderLeftWidth: `${size / 2}px`,
|
||||
left: `${x}px`,
|
||||
top: `${y}px`
|
||||
})
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
const styles = {
|
||||
root: injectStylePrefixed(styletron, {
|
||||
position: 'absolute',
|
||||
cursor: 'pointer',
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderColor: 'transparent',
|
||||
borderStyle: 'solid',
|
||||
borderTopWidth: 0
|
||||
})
|
||||
};
|
||||
|
||||
export default Dot;
|
||||
@@ -1,33 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import Styletron from 'styletron-client';
|
||||
import { injectStylePrefixed } from 'styletron-utils';
|
||||
import React from 'react';
|
||||
|
||||
export const styletron = new Styletron();
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
return <div {...other} className={classnames(viewStyle, ...style)} />;
|
||||
}
|
||||
}
|
||||
|
||||
const viewStyle = injectStylePrefixed(styletron, {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: '0px',
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: '0',
|
||||
margin: '0px',
|
||||
padding: '0px',
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: '0px',
|
||||
minWidth: '0px'
|
||||
});
|
||||
|
||||
export default View;
|
||||
@@ -1,97 +1,56 @@
|
||||
import aphrodite from './implementations/aphrodite';
|
||||
import cssModules from './implementations/css-modules';
|
||||
import emotion from './implementations/emotion';
|
||||
import jss from './implementations/jss';
|
||||
import glamor from './implementations/glamor';
|
||||
import inlineStyles from './implementations/inline-styles';
|
||||
import radium from './implementations/radium';
|
||||
import reactNativeWeb from './implementations/react-native-web';
|
||||
import reactxp from './implementations/reactxp';
|
||||
import styledComponents from './implementations/styled-components';
|
||||
import styletron from './implementations/styletron';
|
||||
import App from './app/App';
|
||||
import impl from './impl';
|
||||
import Tree from './cases/Tree';
|
||||
import SierpinskiTriangle from './cases/SierpinskiTriangle';
|
||||
|
||||
import renderDeepTree from './cases/renderDeepTree';
|
||||
import renderSierpinskiTriangle from './cases/renderSierpinskiTriangle';
|
||||
import renderWideTree from './cases/renderWideTree';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const testMatrix = {
|
||||
'inline-styles': [
|
||||
() => renderDeepTree('inline-styles', inlineStyles),
|
||||
() => renderWideTree('inline-styles', inlineStyles),
|
||||
() => renderSierpinskiTriangle('inline-styles', inlineStyles)
|
||||
],
|
||||
'css-modules': [
|
||||
() => renderDeepTree('css-modules', cssModules),
|
||||
() => renderWideTree('css-modules', cssModules)
|
||||
],
|
||||
'react-native-web': [
|
||||
() => renderDeepTree('react-native-web', reactNativeWeb),
|
||||
() => renderWideTree('react-native-web', reactNativeWeb),
|
||||
() => renderSierpinskiTriangle('react-native-web', reactNativeWeb)
|
||||
],
|
||||
const implementations = impl;
|
||||
const packageNames = Object.keys(implementations);
|
||||
|
||||
aphrodite: [
|
||||
() => renderDeepTree('aphrodite', aphrodite),
|
||||
() => renderWideTree('aphrodite', aphrodite)
|
||||
],
|
||||
emotion: [
|
||||
() => renderDeepTree('emotion', emotion),
|
||||
() => renderWideTree('emotion', emotion),
|
||||
() => renderSierpinskiTriangle('emotion', emotion)
|
||||
],
|
||||
glamor: [
|
||||
() => renderDeepTree('glamor', glamor),
|
||||
() => renderWideTree('glamor', glamor)
|
||||
// disabled: glamor starts to lock up the browser
|
||||
// () => renderSierpinskiTriangle('glamor', glamor)
|
||||
],
|
||||
jss: [() => renderDeepTree('jss', jss), () => renderWideTree('jss', jss)],
|
||||
radium: [
|
||||
() => renderDeepTree('radium', radium),
|
||||
() => renderWideTree('radium', radium),
|
||||
() => renderSierpinskiTriangle('radium', radium)
|
||||
],
|
||||
reactxp: [
|
||||
() => renderDeepTree('reactxp', reactxp),
|
||||
() => renderWideTree('reactxp', reactxp),
|
||||
() => renderSierpinskiTriangle('reactxp', reactxp)
|
||||
],
|
||||
'styled-components': [
|
||||
() => renderDeepTree('styled-components', styledComponents),
|
||||
() => renderWideTree('styled-components', styledComponents),
|
||||
() => renderSierpinskiTriangle('styled-components', styledComponents)
|
||||
],
|
||||
styletron: [
|
||||
() => renderDeepTree('styletron', styletron),
|
||||
() => renderWideTree('styletron', styletron),
|
||||
() => renderSierpinskiTriangle('styletron', styletron)
|
||||
]
|
||||
const createTestBlock = fn => {
|
||||
return packageNames.reduce((testSetups, packageName) => {
|
||||
const { name, components, version } = implementations[packageName];
|
||||
const { Component, getComponentProps, sampleCount, Provider, benchmarkType } = fn(components);
|
||||
|
||||
testSetups[packageName] = {
|
||||
Component,
|
||||
getComponentProps,
|
||||
sampleCount,
|
||||
Provider,
|
||||
benchmarkType,
|
||||
version,
|
||||
name
|
||||
};
|
||||
return testSetups;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const allTests = Object.keys(testMatrix).reduce((acc, curr) => {
|
||||
testMatrix[curr].forEach(test => {
|
||||
acc.push(test);
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
const tests = {
|
||||
'Mount deep tree': createTestBlock(components => ({
|
||||
benchmarkType: 'mount',
|
||||
Component: Tree,
|
||||
getComponentProps: () => ({ breadth: 2, components, depth: 7, id: 0, wrap: 1 }),
|
||||
Provider: components.Provider,
|
||||
sampleCount: 50
|
||||
})),
|
||||
'Mount wide tree': createTestBlock(components => ({
|
||||
benchmarkType: 'mount',
|
||||
Component: Tree,
|
||||
getComponentProps: () => ({ breadth: 6, components, depth: 3, id: 0, wrap: 2 }),
|
||||
Provider: components.Provider,
|
||||
sampleCount: 50
|
||||
})),
|
||||
'Update dynamic styles': createTestBlock(components => ({
|
||||
benchmarkType: 'update',
|
||||
Component: SierpinskiTriangle,
|
||||
getComponentProps: ({ cycle }) => {
|
||||
return { components, s: 200, renderCount: cycle, x: 0, y: 0 };
|
||||
},
|
||||
Provider: components.Provider,
|
||||
sampleCount: 100
|
||||
}))
|
||||
};
|
||||
|
||||
const tests = [];
|
||||
|
||||
if (window.location.search) {
|
||||
window.location.search
|
||||
.slice(1)
|
||||
.split(',')
|
||||
.forEach(implementation => {
|
||||
if (Array.isArray(testMatrix[implementation])) {
|
||||
tests.push(...testMatrix[implementation]);
|
||||
} else {
|
||||
throw new Error(`Benchmark for ${implementation} not found`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tests.push(...allTests);
|
||||
}
|
||||
|
||||
tests.push(() => () => Promise.resolve(console.log('Done')));
|
||||
|
||||
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());
|
||||
ReactDOM.render(<App tests={tests} />, document.querySelector('.root'));
|
||||
|
||||
@@ -7,10 +7,10 @@ const appDirectory = path.resolve(__dirname);
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: ['babel-polyfill', './src/index'],
|
||||
entry: './src/index',
|
||||
output: {
|
||||
path: path.resolve(appDirectory, 'dist'),
|
||||
filename: 'performance.bundle.js'
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@@ -30,9 +30,9 @@ module.exports = {
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
cacheDirectory: false,
|
||||
presets: babelPreset,
|
||||
plugins: ['react-native-web']
|
||||
plugins: ['react-native-web', 'styled-jsx/babel']
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,14 +49,10 @@ module.exports = {
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
dead_code: true,
|
||||
drop_console: true,
|
||||
screw_ie8: true,
|
||||
warnings: false
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.2",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
|
||||
@@ -87,6 +87,9 @@ type State = {
|
||||
shouldDisplaySource: boolean
|
||||
};
|
||||
|
||||
const getAssetTimeout = source =>
|
||||
typeof source === 'object' && source.timeout ? source.timeout : 1000;
|
||||
|
||||
class Image extends Component<*, State> {
|
||||
static displayName = 'Image';
|
||||
|
||||
@@ -246,12 +249,16 @@ class Image extends Component<*, State> {
|
||||
}
|
||||
|
||||
_createImageLoader() {
|
||||
const { source } = this.props;
|
||||
this._destroyImageLoader();
|
||||
this._loadRequest = requestIdleCallback(() => {
|
||||
const uri = resolveAssetSource(this.props.source);
|
||||
this._imageRequestId = ImageLoader.load(uri, this._onLoad, this._onError);
|
||||
this._onLoadStart();
|
||||
});
|
||||
this._loadRequest = requestIdleCallback(
|
||||
() => {
|
||||
const uri = resolveAssetSource(source);
|
||||
this._imageRequestId = ImageLoader.load(uri, this._onLoad, this._onError);
|
||||
this._onLoadStart();
|
||||
},
|
||||
{ timeout: getAssetTimeout(source) }
|
||||
);
|
||||
}
|
||||
|
||||
_destroyImageLoader() {
|
||||
|
||||
@@ -40,9 +40,9 @@ class KeyboardAvoidingView extends Component<*> {
|
||||
|
||||
onKeyboardChange(event: Object) {}
|
||||
|
||||
onLayout(event: ViewLayoutEvent) {
|
||||
onLayout = (event: ViewLayoutEvent) => {
|
||||
this.frame = event.nativeEvent.layout;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
|
||||
@@ -20,6 +20,6 @@ export default class PickerItem extends Component<Props> {
|
||||
|
||||
render() {
|
||||
const { label, testID, value } = this.props;
|
||||
return createElement('option', { label, testID, value });
|
||||
return createElement('option', { testID, value }, label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/Picker prop "children" items 1`] = `
|
||||
<option
|
||||
value="value-1"
|
||||
>
|
||||
label-1
|
||||
</option>
|
||||
`;
|
||||
|
||||
exports[`components/Picker prop "children" renders items 1`] = `
|
||||
<select
|
||||
className="rn-fontFamily-poiln3 rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
|
||||
|
||||
@@ -16,6 +16,12 @@ describe('components/Picker', () => {
|
||||
const component = shallow(picker);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('items', () => {
|
||||
const pickerItem = <Picker.Item label="label-1" value="value-1" />;
|
||||
const component = shallow(pickerItem);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prop "enabled"', () => {
|
||||
|
||||
@@ -34,5 +34,9 @@ Object {
|
||||
"marginLeft": "10px",
|
||||
"marginRight": "10px",
|
||||
"marginTop": "50px",
|
||||
"overflowX": "hidden",
|
||||
"overflowY": "hidden",
|
||||
"overscrollBehaviorX": "contain",
|
||||
"overscrollBehaviorY": "contain",
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -143,7 +143,9 @@ describe('apis/StyleSheet/createReactDOMStyle', () => {
|
||||
borderWidth: 0,
|
||||
marginTop: 50,
|
||||
marginVertical: 25,
|
||||
margin: 10
|
||||
margin: 10,
|
||||
overflow: 'hidden',
|
||||
overscrollBehavior: 'contain'
|
||||
};
|
||||
|
||||
expect(createReactDOMStyle(style)).toMatchSnapshot();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user