mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
380 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ca4becc41 | ||
|
|
cc077e9019 | ||
|
|
1225b00cdb | ||
|
|
ed70617e91 | ||
|
|
134114de83 | ||
|
|
08ee7c83bb | ||
|
|
5fad78dcad | ||
|
|
e04343e48e | ||
|
|
5e3a946f8b | ||
|
|
4e3d8dbb02 | ||
|
|
fee909d26a | ||
|
|
9e58a7b5f1 | ||
|
|
20e1febe21 | ||
|
|
ef209ca281 | ||
|
|
a35949fa71 | ||
|
|
f5ac856c2d | ||
|
|
4b557b1e0b | ||
|
|
d4e9d9d256 | ||
|
|
0ff3e91592 | ||
|
|
092d5d12f7 | ||
|
|
507e0d41f5 | ||
|
|
9e863d5402 | ||
|
|
1364b1dfdf | ||
|
|
7f81e313ed | ||
|
|
a1892ec8b8 | ||
|
|
e6232d5980 | ||
|
|
e93a2eb478 | ||
|
|
44ecf1fe87 | ||
|
|
faec2b4a83 | ||
|
|
3677f0dd57 | ||
|
|
36d161a959 | ||
|
|
1ca18ab056 | ||
|
|
b43717e797 | ||
|
|
801d5f8c68 | ||
|
|
30f2ec9bf5 | ||
|
|
6f3e29f630 | ||
|
|
2607cb25ab | ||
|
|
8f56454ed7 | ||
|
|
d03e06632e | ||
|
|
66732394cb | ||
|
|
077d2f3e63 | ||
|
|
f6ad9c3afb | ||
|
|
f91ecaa81d | ||
|
|
ad3dee0204 | ||
|
|
1a0a40d9be | ||
|
|
0bf6e893c6 | ||
|
|
1190ca20a7 | ||
|
|
8f4bed8cb9 | ||
|
|
5a5d142100 | ||
|
|
fb999b5467 | ||
|
|
8b06f28281 | ||
|
|
9376c72a40 | ||
|
|
d4b1fde9cf | ||
|
|
f237fc3094 | ||
|
|
8777e25d8e | ||
|
|
18d60047d2 | ||
|
|
535a7b7027 | ||
|
|
bdaeac964c | ||
|
|
20257afe88 | ||
|
|
99348eaedb | ||
|
|
6cb16d059d | ||
|
|
3c660e2ad7 | ||
|
|
e9101abefe | ||
|
|
dfa8087f9a | ||
|
|
eaccd8799d | ||
|
|
85b2afc313 | ||
|
|
4865c7bcce | ||
|
|
9e9ab78130 | ||
|
|
00b795a87e | ||
|
|
1edf5241a1 | ||
|
|
4cfcdef264 | ||
|
|
a264c0b956 | ||
|
|
0d8aa24ff3 | ||
|
|
1b77ac4b2f | ||
|
|
44b185ed4c | ||
|
|
d1d570268a | ||
|
|
997c92f841 | ||
|
|
8e60690877 | ||
|
|
7bab19ae6c | ||
|
|
c7f287b207 | ||
|
|
02cfbf8987 | ||
|
|
6203a3fec6 | ||
|
|
d1d5461b29 | ||
|
|
b0ff4489a9 | ||
|
|
635fda8d63 | ||
|
|
5a5eb5425f | ||
|
|
44d59f4996 | ||
|
|
868ab55bac | ||
|
|
65d5a89040 | ||
|
|
deb0a85440 | ||
|
|
19381da37f | ||
|
|
47ba46780c | ||
|
|
88ddeca0c6 | ||
|
|
a61f71133e | ||
|
|
dad2888f7e | ||
|
|
5e9e81eafe | ||
|
|
7ae2a5e188 | ||
|
|
d13f78622b | ||
|
|
6ae68e948f | ||
|
|
b1b70a420d | ||
|
|
43d297bf59 | ||
|
|
060d96b42d | ||
|
|
dd5f8cf641 | ||
|
|
7f256c6423 | ||
|
|
05069253a1 | ||
|
|
f10ac058b6 | ||
|
|
6aa2ac1f70 | ||
|
|
79e6dbaab5 | ||
|
|
fc86c876e0 | ||
|
|
1f25ef82ae | ||
|
|
5b60dcf0ff | ||
|
|
1cf152e8a0 | ||
|
|
d034a0c6ec | ||
|
|
33d1cdf193 | ||
|
|
483a76cb5c | ||
|
|
65055028c6 | ||
|
|
93f425e414 | ||
|
|
ce4cc8a946 | ||
|
|
77fd867421 | ||
|
|
22999d7e5b | ||
|
|
3c400a662b | ||
|
|
e78ce713cb | ||
|
|
70282cb4ca | ||
|
|
7abdb33a1d | ||
|
|
a9c7b38df9 | ||
|
|
d57fb6407a | ||
|
|
bcdeda5dab | ||
|
|
edef737249 | ||
|
|
9163b974db | ||
|
|
a388ef3e26 | ||
|
|
a84ecefa5d | ||
|
|
54af7e9da2 | ||
|
|
be3c78f317 | ||
|
|
6b85f5a22a | ||
|
|
875a2c98b3 | ||
|
|
6525d9d84a | ||
|
|
61356a786b | ||
|
|
864250f34d | ||
|
|
7ee570f0ed | ||
|
|
118b64a932 | ||
|
|
3cc1e480a7 | ||
|
|
124de7562d | ||
|
|
7aef8f04c1 | ||
|
|
08a353fbef | ||
|
|
51557d306b | ||
|
|
6b910166b2 | ||
|
|
668d389035 | ||
|
|
79a6a5a486 | ||
|
|
1c37a42566 | ||
|
|
c38369ac0f | ||
|
|
03769f7d45 | ||
|
|
eb43a8f3e7 | ||
|
|
cdf13b880d | ||
|
|
47fad1ef58 | ||
|
|
5f69c8e8b8 | ||
|
|
21550db5f2 | ||
|
|
1cae5d55a1 | ||
|
|
11d23f850a | ||
|
|
d994a25017 | ||
|
|
756df70154 | ||
|
|
f0b06419f9 | ||
|
|
60ff75705e | ||
|
|
5e8ad67296 | ||
|
|
ba24a882be | ||
|
|
c7686209cd | ||
|
|
f1b281ae32 | ||
|
|
b676fbd5e0 | ||
|
|
51aef6c791 | ||
|
|
ae9a9cde5f | ||
|
|
beb907b180 | ||
|
|
a3362e1f38 | ||
|
|
64d2d34367 | ||
|
|
d83cd45b6f | ||
|
|
227971d22c | ||
|
|
4822cf4620 | ||
|
|
91e4528eac | ||
|
|
1ee64d8285 | ||
|
|
66a4c13bf3 | ||
|
|
9012e98ba7 | ||
|
|
046e01dfa9 | ||
|
|
6e71e1e058 | ||
|
|
d5a9f3e779 | ||
|
|
f16f5f21ce | ||
|
|
0bb7e67e63 | ||
|
|
c6b54930b6 | ||
|
|
599f1fcaf5 | ||
|
|
3f7a4e455f | ||
|
|
1f3e9cc6ee | ||
|
|
17ed63129f | ||
|
|
769334d04e | ||
|
|
dad80d5718 | ||
|
|
d8e93058da | ||
|
|
4ae894313f | ||
|
|
438f398022 | ||
|
|
630ee24fdd | ||
|
|
ae13873c2c | ||
|
|
7705f521c8 | ||
|
|
cbd98a8bd7 | ||
|
|
1f80e4c105 | ||
|
|
dbc8f31be6 | ||
|
|
ed994dc670 | ||
|
|
a57e58607a | ||
|
|
03ea259d70 | ||
|
|
e39b58fd04 | ||
|
|
ab45211401 | ||
|
|
32183bb92a | ||
|
|
761c42301d | ||
|
|
0863894f40 | ||
|
|
8f736ddefe | ||
|
|
ab686e2a07 | ||
|
|
2c14bdab2e | ||
|
|
0b8b064757 | ||
|
|
93eadb734b | ||
|
|
8d561d7309 | ||
|
|
cdca9e1e2b | ||
|
|
170bab659d | ||
|
|
941c628445 | ||
|
|
547c375bd6 | ||
|
|
aa85876eb2 | ||
|
|
50b168cc41 | ||
|
|
25a11e673d | ||
|
|
e846054f4e | ||
|
|
d6854abd7d | ||
|
|
1b172319b9 | ||
|
|
e81394c26e | ||
|
|
d33aa3eee2 | ||
|
|
5d78c73e8c | ||
|
|
7735d304ef | ||
|
|
b7c72308ea | ||
|
|
5fee075774 | ||
|
|
25204eeff0 | ||
|
|
9c61fe58d3 | ||
|
|
782125d169 | ||
|
|
af805d67e6 | ||
|
|
68068f8cb6 | ||
|
|
e05e2122d7 | ||
|
|
47dac44120 | ||
|
|
22af6894c2 | ||
|
|
458c534200 | ||
|
|
ec2db3e2a3 | ||
|
|
e6f00f7592 | ||
|
|
976320916e | ||
|
|
808790505e | ||
|
|
89ad493ce5 | ||
|
|
c4f2869ad8 | ||
|
|
3ae1e5b786 | ||
|
|
e929fb572d | ||
|
|
5af9d537c2 | ||
|
|
b7d3f0d099 | ||
|
|
f1ca00a13a | ||
|
|
9451c0f85e | ||
|
|
b408bc5537 | ||
|
|
a2f25a46c4 | ||
|
|
29d52f5b31 | ||
|
|
ba6be1f64a | ||
|
|
43f78828a5 | ||
|
|
26bc8173f0 | ||
|
|
ecae52ccc5 | ||
|
|
997653863f | ||
|
|
5dc692719f | ||
|
|
0361845537 | ||
|
|
f391031fb1 | ||
|
|
77799abf9b | ||
|
|
2cfd09ecdb | ||
|
|
89eea2b366 | ||
|
|
18440158b3 | ||
|
|
24eda7c4ad | ||
|
|
44ebd8f5a3 | ||
|
|
a3ed8f05e6 | ||
|
|
b653fe0bd3 | ||
|
|
30da226e4d | ||
|
|
f1f39bfabd | ||
|
|
267c5aab7e | ||
|
|
fe71c7efe5 | ||
|
|
eb59875bed | ||
|
|
e1fc253277 | ||
|
|
40b03aca52 | ||
|
|
418a1a9516 | ||
|
|
8762f8e9c8 | ||
|
|
10ef2bfe20 | ||
|
|
6d2ae4597e | ||
|
|
a34b8b149f | ||
|
|
6166024d15 | ||
|
|
701ecb7c52 | ||
|
|
75042093c2 | ||
|
|
bb417900a9 | ||
|
|
89e0a15d1b | ||
|
|
b2e0a3702f | ||
|
|
a4644c204d | ||
|
|
1e9536b611 | ||
|
|
d15dafc108 | ||
|
|
c9c1aab97e | ||
|
|
a2903f9d30 | ||
|
|
c7771ac64f | ||
|
|
c8129c2a99 | ||
|
|
b793737393 | ||
|
|
2a4d1c81d8 | ||
|
|
a8a25d66ea | ||
|
|
e06d7a9650 | ||
|
|
c2501f2bc2 | ||
|
|
c51e7f1965 | ||
|
|
dfff6b3780 | ||
|
|
5f6b4a746a | ||
|
|
f077907dd4 | ||
|
|
a94367bdcb | ||
|
|
65febbbc52 | ||
|
|
b14d2e5bd8 | ||
|
|
7c83ba162d | ||
|
|
3ffc005a7b | ||
|
|
50a70ad02f | ||
|
|
768e895701 | ||
|
|
af5fde994d | ||
|
|
c3d0763944 | ||
|
|
0aba506725 | ||
|
|
91032d8565 | ||
|
|
0696721488 | ||
|
|
fe18830ce6 | ||
|
|
1b86d02300 | ||
|
|
c56b472258 | ||
|
|
b00132f007 | ||
|
|
8b8f8f0374 | ||
|
|
8e94af34e1 | ||
|
|
7ffaf592d5 | ||
|
|
a1017fa785 | ||
|
|
5db300df35 | ||
|
|
214d862e61 | ||
|
|
4ef5453b33 | ||
|
|
a27671d7cf | ||
|
|
8d2a650670 | ||
|
|
4cc1c983e8 | ||
|
|
37413fd55f | ||
|
|
07ff0ea104 | ||
|
|
1a87657500 | ||
|
|
5e4c8e520a | ||
|
|
236121e32c | ||
|
|
39c76ca50c | ||
|
|
e65f91d849 | ||
|
|
a535c558d8 | ||
|
|
a74be91b7c | ||
|
|
8eaaf28a32 | ||
|
|
16d448dc5b | ||
|
|
ea75cced13 | ||
|
|
cfc56a1354 | ||
|
|
b1cd92a65d | ||
|
|
d87f71ebc1 | ||
|
|
a2cafe56fc | ||
|
|
351c0ac3d4 | ||
|
|
877c0d2818 | ||
|
|
3afc5d5de6 | ||
|
|
edf3b9b7ff | ||
|
|
518a85bf1b | ||
|
|
ba75acb66a | ||
|
|
bc68b0b6f4 | ||
|
|
44ecbc072e | ||
|
|
4cf4905fc2 | ||
|
|
509920be4b | ||
|
|
04e3c23e67 | ||
|
|
32f454de66 | ||
|
|
1273bfc7cf | ||
|
|
dc7f526f6b | ||
|
|
7cda89c5ce | ||
|
|
695eba45af | ||
|
|
92a2cb274a | ||
|
|
b1ca04d11e | ||
|
|
22ab70ea6f | ||
|
|
49f36d8eb1 | ||
|
|
80ba119b83 | ||
|
|
c30b67f6db | ||
|
|
4580f93199 | ||
|
|
4c46126ffe | ||
|
|
f8d5c15405 | ||
|
|
dc54e03625 | ||
|
|
4d5819ae28 | ||
|
|
5c482ef3be | ||
|
|
f51592f96e | ||
|
|
6bffe1775f | ||
|
|
7e75d037f2 | ||
|
|
7536508fe3 | ||
|
|
945fff0015 | ||
|
|
5032ed6fe1 |
8
.babelrc
8
.babelrc
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"react-native"
|
||||
],
|
||||
"plugins": [
|
||||
[ "transform-react-remove-prop-types", { "mode": "wrap" } ]
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# EditorConfig: http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
88
.eslintrc
88
.eslintrc
@@ -9,8 +9,11 @@
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"prettier",
|
||||
"prettier/react"
|
||||
],
|
||||
"plugins": [
|
||||
"jsx-a11y",
|
||||
"promise",
|
||||
"react"
|
||||
],
|
||||
@@ -21,34 +24,24 @@
|
||||
"globals": {
|
||||
"document": false,
|
||||
"navigator": false,
|
||||
"window": false
|
||||
"window": false,
|
||||
// Flow global types
|
||||
"HTMLInputElement": false,
|
||||
"ReactClass": false,
|
||||
"ReactComponent": false,
|
||||
"ReactElement": false,
|
||||
"ReactPropsChainableTypeChecker": false,
|
||||
"ReactPropsCheckType": false,
|
||||
"ReactPropTypes": false,
|
||||
"SyntheticEvent": false
|
||||
},
|
||||
"rules": {
|
||||
"accessor-pairs": 2,
|
||||
"array-bracket-spacing": ["error", "always"],
|
||||
"arrow-parens": [2, "always"],
|
||||
"arrow-spacing": [2, { "before": true, "after": true }],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"camelcase": 0,
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"constructor-super": 2,
|
||||
"curly": [2, "all"],
|
||||
"default-case": [2, { commentPattern: '^no default$' }],
|
||||
"dot-location": [2, "property"],
|
||||
"eol-last": 2,
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
"max-len": [2, 120, 4],
|
||||
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
|
||||
"new-parens": 2,
|
||||
"no-alert": 1,
|
||||
"no-array-constructor": 2,
|
||||
"no-caller": 2,
|
||||
@@ -71,8 +64,6 @@
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-boolean-cast": 2,
|
||||
"no-extra-parens": [2, "functions"],
|
||||
"no-extra-semi": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
@@ -85,10 +76,7 @@
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-multiple-empty-lines": [2, { "max": 1 }],
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-new": 2,
|
||||
@@ -110,11 +98,9 @@
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-sparse-arrays": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-undef": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
@@ -129,66 +115,20 @@
|
||||
"no-useless-constructor": 2,
|
||||
"no-useless-escape": 2,
|
||||
"no-var": 2,
|
||||
"no-whitespace-before-property": 2,
|
||||
"no-with": 2,
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"operator-linebreak": [2, "after"],
|
||||
"padded-blocks": [2, "never"],
|
||||
"prefer-const": 2,
|
||||
"prefer-rest-params": 2,
|
||||
"prefer-template": 2,
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"radix": 2,
|
||||
"rest-spread-spacing": ["error"],
|
||||
"semi": [2, "always"],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": 2,
|
||||
"valid-typeof": 2,
|
||||
"wrap-iife": [2, "outside"],
|
||||
"yield-star-spacing": [2, "both"],
|
||||
"yoda": [2, "never"],
|
||||
|
||||
// promise
|
||||
"promise/param-names": 2,
|
||||
|
||||
// jsx accessibility
|
||||
"jsx-a11y/aria-props": 2,
|
||||
"jsx-a11y/aria-proptypes": 2,
|
||||
"jsx-a11y/aria-role": 2,
|
||||
"jsx-a11y/aria-unsupported-elements": 2,
|
||||
"jsx-a11y/heading-has-content": 2,
|
||||
"jsx-a11y/href-no-hash": 2,
|
||||
"jsx-a11y/html-has-lang": 2,
|
||||
"jsx-a11y/img-has-alt": 2,
|
||||
"jsx-a11y/img-redundant-alt": 2,
|
||||
"jsx-a11y/label-has-for": 2,
|
||||
"jsx-a11y/mouse-events-have-key-events": 2,
|
||||
"jsx-a11y/no-access-key": 2,
|
||||
"jsx-a11y/no-marquee": 2,
|
||||
"jsx-a11y/no-onchange": 0,
|
||||
"jsx-a11y/onclick-has-focus": 2,
|
||||
"jsx-a11y/onclick-has-role": 2,
|
||||
"jsx-a11y/role-has-required-aria-props": 2,
|
||||
"jsx-a11y/role-supports-aria-props": 2,
|
||||
"jsx-a11y/scope": 2,
|
||||
"jsx-a11y/tabindex-no-positive": 2,
|
||||
|
||||
// react
|
||||
"jsx-quotes": [2, "prefer-single"],
|
||||
"react/display-name": 0,
|
||||
"react/jsx-boolean-value": 2,
|
||||
"react/jsx-handler-names": [2, {
|
||||
"eventHandlerPrefix": "_handle"
|
||||
}],
|
||||
"react/jsx-indent": [2, 2],
|
||||
"react/jsx-indent-props": [2, 2],
|
||||
"react/jsx-no-bind": 2,
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
|
||||
14
.flowconfig
Normal file
14
.flowconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
[ignore]
|
||||
.*/__tests__/.*
|
||||
.*/benchmarks/.*
|
||||
.*/docs/.*
|
||||
.*/node_modules/animated/*
|
||||
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
types
|
||||
|
||||
[options]
|
||||
unsafe.enable_getters_and_setters=true
|
||||
125
.github/CONTRIBUTING.md
vendored
Normal file
125
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# Contributing
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
|
||||
issue hasn't already been reported.
|
||||
|
||||
## Getting started
|
||||
|
||||
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
|
||||
to find a list of open issues that need attention.
|
||||
|
||||
Fork, then clone the repo:
|
||||
|
||||
```
|
||||
git clone https://github.com/your-username/react-native-web.git
|
||||
```
|
||||
|
||||
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
## Automated tests
|
||||
|
||||
To run flow:
|
||||
|
||||
```
|
||||
yarn flow
|
||||
```
|
||||
|
||||
To run the unit tests:
|
||||
|
||||
```
|
||||
yarn jest
|
||||
```
|
||||
|
||||
…in watch mode:
|
||||
|
||||
```
|
||||
yarn jest:watch
|
||||
```
|
||||
|
||||
To run all automated tests:
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
## Visual tests
|
||||
|
||||
To run the interactive storybook:
|
||||
|
||||
```
|
||||
yarn docs:start
|
||||
```
|
||||
|
||||
To generate a static build of the storybook:
|
||||
|
||||
```
|
||||
yarn docs:build
|
||||
```
|
||||
|
||||
To run the performance benchmarks in a browser (opening `./benchmarks/index.html`):
|
||||
|
||||
```
|
||||
yarn benchmark
|
||||
```
|
||||
|
||||
## Compile and build
|
||||
|
||||
To compile the source code to `dist`:
|
||||
|
||||
```
|
||||
yarn compile
|
||||
```
|
||||
|
||||
To create a UMD bundle of the library:
|
||||
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Pre-commit
|
||||
|
||||
To format and lint code before commit:
|
||||
|
||||
```
|
||||
yarn precommit
|
||||
```
|
||||
|
||||
To format and lint the entire project:
|
||||
|
||||
```
|
||||
yarn fmt
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
Please open an issue with a proposal for a new feature or refactoring before
|
||||
starting on the work. We don't want you to waste your efforts on a pull request
|
||||
that we won't want to accept.
|
||||
|
||||
## Pull requests
|
||||
|
||||
**Before submitting a pull request,** please make sure the following is done:
|
||||
|
||||
1. Fork the repository and create your branch from `master`.
|
||||
2. If you've added code that should be tested, add tests!
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Ensure the tests pass (`yarn test`).
|
||||
5. Lint and format your code (`yarn fmt && yarn lint`).
|
||||
|
||||
You can now submit a pull request, referencing any issues it addresses.
|
||||
|
||||
Please try to keep your pull request focused in scope and avoid including
|
||||
unrelated commits.
|
||||
|
||||
After you have submitted your pull request, we'll try to get back to you as
|
||||
soon as possible. We may suggest some changes or improvements.
|
||||
|
||||
Thank you for contributing!
|
||||
34
.github/ISSUE_TEMPLATE.md
vendored
34
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,30 +1,20 @@
|
||||
<!--
|
||||
React Native for Web is an implementation of React Native. If you have feature
|
||||
requests, you should post them to https://productpains.com/product/react-native/.
|
||||
|
||||
GitHub issues should only be used for bugs or Web-specific features you believe
|
||||
React Native requires.
|
||||
|
||||
Make sure to add ALL the information needed to understand the bug so that
|
||||
someone can help. If the info is missing we'll add the 'needs more information'
|
||||
label and close the issue until there is enough information.
|
||||
-->
|
||||
**Do you want to request a *feature* or report a *bug*?**
|
||||
|
||||
**What is the current behavior?**
|
||||
|
||||
Link to minimal test case: (template: [codepen](https://codepen.io/necolas/pen/PZzwBR?editors=0010))
|
||||
|
||||
**What is the expected behaviour?**
|
||||
|
||||
**Steps to reproduce**
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and
|
||||
if a minimal demo of the problem via Glitch or similar (template:
|
||||
https://glitch.com/edit/#!/react-native-web-playground).**
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
**Environment (include versions)**
|
||||
**What is the expected behavior?**
|
||||
|
||||
OS:
|
||||
Device:
|
||||
Browser:
|
||||
React Native for Web (version):
|
||||
React (version):
|
||||
**Environment (include versions). Did this work in previous versions?**
|
||||
|
||||
* OS:
|
||||
* Device:
|
||||
* Browser:
|
||||
* React Native for Web (version):
|
||||
* React (version):
|
||||
|
||||
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,17 +1 @@
|
||||
<!--
|
||||
Thanks for submitting a pull request! Make sure the PR does only one thing.
|
||||
Please provide enough information so that others can review your pull
|
||||
request. Make sure you have read the Contributing Guidelines -
|
||||
https://github.com/necolas/react-native-web/CONTRIBUTING.md
|
||||
-->
|
||||
|
||||
**This patch solves the following problem**
|
||||
|
||||
**Test plan**
|
||||
|
||||
**This pull request**
|
||||
|
||||
- [ ] includes documentation
|
||||
- [ ] includes tests
|
||||
- [ ] includes an interactive example
|
||||
- [ ] includes screenshots/videos
|
||||
**Before submitting a pull request,** please make sure you have followed the steps the CONTRIBUTING guide.
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
/dist
|
||||
/dist-examples
|
||||
/node_modules
|
||||
node_modules
|
||||
dist
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
We are open to, and grateful for, any contributions made by the community.
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
|
||||
issue hasn't already been reported.
|
||||
|
||||
## Development
|
||||
|
||||
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
|
||||
to find a list of open issues that need attention.
|
||||
|
||||
Fork, then clone the repo:
|
||||
|
||||
```
|
||||
git clone https://github.com/your-username/react-native-web.git
|
||||
```
|
||||
|
||||
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install):
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
Run the examples:
|
||||
|
||||
```
|
||||
npm run examples
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
To create a UMD build:
|
||||
|
||||
```
|
||||
npm run build:umd
|
||||
```
|
||||
|
||||
### Testing and Linting
|
||||
|
||||
To run the tests:
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
To continuously watch and run tests, run the following:
|
||||
|
||||
```
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
To perform linting, run the following:
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
Please open an issue with a proposal for a new feature or refactoring before
|
||||
starting on the work. We don't want you to waste your efforts on a pull request
|
||||
that we won't want to accept.
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
* Open a new issue in the [Issue tracker](https://github.com/necolas/react-native-web/issues).
|
||||
* Fork the repo.
|
||||
* Create a new feature branch based off the `master` branch.
|
||||
* Make sure all tests pass and there are no linting errors.
|
||||
* Submit a pull request, referencing any issues it addresses.
|
||||
|
||||
Please try to keep your pull request focused in scope and avoid including unrelated commits.
|
||||
|
||||
After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements.
|
||||
|
||||
Thank you for contributing!
|
||||
87
README.md
87
README.md
@@ -16,86 +16,45 @@ Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
|
||||
## Overview
|
||||
|
||||
"React Native for Web" is a project to bring React Native's building blocks and
|
||||
touch handling to the Web. [Read more](#why).
|
||||
touch handling to the Web.
|
||||
|
||||
Browse the UI Explorer to see React Native [examples running on
|
||||
Web](https://necolas.github.io/react-native-web/storybook/). Or try it out
|
||||
online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZzwBR).
|
||||
Browse the [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
|
||||
to see React Native examples running on Web. Or remix the [React Native for
|
||||
Web: Playground](https://glitch.com/edit/#!/react-native-web-playground) on
|
||||
Glitch.
|
||||
|
||||
## Quick start
|
||||
|
||||
To install in your app:
|
||||
NOTE: React Native for Web supports React/ReactDOM 15.4, 15.5, or 15.6.
|
||||
|
||||
Install in your existing app using `yarn` or `npm`:
|
||||
|
||||
```
|
||||
npm install --save react react-native-web
|
||||
yarn add react@15.6 react-dom@15.6 react-native-web
|
||||
```
|
||||
|
||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||
|
||||
Alternatively, you can quickly setup a local project
|
||||
using [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
(which supports `react-native-web` out-of-the-box once installed) and
|
||||
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter).
|
||||
Then read the [Getting Started](docs/guides/getting-started.md) guide.
|
||||
|
||||
## Documentation
|
||||
|
||||
The [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
|
||||
interactively documents all the APIs and Components.
|
||||
|
||||
Guides:
|
||||
|
||||
* [Getting started](docs/guides/getting-started.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
* [Accessibility](docs/guides/accessibility.md)
|
||||
* [Client and server rendering](docs/guides/rendering.md)
|
||||
* [Direct manipulation](docs/guides/direct-manipulation.md)
|
||||
* [Internationalization](docs/guides/internationalization.md)
|
||||
* [Advanced use](docs/guides/advanced.md)
|
||||
* [Known issues](docs/guides/known-issues.md)
|
||||
* [React Native](docs/guides/react-native.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
|
||||
Exported modules:
|
||||
## Starter kits
|
||||
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Image`](docs/components/Image.md)
|
||||
* [`ListView`](docs/components/ListView.md)
|
||||
* [`ProgressBar`](docs/components/ProgressBar.md)
|
||||
* [`ScrollView`](docs/components/ScrollView.md)
|
||||
* [`Switch`](docs/components/Switch.md)
|
||||
* [`Text`](docs/components/Text.md)
|
||||
* [`TextInput`](docs/components/TextInput.md)
|
||||
* [`TouchableHighlight`](http://facebook.github.io/react-native/releases/0.22/docs/touchablehighlight.html) (mirrors React Native)
|
||||
* [`TouchableOpacity`](http://facebook.github.io/react-native/releases/0.22/docs/touchableopacity.html) (mirrors React Native)
|
||||
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
|
||||
* [`View`](docs/components/View.md)
|
||||
* APIs
|
||||
* [`Animated`](http://facebook.github.io/react-native/releases/0.20/docs/animated.html) (mirrors React Native)
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
* [`NetInfo`](docs/apis/NetInfo.md)
|
||||
* [`PanResponder`](http://facebook.github.io/react-native/releases/0.20/docs/panresponder.html#content) (mirrors React Native)
|
||||
* [`PixelRatio`](docs/apis/PixelRatio.md)
|
||||
* [`Platform`](docs/apis/Platform.md)
|
||||
* [`StyleSheet`](docs/apis/StyleSheet.md)
|
||||
* [`Vibration`](docs/apis/Vibration.md)
|
||||
|
||||
<span id="#why"></span>
|
||||
## Why?
|
||||
|
||||
There are many different teams at Twitter building web applications with React.
|
||||
We want to share React components, libraries, and APIs between teams…much like
|
||||
the OSS community tries to do. At our scale, this involves dealing with
|
||||
multiple, inter-related problems including: a common way to handle style,
|
||||
animation, touch, viewport adaptation, accessibility, themes, RTL layout, and
|
||||
server-rendering.
|
||||
|
||||
This is hard to do with React DOM, as the components are essentially the same
|
||||
low-level building blocks that the browser provides. However, React Native
|
||||
avoids, solves, or can solve almost all these problems facing Web teams.
|
||||
Central to this is React Native's JavaScript style API (not strictly
|
||||
"CSS-in-JS") which avoids the key [problems with
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js) by giving up some of the
|
||||
complexity of CSS.
|
||||
* [create-react-app](https://github.com/facebookincubator/create-react-app) ([on Glitch](https://glitch.com/edit/#!/react-native-web-playground))
|
||||
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
|
||||
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
|
||||
|
||||
## Example code
|
||||
|
||||
@@ -138,10 +97,10 @@ AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-ro
|
||||
|
||||
## Related projects
|
||||
|
||||
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
|
||||
* [react-primitives](https://github.com/lelandrichardson/react-primitives/)
|
||||
* [react-sketchapp](https://github.com/airbnb/react-sketchapp)
|
||||
* [reactxp](https://github.com/microsoft/reactxp)
|
||||
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
|
||||
* [react-web](https://github.com/taobaofed/react-web)
|
||||
* [react-native-for-web](https://github.com/KodersLab/react-native-for-web)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
51
benchmarks/README.md
Normal file
51
benchmarks/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Performance
|
||||
|
||||
To run these benchmarks:
|
||||
|
||||
```
|
||||
npm run build:performance
|
||||
open ./performance/index.html
|
||||
```
|
||||
|
||||
Append `?fastest` to the URL to include the fastest "other libraries", and
|
||||
`?all` to include all the "other libraries".
|
||||
|
||||
## Notes
|
||||
|
||||
The components used in the render benchmarks are simple enough to be
|
||||
implemented by multiple UI or style libraries. The implementations are not
|
||||
equivalent in functionality. For example, React Native for Web's stylesheet is
|
||||
unique in that it also converts React Native styles to DOM styles, has
|
||||
deterministic resolution, and supports RTL layout.
|
||||
|
||||
`react-native-web/stylesheet` is a comparative baseline that implements a
|
||||
simple `View` without much of React Native's functionality.
|
||||
|
||||
## Benchmark results
|
||||
|
||||
Typical render timings*: mean ± two standard deviations.
|
||||
|
||||
| Implementation | Deep tree (ms) | Wide tree (ms) | Tweets (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `css-modules` | `84.19` `±14.69` | `183.37` `±22.98` | |
|
||||
| `react-native-web/stylesheet@0.0.113` | `88.83` `±14.31` | `185.54` `±24.62` | |
|
||||
| `react-native-web@0.0.113` | `110.45` `±19.63` | `251.53` `±32.52` | `15.52` `±7.93ms` |
|
||||
|
||||
Other libraries
|
||||
|
||||
| Implementation | Deep tree (ms) | Wide tree (ms) |
|
||||
| :--- | ---: | ---: |
|
||||
| `aphrodite@1.2.3` | `84.68` `±18.80` | `180.62` `±41.98` |
|
||||
| `styletron@2.5.1` | `83.93` `±13.10` | `185.96` `±45.65` |
|
||||
| `react-jss@7.0.1` | `174.75` `±30.87` | `411.77` `±83.83` |
|
||||
| `glamor@3.0.0-3` | `255.21` `±45.68` | `545.74` `±107.79` |
|
||||
| `reactxp@0.34.3` | `237.46` `±36.72` | `514.48` `±84.87` |
|
||||
| `styled-components@2.1.1` | `266.91` `±50.04` | `598.29` `±95.13` |
|
||||
| `styled-components/primitives@2.1.1` | `266.62` `±50.39` | `567.13` `±68.12` |
|
||||
| `radium@0.19.1` | `518.48` `±69.74` | `1058.85` `±120.85` |
|
||||
|
||||
These results indicate that styled render performance is not a significant
|
||||
differentiating factor between `aphrodite`, `css-modules`, `react-native-web`,
|
||||
and `styletron`.
|
||||
|
||||
*MacBook Pro (13-inch, Early 2015); 3.1 GHz Intel Core i7; 16 GB 1867 MHz DDR3. Google Chrome 58 (2x CPU slowdown).
|
||||
97
benchmarks/benchmark.js
Normal file
97
benchmarks/benchmark.js
Normal file
@@ -0,0 +1,97 @@
|
||||
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);
|
||||
};
|
||||
|
||||
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
|
||||
return new Promise(resolve => {
|
||||
const durations = [];
|
||||
let i = 0;
|
||||
|
||||
setup();
|
||||
const first = measure('first', task);
|
||||
teardown();
|
||||
|
||||
const done = () => {
|
||||
const stdDev = standardDeviation(durations);
|
||||
const formattedFirst = fmt(first);
|
||||
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(`First: ${formattedFirst}`);
|
||||
console.log(`Median: ${formattedMedian}`);
|
||||
console.log(`Mean: ${formattedMean}`);
|
||||
console.log(`Standard deviation: ${formattedStdDev}`);
|
||||
console.log(durations);
|
||||
console.groupEnd();
|
||||
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;
|
||||
24
benchmarks/createRenderBenchmark.js
Normal file
24
benchmarks/createRenderBenchmark.js
Normal file
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
11
benchmarks/index.html
Normal file
11
benchmarks/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Performance tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="root"></div>
|
||||
<script src="dist/performance.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
66
benchmarks/index.js
Normal file
66
benchmarks/index.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import aphrodite from './src/aphrodite';
|
||||
import cssModules from './src/css-modules';
|
||||
import glamor from './src/glamor';
|
||||
import jss from './src/jss';
|
||||
import radium from './src/radium';
|
||||
import reactNative from './src/react-native';
|
||||
import reactNativeStyleSheet from './src/react-native-stylesheet';
|
||||
import styledComponents from './src/styled-components';
|
||||
import styledComponentsPrimitives from './src/styled-components-primitives';
|
||||
import styletron from './src/styletron';
|
||||
import xp from './src/reactxp';
|
||||
|
||||
import renderDeepTree from './tests/renderDeepTree';
|
||||
import renderTweet from './tests/renderTweet';
|
||||
import renderWideTree from './tests/renderWideTree';
|
||||
|
||||
const testAll = window.location.search === '?all';
|
||||
const testFastest = window.location.search === '?fastest';
|
||||
|
||||
const coreTests = [
|
||||
() => renderTweet('react-native-web', reactNative),
|
||||
|
||||
() => renderDeepTree('css-modules', cssModules),
|
||||
() => renderWideTree('css-modules', cssModules),
|
||||
() => renderDeepTree('react-native-web/stylesheet', reactNativeStyleSheet),
|
||||
() => renderWideTree('react-native-web/stylesheet', reactNativeStyleSheet),
|
||||
() => renderDeepTree('react-native-web', reactNative),
|
||||
() => renderWideTree('react-native-web', reactNative)
|
||||
];
|
||||
|
||||
const fastestTests = [
|
||||
() => renderDeepTree('styletron', styletron),
|
||||
() => renderWideTree('styletron', styletron),
|
||||
() => renderDeepTree('aphrodite', aphrodite),
|
||||
() => renderWideTree('aphrodite', aphrodite)
|
||||
];
|
||||
|
||||
/**
|
||||
* Optionally run tests using other libraries
|
||||
*/
|
||||
const restTests = [
|
||||
() => renderDeepTree('glamor', glamor),
|
||||
() => renderWideTree('glamor', glamor),
|
||||
() => renderDeepTree('react-jss', jss),
|
||||
() => renderWideTree('react-jss', jss),
|
||||
() => renderDeepTree('radium', radium),
|
||||
() => renderWideTree('radium', radium),
|
||||
() => renderDeepTree('reactxp', xp),
|
||||
() => renderWideTree('reactxp', xp),
|
||||
() => renderDeepTree('styled-components', styledComponents),
|
||||
() => renderWideTree('styled-components', styledComponents),
|
||||
() => renderDeepTree('styled-components/primitives', styledComponentsPrimitives),
|
||||
() => renderWideTree('styled-components/primitives', styledComponentsPrimitives)
|
||||
];
|
||||
|
||||
const tests = [...coreTests];
|
||||
if (testFastest) {
|
||||
tests.push(...fastestTests);
|
||||
}
|
||||
if (testAll) {
|
||||
tests.push(...fastestTests);
|
||||
tests.push(...restTests);
|
||||
}
|
||||
|
||||
// run benchmarks
|
||||
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());
|
||||
22
benchmarks/package.json
Normal file
22
benchmarks/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "benchmarks",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"aphrodite": "^1.2.3",
|
||||
"classnames": "^2.2.5",
|
||||
"glamor": "^2.20.37",
|
||||
"marky": "^1.2.0",
|
||||
"radium": "^0.19.1",
|
||||
"react-jss": "^7.0.1",
|
||||
"react-primitives": "^0.4.3",
|
||||
"reactxp": "0.34.3",
|
||||
"styled-components": "^2.1.1",
|
||||
"styletron-client": "^2.5.7",
|
||||
"styletron-utils": "^2.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^0.28.4",
|
||||
"react-addons-perf": "^15.4.2",
|
||||
"style-loader": "^0.18.2"
|
||||
}
|
||||
}
|
||||
7
benchmarks/src/aphrodite.js
Normal file
7
benchmarks/src/aphrodite.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/aphrodite';
|
||||
import View from './components/View/aphrodite';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
108
benchmarks/src/components/AppText/index.js
Normal file
108
benchmarks/src/components/AppText/index.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import theme from '../theme';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
|
||||
class AppText extends PureComponent {
|
||||
static displayName = 'AppText';
|
||||
|
||||
static propTypes = {
|
||||
align: PropTypes.oneOf(['center', 'left', 'right']),
|
||||
color: PropTypes.oneOf(['blue', 'deepGray', 'normal', 'red', 'white']),
|
||||
fontStyle: PropTypes.oneOf(['normal', 'italic']),
|
||||
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
||||
uppercase: PropTypes.bool,
|
||||
weight: PropTypes.oneOf(['normal', 'bold'])
|
||||
};
|
||||
|
||||
render() {
|
||||
const { align, color, fontStyle, size, uppercase, weight, ...other } = this.props;
|
||||
|
||||
const style = [
|
||||
styles.root,
|
||||
align && alignStyles[align],
|
||||
color && colorStyles[color],
|
||||
fontStyle && fontStyles[fontStyle],
|
||||
size && sizeStyles[size],
|
||||
weight && weightStyles[weight],
|
||||
uppercase === true && styles.uppercase
|
||||
];
|
||||
|
||||
return <Text {...other} style={style} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize.normal,
|
||||
fontWeight: 'normal',
|
||||
lineHeight: theme.createLength(theme.lineHeight),
|
||||
wordWrap: 'break-word'
|
||||
},
|
||||
uppercase: {
|
||||
textTransform: 'uppercase'
|
||||
}
|
||||
});
|
||||
|
||||
const alignStyles = StyleSheet.create({
|
||||
center: {
|
||||
textAlign: 'center'
|
||||
},
|
||||
left: {
|
||||
textAlign: 'left'
|
||||
},
|
||||
right: {
|
||||
textAlign: 'right'
|
||||
}
|
||||
});
|
||||
|
||||
const colorStyles = StyleSheet.create({
|
||||
blue: {
|
||||
color: theme.colors.blue
|
||||
},
|
||||
deepGray: {
|
||||
color: theme.colors.deepGray
|
||||
},
|
||||
normal: {
|
||||
color: theme.colors.textBlack
|
||||
},
|
||||
red: {
|
||||
color: theme.colors.red
|
||||
},
|
||||
white: {
|
||||
color: theme.colors.white
|
||||
}
|
||||
});
|
||||
|
||||
const fontStyles = StyleSheet.create({
|
||||
normal: {
|
||||
fontStyle: 'normal'
|
||||
},
|
||||
italic: {
|
||||
fontStyle: 'italic'
|
||||
}
|
||||
});
|
||||
|
||||
const sizeStyles = StyleSheet.create({
|
||||
small: {
|
||||
fontSize: theme.fontSize.small
|
||||
},
|
||||
normal: {
|
||||
fontSize: theme.fontSize.normal
|
||||
},
|
||||
large: {
|
||||
fontSize: theme.fontSize.large
|
||||
}
|
||||
});
|
||||
|
||||
const weightStyles = StyleSheet.create({
|
||||
normal: {
|
||||
fontWeight: '400'
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
});
|
||||
|
||||
export default AppText;
|
||||
43
benchmarks/src/components/AspectRatio/index.js
Normal file
43
benchmarks/src/components/AspectRatio/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
class AspectRatio extends PureComponent {
|
||||
static displayName = 'AspectRatio';
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
ratio: PropTypes.number,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
ratio: 1
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, ratio, style } = this.props;
|
||||
const percentage = 100 / ratio;
|
||||
|
||||
return (
|
||||
<View style={[styles.root, style]}>
|
||||
<View style={[styles.shim, { paddingBottom: `${percentage}%` }]} />
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
shim: {
|
||||
display: 'block',
|
||||
width: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
export default AspectRatio;
|
||||
48
benchmarks/src/components/Box/aphrodite.js
Normal file
48
benchmarks/src/components/Box/aphrodite.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import View from '../View/aphrodite';
|
||||
import { StyleSheet } from 'aphrodite';
|
||||
|
||||
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 = StyleSheet.create({
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
});
|
||||
|
||||
export default Box;
|
||||
17
benchmarks/src/components/Box/css-modules.js
Normal file
17
benchmarks/src/components/Box/css-modules.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import View from '../View/css-modules';
|
||||
import styles from './styles.css';
|
||||
|
||||
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) =>
|
||||
<View
|
||||
{...other}
|
||||
className={classnames(styles[`color${color}`], {
|
||||
[styles.fixed]: fixed,
|
||||
[styles.outer]: outer,
|
||||
[styles.row]: layout === 'row'
|
||||
})}
|
||||
/>;
|
||||
|
||||
export default Box;
|
||||
47
benchmarks/src/components/Box/glamor.js
Normal file
47
benchmarks/src/components/Box/glamor.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import View from '../View/glamor';
|
||||
|
||||
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: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
};
|
||||
|
||||
export default Box;
|
||||
49
benchmarks/src/components/Box/jss.js
Normal file
49
benchmarks/src/components/Box/jss.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import injectSheet from 'react-jss';
|
||||
import React from 'react';
|
||||
import View from '../View/jss';
|
||||
|
||||
const Box = ({ classes, color, fixed = false, layout = 'column', outer = false, ...other }) =>
|
||||
<View
|
||||
{...other}
|
||||
className={classnames({
|
||||
[classes[`color${color}`]]: true,
|
||||
[classes.fixed]: fixed,
|
||||
[classes.row]: layout === 'row',
|
||||
[classes.outer]: outer
|
||||
})}
|
||||
/>;
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
};
|
||||
|
||||
export default injectSheet(styles)(Box);
|
||||
48
benchmarks/src/components/Box/radium.js
Normal file
48
benchmarks/src/components/Box/radium.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import Radium from 'radium';
|
||||
import React from 'react';
|
||||
import View from '../View/radium';
|
||||
|
||||
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: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
};
|
||||
|
||||
export default Radium(Box);
|
||||
48
benchmarks/src/components/Box/react-native-stylesheet.js
vendored
Normal file
48
benchmarks/src/components/Box/react-native-stylesheet.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import StyleSheet from 'react-native/apis/StyleSheet';
|
||||
import View from '../View/react-native-stylesheet';
|
||||
|
||||
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 = StyleSheet.create({
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
});
|
||||
|
||||
export default Box;
|
||||
47
benchmarks/src/components/Box/react-native.js
vendored
Normal file
47
benchmarks/src/components/Box/react-native.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
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 = StyleSheet.create({
|
||||
outer: {
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#222'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#666'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#999'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: 'orange'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
fixed: {
|
||||
width: 20,
|
||||
height: 20
|
||||
}
|
||||
});
|
||||
|
||||
export default Box;
|
||||
47
benchmarks/src/components/Box/reactxp.js
Normal file
47
benchmarks/src/components/Box/reactxp.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { Styles, View } from 'reactxp';
|
||||
|
||||
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: Styles.createViewStyle({
|
||||
padding: 4
|
||||
}),
|
||||
row: Styles.createViewStyle({
|
||||
flexDirection: 'row'
|
||||
}),
|
||||
color0: Styles.createViewStyle({
|
||||
backgroundColor: '#222'
|
||||
}),
|
||||
color1: Styles.createViewStyle({
|
||||
backgroundColor: '#666'
|
||||
}),
|
||||
color2: Styles.createViewStyle({
|
||||
backgroundColor: '#999'
|
||||
}),
|
||||
color3: Styles.createViewStyle({
|
||||
backgroundColor: 'blue'
|
||||
}),
|
||||
color4: Styles.createViewStyle({
|
||||
backgroundColor: 'orange'
|
||||
}),
|
||||
color5: Styles.createViewStyle({
|
||||
backgroundColor: 'red'
|
||||
}),
|
||||
fixed: Styles.createViewStyle({
|
||||
width: 20,
|
||||
height: 20
|
||||
})
|
||||
};
|
||||
|
||||
export default Box;
|
||||
@@ -0,0 +1,30 @@
|
||||
import styled from 'styled-components/primitives';
|
||||
|
||||
const getColor = color => {
|
||||
switch (color) {
|
||||
case 0:
|
||||
return '#222';
|
||||
case 1:
|
||||
return '#666';
|
||||
case 2:
|
||||
return '#999';
|
||||
case 3:
|
||||
return 'blue';
|
||||
case 4:
|
||||
return 'orange';
|
||||
case 5:
|
||||
return 'red';
|
||||
default:
|
||||
return 'transparent';
|
||||
}
|
||||
};
|
||||
|
||||
const Box = styled.View`
|
||||
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)};
|
||||
`;
|
||||
|
||||
export default Box;
|
||||
31
benchmarks/src/components/Box/styled-components.js
Normal file
31
benchmarks/src/components/Box/styled-components.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import styled from 'styled-components';
|
||||
import View from '../View/styled-components';
|
||||
|
||||
const getColor = color => {
|
||||
switch (color) {
|
||||
case 0:
|
||||
return '#222';
|
||||
case 1:
|
||||
return '#666';
|
||||
case 2:
|
||||
return '#999';
|
||||
case 3:
|
||||
return 'blue';
|
||||
case 4:
|
||||
return 'orange';
|
||||
case 5:
|
||||
return 'red';
|
||||
default:
|
||||
return 'transparent';
|
||||
}
|
||||
};
|
||||
|
||||
const Box = styled(View)`
|
||||
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)};
|
||||
`;
|
||||
|
||||
export default Box;
|
||||
36
benchmarks/src/components/Box/styles.css
Normal file
36
benchmarks/src/components/Box/styles.css
Normal file
@@ -0,0 +1,36 @@
|
||||
.outer {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.color0 {
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.color1 {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
.color2 {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.color3 {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
.color4 {
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.color5 {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
48
benchmarks/src/components/Box/styletron.js
Normal file
48
benchmarks/src/components/Box/styletron.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { injectStylePrefixed } from 'styletron-utils';
|
||||
import React from 'react';
|
||||
import View, { styletron } from '../View/styletron';
|
||||
|
||||
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;
|
||||
53
benchmarks/src/components/GridView/index.js
Normal file
53
benchmarks/src/components/GridView/index.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import theme from '../theme';
|
||||
|
||||
class GridView extends Component {
|
||||
static displayName = 'GridView';
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
hasGap: PropTypes.bool,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, hasGap, style, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<View {...other} style={[style, styles.root, hasGap && styles.hasGap]}>
|
||||
{React.Children.map(children, child => {
|
||||
return (
|
||||
child &&
|
||||
React.cloneElement(child, {
|
||||
style: [child.props.style, styles.column, hasGap && styles.hasGapColumn]
|
||||
})
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
/**
|
||||
* 1. Distribute all space (rather than extra space)
|
||||
* 2. Prevent wide content from forcing wider flex columns
|
||||
*/
|
||||
column: {
|
||||
flexBasis: 0, // 1
|
||||
minWidth: 0 // 2
|
||||
},
|
||||
hasGap: {
|
||||
marginHorizontal: theme.createLength(theme.spaceX * -0.5, 'rem')
|
||||
},
|
||||
hasGapColumn: {
|
||||
marginHorizontal: theme.createLength(theme.spaceX * 0.5, 'rem')
|
||||
}
|
||||
});
|
||||
|
||||
export default GridView;
|
||||
19
benchmarks/src/components/Icons/DirectMessage.js
Normal file
19
benchmarks/src/components/Icons/DirectMessage.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createDOMElement } from 'react-native';
|
||||
import React from 'react';
|
||||
import styles from './styles';
|
||||
|
||||
const IconDirectMessage = props =>
|
||||
createDOMElement('svg', {
|
||||
children: (
|
||||
<g>
|
||||
<path d="M43.34 14H12.66L28 27.946z" />
|
||||
<path d="M51.392 14.789L30.018 34.22c-.009.008-.028.006-.039.012-.563.5-1.266.768-1.98.768-.72 0-1.442-.258-2.017-.78L4.609 14.79A3.957 3.957 0 0 0 3 18v37a1.998 1.998 0 0 0 2 2c.464 0 .924-.162 1.292-.473L19 46h30c2.243 0 4-1.757 4-4V18a3.96 3.96 0 0 0-1.608-3.211z" />
|
||||
</g>
|
||||
),
|
||||
style: [styles.icon, props.style],
|
||||
viewBox: '0 0 56 72'
|
||||
});
|
||||
|
||||
IconDirectMessage.metadata = { height: 72, width: 56 };
|
||||
|
||||
export default IconDirectMessage;
|
||||
18
benchmarks/src/components/Icons/Heart.js
Normal file
18
benchmarks/src/components/Icons/Heart.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createDOMElement } from 'react-native';
|
||||
import React from 'react';
|
||||
import styles from './styles';
|
||||
|
||||
const IconHeart = props =>
|
||||
createDOMElement('svg', {
|
||||
children: (
|
||||
<g>
|
||||
<path d="M38.723 12c-7.187 0-11.16 7.306-11.723 8.131C26.437 19.306 22.504 12 15.277 12 8.791 12 3.533 18.163 3.533 24.647 3.533 39.964 21.891 55.907 27 56c5.109-.093 23.467-16.036 23.467-31.353C50.467 18.163 45.209 12 38.723 12z" />
|
||||
</g>
|
||||
),
|
||||
style: [styles.icon, props.style],
|
||||
viewBox: '0 0 54 72'
|
||||
});
|
||||
|
||||
IconHeart.metadata = { height: 72, width: 54 };
|
||||
|
||||
export default IconHeart;
|
||||
18
benchmarks/src/components/Icons/Reply.js
Normal file
18
benchmarks/src/components/Icons/Reply.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createDOMElement } from 'react-native';
|
||||
import React from 'react';
|
||||
import styles from './styles';
|
||||
|
||||
const IconReply = props =>
|
||||
createDOMElement('svg', {
|
||||
children: (
|
||||
<g>
|
||||
<path d="M41 31h-9V19a2.999 2.999 0 0 0-4.817-2.386l-21 16a3 3 0 0 0-.001 4.773l21 16a3.006 3.006 0 0 0 3.15.301A2.997 2.997 0 0 0 32 51V39h9c5.514 0 10 4.486 10 10a4 4 0 0 0 8 0c0-9.925-8.075-18-18-18z" />
|
||||
</g>
|
||||
),
|
||||
style: [styles.icon, props.style],
|
||||
viewBox: '0 0 62 72'
|
||||
});
|
||||
|
||||
IconReply.metadata = { height: 72, width: 62 };
|
||||
|
||||
export default IconReply;
|
||||
18
benchmarks/src/components/Icons/Retweet.js
Normal file
18
benchmarks/src/components/Icons/Retweet.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createDOMElement } from 'react-native';
|
||||
import React from 'react';
|
||||
import styles from './styles';
|
||||
|
||||
const IconRetweet = props =>
|
||||
createDOMElement('svg', {
|
||||
children: (
|
||||
<g>
|
||||
<path d="M70.676 36.644A3 3 0 0 0 68 35h-7V19a4 4 0 0 0-4-4H34a4 4 0 0 0 0 8h18a1 1 0 0 1 1 .998V35h-7a3.001 3.001 0 0 0-2.419 4.775l11 15a3.003 3.003 0 0 0 4.839-.001l11-15a3.001 3.001 0 0 0 .256-3.13zM40.001 48H22a.995.995 0 0 1-.992-.96L21.001 36h7a3.001 3.001 0 0 0 2.419-4.775l-11-15a3.003 3.003 0 0 0-4.839.001l-11 15A3 3 0 0 0 6.001 36h7l.011 16.003a4 4 0 0 0 4 3.997h22.989a4 4 0 0 0 0-8z" />
|
||||
</g>
|
||||
),
|
||||
style: [styles.icon, props.style],
|
||||
viewBox: '0 0 74 72'
|
||||
});
|
||||
|
||||
IconRetweet.metadata = { height: 72, width: 74 };
|
||||
|
||||
export default IconRetweet;
|
||||
15
benchmarks/src/components/Icons/styles.js
Normal file
15
benchmarks/src/components/Icons/styles.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
icon: {
|
||||
display: 'inline-block',
|
||||
fill: 'currentcolor',
|
||||
height: '1.25em',
|
||||
maxWidth: '100%',
|
||||
position: 'relative',
|
||||
userSelect: 'none',
|
||||
verticalAlign: 'text-bottom'
|
||||
}
|
||||
});
|
||||
|
||||
export default styles;
|
||||
44
benchmarks/src/components/NestedTree/index.js
Normal file
44
benchmarks/src/components/NestedTree/index.js
Normal file
@@ -0,0 +1,44 @@
|
||||
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
|
||||
};
|
||||
|
||||
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;
|
||||
144
benchmarks/src/components/Tweet/index.js
Normal file
144
benchmarks/src/components/Tweet/index.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import AspectRatio from '../AspectRatio';
|
||||
import GridView from '../GridView';
|
||||
import PropTypes from 'prop-types';
|
||||
import TweetActionsBar from '../TweetActionsBar';
|
||||
import TweetText from '../TweetText';
|
||||
import UserAvatar from '../UserAvatar';
|
||||
import UserNames from '../UserNames';
|
||||
import { Image, StyleSheet, Text, View } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import theme from '../theme';
|
||||
|
||||
export class Tweet extends Component {
|
||||
static displayName = 'Tweet';
|
||||
|
||||
static propTypes = {
|
||||
tweet: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { tweet } = this.props;
|
||||
const { id, lang, media, textParts, timestamp, user } = tweet;
|
||||
const { fullName, profileImageUrl, screenName } = user;
|
||||
|
||||
return (
|
||||
<View accessibilityRole="article" accessible style={styles.root}>
|
||||
<GridView hasGap>
|
||||
<View style={styles.avatarColumn}>
|
||||
<View
|
||||
accessibilityRole="link"
|
||||
accessible
|
||||
href={`/${screenName}`}
|
||||
style={styles.avatarLink}
|
||||
>
|
||||
<UserAvatar style={styles.avatar} uri={profileImageUrl} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.bodyColumn}>
|
||||
<View style={styles.body}>
|
||||
<View style={styles.row}>
|
||||
<Text
|
||||
accessibilityRole="link"
|
||||
children={timestamp}
|
||||
href={`/${screenName}/status/${id}`}
|
||||
style={styles.timestamp}
|
||||
/>
|
||||
<UserNames fullName={fullName} screenName={screenName} />
|
||||
</View>
|
||||
|
||||
<View accessibilityRole="heading" aria-level="4">
|
||||
<TweetText displayMode={'links'} lang={lang} textParts={textParts} />
|
||||
</View>
|
||||
|
||||
{media
|
||||
? <View style={styles.richContent}>
|
||||
<AspectRatio ratio={16 / 9}>
|
||||
<Image
|
||||
resizeMode={Image.resizeMode.cover}
|
||||
source={media.source}
|
||||
style={styles.media}
|
||||
/>
|
||||
</AspectRatio>
|
||||
</View>
|
||||
: null}
|
||||
</View>
|
||||
|
||||
<TweetActionsBar
|
||||
actions={[
|
||||
{ name: 'reply', label: 'Reply' },
|
||||
{
|
||||
name: 'retweet',
|
||||
label: 'Retweet',
|
||||
count: tweet.retweet_count,
|
||||
highlighted: tweet.retweeted
|
||||
},
|
||||
{
|
||||
name: 'like',
|
||||
label: 'Like',
|
||||
count: tweet.favorite_count,
|
||||
highlighted: tweet.favorited
|
||||
},
|
||||
{ name: 'directMessage', label: 'Direct Message' }
|
||||
]}
|
||||
style={styles.actionBar}
|
||||
/>
|
||||
</View>
|
||||
</GridView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
paddingVertical: theme.createLength(theme.spaceY * 0.75, 'rem'),
|
||||
paddingHorizontal: theme.createLength(theme.spaceX, 'rem')
|
||||
},
|
||||
avatarColumn: {
|
||||
flexGrow: 1,
|
||||
minWidth: 32
|
||||
},
|
||||
bodyColumn: {
|
||||
flexGrow: 7
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
avatarLink: {
|
||||
display: 'block',
|
||||
flexShrink: 1,
|
||||
flexGrow: 0,
|
||||
width: '100%'
|
||||
},
|
||||
avatar: {
|
||||
width: '100%'
|
||||
},
|
||||
body: {
|
||||
marginTop: '-0.15rem'
|
||||
},
|
||||
timestamp: {
|
||||
color: theme.colors.deepGray,
|
||||
marginLeft: theme.createLength(theme.spaceX, 'rem'),
|
||||
order: 1,
|
||||
textDecorationLine: 'none',
|
||||
whiteSpace: 'nowrap'
|
||||
},
|
||||
actionBar: {
|
||||
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem')
|
||||
},
|
||||
richContent: {
|
||||
borderRadius: '0.35rem',
|
||||
marginTop: theme.createLength(theme.spaceY * 0.5, 'rem'),
|
||||
overflow: 'hidden'
|
||||
},
|
||||
media: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
margin: 'auto',
|
||||
width: 'auto',
|
||||
height: 'auto'
|
||||
}
|
||||
});
|
||||
|
||||
export default Tweet;
|
||||
82
benchmarks/src/components/TweetAction/index.js
Normal file
82
benchmarks/src/components/TweetAction/index.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import IconReply from '../Icons/Reply';
|
||||
import IconHeart from '../Icons/Heart';
|
||||
import IconRetweet from '../Icons/Retweet';
|
||||
import IconDirectMessage from '../Icons/DirectMessage';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import theme from '../theme';
|
||||
import { Text, View, StyleSheet } from 'react-native';
|
||||
|
||||
const getIcon = (icon, highlighted) => {
|
||||
switch (icon) {
|
||||
case 'like':
|
||||
return <IconHeart />;
|
||||
case 'reply':
|
||||
return <IconReply />;
|
||||
case 'retweet':
|
||||
return <IconRetweet />;
|
||||
case 'directMessage':
|
||||
return <IconDirectMessage />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default class TweetAction extends React.Component {
|
||||
static displayName = 'TweetAction';
|
||||
|
||||
static propTypes = {
|
||||
count: PropTypes.number,
|
||||
displayMode: PropTypes.oneOf(['like', 'reply', 'retweet', 'directMessage']),
|
||||
highlighted: PropTypes.bool,
|
||||
onPress: PropTypes.func,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
render() {
|
||||
const { count, displayMode, highlighted, onPress, style } = this.props;
|
||||
|
||||
return (
|
||||
<View accessibilityRole="button" onPress={onPress} style={[styles.root, style]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.inner,
|
||||
displayMode === 'like' && highlighted && styles.likedColor,
|
||||
displayMode === 'retweet' && highlighted && styles.retweetedColor
|
||||
]}
|
||||
>
|
||||
{getIcon(displayMode, highlighted)}
|
||||
{count > 0
|
||||
? <Text style={styles.count}>
|
||||
{count}
|
||||
</Text>
|
||||
: null}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
minHeight: theme.createLength(theme.lineHeight, 'rem'),
|
||||
overflow: 'visible',
|
||||
userSelect: 'none',
|
||||
whiteSpace: 'nowrap'
|
||||
},
|
||||
inner: {
|
||||
alignItems: 'center',
|
||||
color: theme.colors.deepGray,
|
||||
display: 'flex',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
count: {
|
||||
marginLeft: '0.25em'
|
||||
},
|
||||
retweetedColor: {
|
||||
color: theme.colors.green
|
||||
},
|
||||
likedColor: {
|
||||
color: theme.colors.red
|
||||
}
|
||||
});
|
||||
52
benchmarks/src/components/TweetActionsBar/index.js
Normal file
52
benchmarks/src/components/TweetActionsBar/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import TweetAction from '../TweetAction';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
const actionNames = ['reply', 'retweet', 'like', 'directMessage'];
|
||||
|
||||
export default class TweetActionsBar extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
count: PropTypes.number,
|
||||
label: PropTypes.string,
|
||||
highlighted: PropTypes.bool,
|
||||
name: PropTypes.oneOf(actionNames).isRequired,
|
||||
onPress: PropTypes.func
|
||||
})
|
||||
),
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
render() {
|
||||
const { actions, style } = this.props;
|
||||
|
||||
/* eslint-disable react/jsx-handler-names */
|
||||
return (
|
||||
<View style={[styles.root, style]}>
|
||||
{actions.map((action, i) =>
|
||||
<TweetAction
|
||||
accessibilityLabel={actions.label}
|
||||
count={action.count}
|
||||
displayMode={action.name}
|
||||
highlighted={action.highlighted}
|
||||
key={i}
|
||||
onPress={action.onPress}
|
||||
style={styles.action}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
action: {
|
||||
display: 'block',
|
||||
marginRight: '10%'
|
||||
}
|
||||
});
|
||||
29
benchmarks/src/components/TweetText/index.js
Normal file
29
benchmarks/src/components/TweetText/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import AppText from '../AppText';
|
||||
import React from 'react';
|
||||
import TweetTextPart from '../TweetTextPart';
|
||||
import { array, number, string } from 'prop-types';
|
||||
|
||||
class TweetText extends React.Component {
|
||||
static displayName = 'TweetText';
|
||||
|
||||
static propTypes = {
|
||||
displayMode: TweetTextPart.propTypes.displayMode,
|
||||
lang: string,
|
||||
numberOfLines: number,
|
||||
textParts: array.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { displayMode, lang, numberOfLines, textParts, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<AppText {...other} lang={lang} numberOfLines={numberOfLines}>
|
||||
{textParts.map((part, i) =>
|
||||
<TweetTextPart displayMode={displayMode} key={i} part={part} />
|
||||
)}
|
||||
</AppText>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TweetText;
|
||||
112
benchmarks/src/components/TweetTextPart/index.js
Normal file
112
benchmarks/src/components/TweetTextPart/index.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { Image, StyleSheet, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import theme from '../theme';
|
||||
|
||||
const createTextEntity = ({ part }) => <Text>{`${part.prefix}${part.text}`}</Text>;
|
||||
|
||||
const createTwemojiEntity = ({ part }) =>
|
||||
<Image
|
||||
accessibilityLabel={part.text}
|
||||
draggable={false}
|
||||
source={{ uri: part.emoji }}
|
||||
style={styles.twemoji}
|
||||
/>;
|
||||
|
||||
// @mention, #hashtag, $cashtag
|
||||
const createSymbolEntity = ({ displayMode, part }) => {
|
||||
const links = displayMode === 'links';
|
||||
return (
|
||||
<Text accessibilityRole={links ? 'link' : null} href={part.url} style={[links && styles.link]}>
|
||||
{`${part.prefix}${part.text}`}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
// internal links
|
||||
const createLinkEntity = ({ displayMode, part }) => {
|
||||
const { displayUrl, linkRelation, url } = part;
|
||||
const links = displayMode === 'links';
|
||||
|
||||
return (
|
||||
<Text
|
||||
accessibilityRole={links ? 'link' : null}
|
||||
href={url}
|
||||
rel={links ? linkRelation : null}
|
||||
style={[links && styles.link]}
|
||||
>
|
||||
{displayUrl}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
// external links
|
||||
const createExternalLinkEntity = ({ displayMode, part }) => {
|
||||
const { displayUrl, linkRelation, url } = part;
|
||||
const links = displayMode === 'links';
|
||||
|
||||
return (
|
||||
<Text
|
||||
accessibilityRole={links ? 'link' : null}
|
||||
href={url}
|
||||
rel={links ? linkRelation : null}
|
||||
style={[links && styles.link]}
|
||||
target="_blank"
|
||||
>
|
||||
{displayUrl}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
class TweetTextPart extends React.Component {
|
||||
static displayName = 'TweetTextPart';
|
||||
|
||||
static propTypes = {
|
||||
displayMode: PropTypes.oneOf(['links', 'no-links']),
|
||||
part: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
displayMode: 'links'
|
||||
};
|
||||
|
||||
render() {
|
||||
let renderer;
|
||||
const { isEmoji, isEntity, isHashtag, isMention, isMedia, isUrl } = this.props.part;
|
||||
|
||||
if (isEmoji || isEntity || isUrl || isMedia) {
|
||||
if (isUrl) {
|
||||
renderer = createExternalLinkEntity;
|
||||
} else if (isHashtag || isMention) {
|
||||
renderer = createSymbolEntity;
|
||||
} else if (isEmoji) {
|
||||
renderer = createTwemojiEntity;
|
||||
} else {
|
||||
renderer = createLinkEntity;
|
||||
}
|
||||
} else {
|
||||
renderer = createTextEntity;
|
||||
}
|
||||
|
||||
return renderer(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
link: {
|
||||
color: theme.colors.blue,
|
||||
textDecorationLine: 'none',
|
||||
unicodeBidi: 'embed'
|
||||
},
|
||||
twemoji: {
|
||||
display: 'inline-block',
|
||||
height: '1.25em',
|
||||
width: '1.25em',
|
||||
paddingRight: '0.05em',
|
||||
paddingLeft: '0.1em',
|
||||
verticalAlign: '-0.2em'
|
||||
}
|
||||
});
|
||||
|
||||
export default TweetTextPart;
|
||||
65
benchmarks/src/components/UserAvatar/index.js
Normal file
65
benchmarks/src/components/UserAvatar/index.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import AspectRatio from '../AspectRatio';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Image, StyleSheet } from 'react-native';
|
||||
import React, { PureComponent } from 'react';
|
||||
import theme from '../theme';
|
||||
|
||||
class UserAvatar extends PureComponent {
|
||||
static displayName = 'UserAvatar';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: PropTypes.string,
|
||||
circle: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
uri: PropTypes.string
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
circle: false
|
||||
};
|
||||
|
||||
render() {
|
||||
const { accessibilityLabel, circle, style, uri } = this.props;
|
||||
|
||||
return (
|
||||
<AspectRatio ratio={1} style={[styles.root, style]}>
|
||||
{uri
|
||||
? <Image
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
onLoad={this._handleLoad}
|
||||
ref={this._setImageRef}
|
||||
source={{ uri }}
|
||||
style={[styles.image, circle && styles.circle]}
|
||||
/>
|
||||
: null}
|
||||
</AspectRatio>
|
||||
);
|
||||
}
|
||||
|
||||
_handleLoad = () => {
|
||||
this._imageRef && this._imageRef.setNativeProps(nativeProps);
|
||||
};
|
||||
|
||||
_setImageRef = component => {
|
||||
this._imageRef = component;
|
||||
};
|
||||
}
|
||||
|
||||
const nativeProps = { style: { backgroundColor: '#fff' } };
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderRadius: '0.35rem'
|
||||
},
|
||||
circle: {
|
||||
borderRadius: '9999px'
|
||||
},
|
||||
image: {
|
||||
backgroundColor: theme.colors.fadedGray,
|
||||
display: 'block',
|
||||
height: '100%',
|
||||
width: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
export default UserAvatar;
|
||||
52
benchmarks/src/components/UserNames/index.js
Normal file
52
benchmarks/src/components/UserNames/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import AppText from '../AppText';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
class UserNames extends PureComponent {
|
||||
static displayName = 'UserNames';
|
||||
|
||||
static propTypes = {
|
||||
fullName: PropTypes.string,
|
||||
layout: PropTypes.oneOf(['nowrap', 'stack']),
|
||||
onPress: PropTypes.func,
|
||||
screenName: PropTypes.string,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
layout: 'nowrap'
|
||||
};
|
||||
|
||||
render() {
|
||||
const { fullName, layout, onPress, screenName, style, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<AppText
|
||||
{...other}
|
||||
color="deepGray"
|
||||
numberOfLines={layout === 'nowrap' ? 1 : null}
|
||||
onPress={onPress}
|
||||
style={[styles.root, style]}
|
||||
>
|
||||
<AppText color="normal" weight="bold">
|
||||
{fullName}
|
||||
</AppText>
|
||||
{layout === 'stack' ? ' \u000A' : ' '}
|
||||
<AppText color="deepGray" style={styles.screenName}>{`@${screenName}`}</AppText>
|
||||
</AppText>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
display: 'inline-block'
|
||||
},
|
||||
screenName: {
|
||||
unicodeBidi: 'embed',
|
||||
writingDirection: 'ltr'
|
||||
}
|
||||
});
|
||||
|
||||
export default UserNames;
|
||||
31
benchmarks/src/components/View/aphrodite.js
Normal file
31
benchmarks/src/components/View/aphrodite.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { css, StyleSheet } from 'aphrodite';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
return <div {...other} className={css(styles.root, style)} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
}
|
||||
});
|
||||
|
||||
export default View;
|
||||
13
benchmarks/src/components/View/css-modules.js
Normal file
13
benchmarks/src/components/View/css-modules.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const props = this.props;
|
||||
return <div {...props} className={classnames(styles.initial, props.className)} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default View;
|
||||
29
benchmarks/src/components/View/glamor.js
Normal file
29
benchmarks/src/components/View/glamor.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import { css } from 'glamor';
|
||||
import React from 'react';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
return <div {...other} className={css(viewStyle, ...style)} />;
|
||||
}
|
||||
}
|
||||
|
||||
const viewStyle = {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
};
|
||||
|
||||
export default View;
|
||||
32
benchmarks/src/components/View/jss.js
Normal file
32
benchmarks/src/components/View/jss.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import injectSheet from 'react-jss';
|
||||
import React from 'react';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { classes, className, ...other } = this.props;
|
||||
return <div {...other} className={classnames(classes.root, className)} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
root: {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default injectSheet(styles)(View);
|
||||
31
benchmarks/src/components/View/radium.js
Normal file
31
benchmarks/src/components/View/radium.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import Radium from 'radium';
|
||||
import React from 'react';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
return <div {...other} style={[styles.root, style]} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
root: {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
}
|
||||
};
|
||||
|
||||
export default Radium(View);
|
||||
35
benchmarks/src/components/View/react-native-stylesheet.js
vendored
Normal file
35
benchmarks/src/components/View/react-native-stylesheet.js
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import StyleSheet from 'react-native/apis/StyleSheet';
|
||||
import registry from 'react-native/apis/StyleSheet/registry';
|
||||
|
||||
const emptyObject = {};
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
const styleProps = registry.resolve([styles.root, style]) || emptyObject;
|
||||
return <div {...other} {...styleProps} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
}
|
||||
});
|
||||
|
||||
export default View;
|
||||
19
benchmarks/src/components/View/styled-components.js
Normal file
19
benchmarks/src/components/View/styled-components.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const View = styled.div`
|
||||
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;
|
||||
`;
|
||||
|
||||
export default View;
|
||||
15
benchmarks/src/components/View/styles.css
Normal file
15
benchmarks/src/components/View/styles.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.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;
|
||||
}
|
||||
33
benchmarks/src/components/View/styletron.js
Normal file
33
benchmarks/src/components/View/styletron.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/* 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;
|
||||
40
benchmarks/src/components/theme.js
Normal file
40
benchmarks/src/components/theme.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const colors = {
|
||||
blue: '#1B95E0',
|
||||
lightBlue: '#71C9F8',
|
||||
green: '#17BF63',
|
||||
orange: '#F45D22',
|
||||
purple: '#794BC4',
|
||||
red: '#E0245E',
|
||||
white: '#FFFFFF',
|
||||
yellow: '#FFAD1F',
|
||||
deepGray: '#657786',
|
||||
fadedGray: '#E6ECF0',
|
||||
faintGray: '#F5F8FA',
|
||||
gray: '#AAB8C2',
|
||||
lightGray: '#CCD6DD',
|
||||
textBlack: '#14171A'
|
||||
};
|
||||
|
||||
const fontSize = {
|
||||
root: '14px',
|
||||
// font scale
|
||||
small: '0.85rem',
|
||||
normal: '1rem',
|
||||
large: '1.25rem'
|
||||
};
|
||||
|
||||
const theme = {
|
||||
colors,
|
||||
fontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", sans-serif, ' +
|
||||
'"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', // emoji fonts
|
||||
fontSize,
|
||||
lineHeight: 1.3125,
|
||||
spaceX: 0.6,
|
||||
spaceY: 1.3125,
|
||||
createLength(num, unit) {
|
||||
return `${num}${unit}`;
|
||||
}
|
||||
};
|
||||
|
||||
export default theme;
|
||||
9
benchmarks/src/css-modules.js
Normal file
9
benchmarks/src/css-modules.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Box from './components/Box/css-modules';
|
||||
import View from './components/View/css-modules';
|
||||
|
||||
const api = {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
|
||||
export default api;
|
||||
7
benchmarks/src/glamor.js
Normal file
7
benchmarks/src/glamor.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/glamor';
|
||||
import View from './components/View/glamor';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
9
benchmarks/src/jss.js
Normal file
9
benchmarks/src/jss.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Box from './components/Box/jss';
|
||||
import View from './components/View/jss';
|
||||
|
||||
const api = {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
|
||||
export default api;
|
||||
9
benchmarks/src/radium.js
Normal file
9
benchmarks/src/radium.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Box from './components/Box/radium';
|
||||
import View from './components/View/radium';
|
||||
|
||||
const api = {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
|
||||
export default api;
|
||||
9
benchmarks/src/react-native-stylesheet.js
vendored
Normal file
9
benchmarks/src/react-native-stylesheet.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import Box from './components/Box/react-native-stylesheet';
|
||||
import View from './components/View/react-native-stylesheet';
|
||||
|
||||
const api = {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
|
||||
export default api;
|
||||
9
benchmarks/src/react-native.js
vendored
Normal file
9
benchmarks/src/react-native.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import Box from './components/Box/react-native';
|
||||
import Tweet from './components/Tweet';
|
||||
import { View } from 'react-native';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Tweet,
|
||||
View
|
||||
};
|
||||
7
benchmarks/src/reactxp.js
Normal file
7
benchmarks/src/reactxp.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/reactxp';
|
||||
import { View } from 'reactxp';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
7
benchmarks/src/styled-components-primitives.js
Normal file
7
benchmarks/src/styled-components-primitives.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/styled-components';
|
||||
import styled from 'styled-components/primitives';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View: styled.View
|
||||
};
|
||||
7
benchmarks/src/styled-components.js
Normal file
7
benchmarks/src/styled-components.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/styled-components';
|
||||
import View from './components/View/styled-components';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
7
benchmarks/src/styletron.js
Normal file
7
benchmarks/src/styletron.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/styletron';
|
||||
import View from './components/View/styletron';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
14
benchmarks/tests/renderDeepTree.js
Normal file
14
benchmarks/tests/renderDeepTree.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import NestedTree from '../src/components/NestedTree';
|
||||
import React from 'react';
|
||||
|
||||
const renderDeepTree = (label, components) =>
|
||||
createRenderBenchmark({
|
||||
name: `Deep tree [${label}]`,
|
||||
runs: 20,
|
||||
getElement() {
|
||||
return <NestedTree breadth={3} components={components} depth={6} id={0} wrap={1} />;
|
||||
}
|
||||
});
|
||||
|
||||
export default renderDeepTree;
|
||||
113
benchmarks/tests/renderTweet.js
Normal file
113
benchmarks/tests/renderTweet.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import Tweet from '../src/components/Tweet';
|
||||
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 =>
|
||||
createRenderBenchmark({
|
||||
name: `Tweet [${label}]`,
|
||||
runs: 10,
|
||||
getElement() {
|
||||
return (
|
||||
<div style={{ width: 500 }}>
|
||||
<Tweet tweet={tweet1} />
|
||||
<Tweet tweet={tweet2} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default renderTweet;
|
||||
14
benchmarks/tests/renderWideTree.js
Normal file
14
benchmarks/tests/renderWideTree.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import createRenderBenchmark from '../createRenderBenchmark';
|
||||
import NestedTree from '../src/components/NestedTree';
|
||||
import React from 'react';
|
||||
|
||||
const renderWideTree = (label, components) =>
|
||||
createRenderBenchmark({
|
||||
name: `Wide tree [${label}]`,
|
||||
runs: 20,
|
||||
getElement() {
|
||||
return <NestedTree breadth={10} components={components} depth={3} id={0} wrap={4} />;
|
||||
}
|
||||
});
|
||||
|
||||
export default renderWideTree;
|
||||
55
benchmarks/webpack.config.js
Normal file
55
benchmarks/webpack.config.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
context: __dirname,
|
||||
entry: './index',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'performance.bundle.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: { module: true, localIdentName: '[hash:base64:8]' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: { cacheDirectory: true }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: 'static',
|
||||
openAnalyzer: false
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
dead_code: true,
|
||||
screw_ie8: true,
|
||||
warnings: false
|
||||
}
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../src')
|
||||
}
|
||||
}
|
||||
};
|
||||
1404
benchmarks/yarn.lock
Normal file
1404
benchmarks/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
2
core.js
2
core.js
@@ -1 +1 @@
|
||||
module.exports = require('./dist/core')
|
||||
module.exports = require('./dist/core');
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
`AppRegistry` is the control point for registering, running, prerendering, and
|
||||
unmounting all apps. App root components should register themselves with
|
||||
`AppRegistry.registerComponent`. Apps can be run by invoking
|
||||
`AppRegistry.runApplication`, and prerendered by invoking
|
||||
`AppRegistry.prerenderApplication` (see the [client and server rendering
|
||||
guide](../guides/rendering.md) for more details).
|
||||
`AppRegistry.runApplication` (see the [getting started guide](../guides/getting-started.md) for more details).
|
||||
|
||||
To "stop" an application when a view should be destroyed, call
|
||||
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
|
||||
@@ -16,8 +14,7 @@ into `runApplication`. These should always be used as a pair.
|
||||
(web) static **getApplication**(appKey:string, appParameters: object)
|
||||
|
||||
Returns the given application element. Use this for server-side rendering.
|
||||
Return object is of type `{ element: ReactElement; stylesheet: ReactElement }`.
|
||||
It's recommended that you use `sheetsheet` to render the style sheet in an app
|
||||
Return object is of type `{ element: ReactElement; stylesheets: [ ReactElement ] }`.
|
||||
|
||||
static **registerConfig**(config: Array<AppConfig>)
|
||||
|
||||
@@ -43,6 +40,9 @@ Runs the application that was registered under `appKey`. The `appParameters`
|
||||
must include the `rootTag` into which the application is rendered, and
|
||||
optionally any `initialProps`.
|
||||
|
||||
On web, if the `rootTag` is a sub-section of your application it should be
|
||||
styled as `position:relative` and given an explicit height.
|
||||
|
||||
static **unmountApplicationComponentAtRootTag**(rootTag: HTMLElement)
|
||||
|
||||
To "stop" an application when a view should be destroyed, call
|
||||
|
||||
16
docs/apis/Clipboard.md
Normal file
16
docs/apis/Clipboard.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Clipboard
|
||||
|
||||
Clipboard gives you an interface for setting to the clipboard. (Getting
|
||||
clipboard content is not supported on web.)
|
||||
|
||||
## Methods
|
||||
|
||||
static **getString**()
|
||||
|
||||
Returns a `Promise` of an empty string.
|
||||
|
||||
static **setString**(content: string): boolean
|
||||
|
||||
Copies a string to the clipboard. On web, some browsers may not support copying
|
||||
to the clipboard, therefore, this function returns a boolean to indicate if the
|
||||
copy was successful.
|
||||
@@ -1,42 +0,0 @@
|
||||
# NativeMethods
|
||||
|
||||
React Native for Web provides several methods to directly access the underlying
|
||||
DOM node. This can be useful in cases when you want to focus a view or measure
|
||||
its on-screen dimensions, for example.
|
||||
|
||||
The methods described are available on most of the default components provided
|
||||
by React Native for Web. Note, however, that they are *not* available on the
|
||||
composite components that you define in your own app. For more information, see
|
||||
[Direct Manipulation](../guides/direct-manipulation.md).
|
||||
|
||||
## Methods
|
||||
|
||||
**blur**()
|
||||
|
||||
Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
|
||||
**focus**()
|
||||
|
||||
Requests focus for the given input or view. The exact behavior triggered will
|
||||
depend the type of view.
|
||||
|
||||
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
|
||||
|
||||
For a given view, `measure` determines the offset relative to the parent view,
|
||||
width, height, and the offset relative to the viewport. Returns the values via
|
||||
an async callback.
|
||||
|
||||
Note that these measurements are not available until after the rendering has
|
||||
been completed.
|
||||
|
||||
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
|
||||
|
||||
Like `measure`, but measures the view relative to another view, specified as
|
||||
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
|
||||
the origin `x`, `y` of the ancestor view.
|
||||
|
||||
**setNativeProps**(nativeProps: Object)
|
||||
|
||||
This function sends props straight to the underlying DOM node. See the [direct
|
||||
manipulation](../guides/direct-manipulation.md) guide for cases where
|
||||
`setNativeProps` should be used.
|
||||
@@ -32,7 +32,7 @@ static **removeEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
**isConnected**: bool = true
|
||||
|
||||
Available on all user agents. Asynchronously fetch a boolean to determine
|
||||
internet connectivity.
|
||||
internet connectivity. Use this if you are only interested with whether the device has internet connectivity.
|
||||
|
||||
**isConnected.addEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# StyleSheet
|
||||
|
||||
The `StyleSheet` abstraction converts predefined styles to (vendor-prefixed)
|
||||
CSS without requiring a compile-time step. Some styles cannot be resolved
|
||||
outside of the render loop and are applied as inline styles. Read more about
|
||||
[how to style your application](../guides/style.md).
|
||||
CSS without requiring a compile-time step. Styles that cannot be resolved
|
||||
outside of the render loop (e.g., dynamic positioning) are usually applied as
|
||||
inline styles. Read more about [how to style your
|
||||
application](../guides/style.md).
|
||||
|
||||
## Methods
|
||||
|
||||
@@ -15,9 +16,10 @@ Each key of the object passed to `create` must define a style object.
|
||||
|
||||
Flattens an array of styles into a single style object.
|
||||
|
||||
**render**: function
|
||||
(web) **getStyleSheets**: function
|
||||
|
||||
Returns a React `<style>` element for use in server-side rendering.
|
||||
Returns an array of stylesheets (`{ id, textContent }`). Useful for
|
||||
compile-time or server-side rendering.
|
||||
|
||||
## Properties
|
||||
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**animating**: bool = true
|
||||
**animating**: boolean = true
|
||||
|
||||
Whether to show the indicator or hide it.
|
||||
|
||||
**color**: string = '#1976D2'
|
||||
**color**: ?color = '#1976D2'
|
||||
|
||||
The foreground color of the spinner.
|
||||
|
||||
**hidesWhenStopped**: bool = true
|
||||
**hidesWhenStopped**: ?boolean = true
|
||||
|
||||
Whether the indicator should hide when not animating.
|
||||
|
||||
**size**: oneOf('small, 'large') | number = 'small'
|
||||
**size**: ?enum('small, 'large') | number = 'small'
|
||||
|
||||
Size of the indicator. Small has a height of `20`, large has a height of `36`.
|
||||
|
||||
|
||||
43
docs/components/Button.md
Normal file
43
docs/components/Button.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Button
|
||||
|
||||
A basic button component. Supports a minimal level of customization. You can
|
||||
build your own custom button using `TouchableOpacity` or
|
||||
`TouchableNativeFeedback`.
|
||||
|
||||
## Props
|
||||
|
||||
**accessibilityLabel**: ?string
|
||||
|
||||
Overrides the text that's read by a screen reader when the user interacts
|
||||
with the element.
|
||||
|
||||
**color**: ?string
|
||||
|
||||
Background color of the button.
|
||||
|
||||
**disabled**: ?boolean
|
||||
|
||||
If `true`, disable all interactions for this element.
|
||||
|
||||
**onPress**: function
|
||||
|
||||
This function is called on press.
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
**title**: string
|
||||
|
||||
Text to display inside the button.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
<Button
|
||||
accessibilityLabel="Learn more about this purple button"
|
||||
color="#841584"
|
||||
onPress={onPressLearnMore}
|
||||
title="Learn More"
|
||||
/>
|
||||
```
|
||||
@@ -9,65 +9,66 @@ Unsupported React Native props:
|
||||
|
||||
## Props
|
||||
|
||||
**accessibilityLabel**: string
|
||||
**accessibilityLabel**: ?string
|
||||
|
||||
The text that's read by a screenreader when someone interacts with the image.
|
||||
|
||||
**accessible**: bool
|
||||
**accessible**: ?boolean
|
||||
|
||||
When `false`, the view is hidden from screenreaders. Default: `true`.
|
||||
When `true`, indicates the image is an accessibility element.
|
||||
|
||||
**children**: any
|
||||
**children**: ?any
|
||||
|
||||
Content to display over the image.
|
||||
|
||||
**defaultSource**: { uri: string }
|
||||
**defaultSource**: ?object
|
||||
|
||||
An image to display as a placeholder while downloading the final image off the network.
|
||||
An image to display as a placeholder while downloading the final image off the
|
||||
network. `{ uri: string, width, height }`
|
||||
|
||||
**onError**: function
|
||||
**onError**: ?function
|
||||
|
||||
Invoked on load error with `{nativeEvent: {error}}`.
|
||||
|
||||
**onLayout**: function
|
||||
**onLayout**: ?function
|
||||
|
||||
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
|
||||
height } } }`, where `x` and `y` are the offsets from the parent node.
|
||||
|
||||
**onLoad**: function
|
||||
**onLoad**: ?function
|
||||
|
||||
Invoked when load completes successfully.
|
||||
|
||||
**onLoadEnd**: function
|
||||
**onLoadEnd**: ?function
|
||||
|
||||
Invoked when load either succeeds or fails,
|
||||
|
||||
**onLoadStart**: function
|
||||
**onLoadStart**: ?function
|
||||
|
||||
Invoked on load start.
|
||||
|
||||
**resizeMode**: oneOf('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
|
||||
**resizeMode**: ?enum('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'cover'
|
||||
|
||||
Determines how to resize the image when the frame doesn't match the raw image
|
||||
dimensions.
|
||||
|
||||
**source**: { uri: string }
|
||||
**source**: ?object
|
||||
|
||||
`uri` is a string representing the resource identifier for the image, which
|
||||
could be an http address or a base64 encoded image.
|
||||
could be an http address or a base64 encoded image. `{ uri: string, width, height }`
|
||||
|
||||
**style**: style
|
||||
**style**: ?style
|
||||
|
||||
+ ...[View#style](./View.md)
|
||||
+ `resizeMode`
|
||||
|
||||
**testID**: string
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate a view in end-to-end tests.
|
||||
|
||||
## Properties
|
||||
|
||||
static **resizeMode**: Object
|
||||
static **resizeMode**: object
|
||||
|
||||
Example usage:
|
||||
|
||||
@@ -75,6 +76,23 @@ Example usage:
|
||||
<Image resizeMode={Image.resizeMode.contain} />
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
static **getSize**(uri: string, success: (width, height) => {}, failure: function)
|
||||
|
||||
Retrieve the width and height (in pixels) of an image prior to displaying it.
|
||||
This method can fail if the image cannot be found, or fails to download.
|
||||
|
||||
(In order to retrieve the image dimensions, the image may first need to be
|
||||
loaded or downloaded, after which it will be cached. This means that in
|
||||
principle you could use this method to preload images, however it is not
|
||||
optimized for that purpose, and may in future be implemented in a way that does
|
||||
not fully load/download the image data.)
|
||||
|
||||
static **prefetch**(url: string): Promise
|
||||
|
||||
Prefetches a remote image for later use by downloading it.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# ListView
|
||||
|
||||
TODO
|
||||
|
||||
## Props
|
||||
|
||||
[...ScrollView props](./ScrollView.md)
|
||||
|
||||
**children**: any
|
||||
|
||||
Content to display over the image.
|
||||
|
||||
**style**: style
|
||||
|
||||
+ ...[View#style](View.md)
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import { ListView } from 'react-native'
|
||||
|
||||
export default class ListViewExample extends Component {
|
||||
static propTypes = {}
|
||||
|
||||
static defaultProps = {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ListView />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -6,18 +6,22 @@ Display an activity progress bar.
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**color**: string = '#1976D2'
|
||||
**color**: ?string = '#1976D2'
|
||||
|
||||
Color of the progress bar.
|
||||
|
||||
**indeterminate**: bool = true
|
||||
**indeterminate**: ?boolean = true
|
||||
|
||||
Whether the progress bar will show indeterminate progress.
|
||||
|
||||
**progress**: number
|
||||
**progress**: ?number
|
||||
|
||||
The progress value (between 0 and 1).
|
||||
|
||||
(web) **trackColor**: string = 'transparent'
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
(web) **trackColor**: ?string = 'transparent'
|
||||
|
||||
Color of the track bar.
|
||||
|
||||
@@ -9,17 +9,17 @@ view directly (discouraged) or make sure all parent views have bounded height
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**contentContainerStyle**: style
|
||||
**contentContainerStyle**: ?style
|
||||
|
||||
These styles will be applied to the scroll view content container which wraps
|
||||
all of the child views.
|
||||
|
||||
**horizontal**: bool = false
|
||||
**horizontal**: ?boolean = false
|
||||
|
||||
When true, the scroll view's children are arranged horizontally in a row
|
||||
When `true`, the scroll view's children are arranged horizontally in a row
|
||||
instead of vertically in a column.
|
||||
|
||||
**keyboardDismissMode**: oneOf('none', 'on-drag') = 'none'
|
||||
**keyboardDismissMode**: ?enum('none', 'on-drag') = 'none'
|
||||
|
||||
Determines whether the keyboard gets dismissed in response to a scroll drag.
|
||||
|
||||
@@ -27,32 +27,44 @@ Determines whether the keyboard gets dismissed in response to a scroll drag.
|
||||
* `on-drag`, the keyboard is dismissed when a drag begins.
|
||||
* `interactive` (not supported on web; same as `none`)
|
||||
|
||||
**onContentSizeChange**: function
|
||||
**onContentSizeChange**: ?function
|
||||
|
||||
Called when scrollable content view of the `ScrollView` changes. It's
|
||||
implemented using the `onLayout` handler attached to the content container
|
||||
which this `ScrollView` renders.
|
||||
|
||||
**onScroll**: function
|
||||
**onScroll**: ?function
|
||||
|
||||
Fires at most once per frame during scrolling. The frequency of the events can
|
||||
be contolled using the `scrollEventThrottle` prop.
|
||||
|
||||
**refreshControl**: element
|
||||
Invoked on scroll with the following event:
|
||||
|
||||
```js
|
||||
{
|
||||
nativeEvent: {
|
||||
contentOffset: { x, y },
|
||||
contentSize: { height, width },
|
||||
layoutMeasurement: { height, width }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**refreshControl**: ?element
|
||||
|
||||
TODO
|
||||
|
||||
A [RefreshControl](../RefreshControl) component, used to provide
|
||||
pull-to-refresh functionality for the `ScrollView`.
|
||||
|
||||
**scrollEnabled**: bool = true
|
||||
**scrollEnabled**: ?boolean = true
|
||||
|
||||
When false, the content does not scroll.
|
||||
|
||||
**scrollEventThrottle**: number = 0
|
||||
**scrollEventThrottle**: ?number = 0
|
||||
|
||||
This controls how often the scroll event will be fired while scrolling (in
|
||||
events per seconds). A higher number yields better accuracy for code that is
|
||||
This controls how often the scroll event will be fired while scrolling (as a
|
||||
time interval in ms). A lower number yields better accuracy for code that is
|
||||
tracking the scroll position, but can lead to scroll performance problems. The
|
||||
default value is `0`, which means the scroll event will be sent only once each
|
||||
time the view is scrolled.
|
||||
@@ -104,7 +116,7 @@ export default class ScrollViewExample extends Component {
|
||||
contentContainerStyle={styles.container}
|
||||
horizontal
|
||||
onScroll={(e) => this.onScroll(e)}
|
||||
scrollEventThrottle={60}
|
||||
scrollEventThrottle={100}
|
||||
style={styles.root}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -9,31 +9,31 @@ supplied `value` prop instead of the expected result of any user actions.
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**disabled**: bool = false
|
||||
**disabled**: ?boolean = false
|
||||
|
||||
If `true` the user won't be able to interact with the switch.
|
||||
|
||||
**onValueChange**: func
|
||||
**onValueChange**: ?function
|
||||
|
||||
Invoked with the new value when the value changes.
|
||||
|
||||
**value**: bool = false
|
||||
**value**: ?boolean = false
|
||||
|
||||
The value of the switch. If `true` the switch will be turned on.
|
||||
|
||||
(web) **activeThumbColor**: color = #009688
|
||||
(web) **activeThumbColor**: ?color = #009688
|
||||
|
||||
The color of the thumb grip when the switch is turned on.
|
||||
|
||||
(web) **activeTrackColor**: color = #A3D3CF
|
||||
(web) **activeTrackColor**: ?color = #A3D3CF
|
||||
|
||||
The color of the track when the switch is turned on.
|
||||
|
||||
(web) **thumbColor**: color = #FAFAFA
|
||||
(web) **thumbColor**: ?color = #FAFAFA
|
||||
|
||||
The color of the thumb grip when the switch is turned off.
|
||||
|
||||
(web) **trackColor**: color = #939393
|
||||
(web) **trackColor**: ?color = #939393
|
||||
|
||||
The color of the track when the switch is turned off.
|
||||
|
||||
|
||||
@@ -16,76 +16,90 @@ Unsupported React Native props:
|
||||
|
||||
NOTE: `Text` will transfer all other props to the rendered HTML element.
|
||||
|
||||
(web) **accessibilityLabel**: string
|
||||
(web) **accessibilityLabel**: ?string
|
||||
|
||||
Defines the text available to assistive technologies upon interaction with the
|
||||
element. (This is implemented using `aria-label`.)
|
||||
Overrides the text that's read by a screen reader when the user interacts
|
||||
with the element. (This is implemented using `aria-label`.)
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles)
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
(web) **accessibilityRole**: ?oneOf(roles)
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
in a manner that is consistent with user expectations for similar views of that
|
||||
type. For example, marking a touchable view with an `accessibilityRole` of
|
||||
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**accessible**: bool = true
|
||||
**accessible**: ?boolean
|
||||
|
||||
When `false`, the text is hidden from assistive technologies. (This is
|
||||
implemented using `aria-hidden`.)
|
||||
When `true`, indicates that the view is an accessibility element (i.e.,
|
||||
focusable) and groups its child content. By default, all the touchable elements
|
||||
and elements with `accessibilityRole` of `button` and `link` are accessible.
|
||||
(This is implemented using `tabindex`.)
|
||||
|
||||
**children**: any
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**children**: ?any
|
||||
|
||||
Child content.
|
||||
|
||||
**numberOfLines**: number
|
||||
**importantForAccessibility**: ?enum('auto', 'no-hide-descendants', 'yes')
|
||||
|
||||
A value of `no` will remove the element from the tab flow.
|
||||
|
||||
A value of `no-hide-descendants` will hide the element and its children from
|
||||
assistive technologies. (This is implemented using `aria-hidden`.)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**numberOfLines**: ?number
|
||||
|
||||
Truncates the text with an ellipsis after this many lines. Currently only supports `1`.
|
||||
|
||||
**onLayout**: function
|
||||
**onLayout**: ?function
|
||||
|
||||
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
|
||||
height } } }`, where `x` and `y` are the offsets from the parent node.
|
||||
|
||||
**onPress**: function
|
||||
**onPress**: ?function
|
||||
|
||||
This function is called on press.
|
||||
|
||||
**selectable**: bool = true
|
||||
**selectable**: ?boolean
|
||||
|
||||
Lets the user select the text.
|
||||
When `false`, the text is not selectable.
|
||||
|
||||
**style**: style
|
||||
**style**: ?style
|
||||
|
||||
+ ...[View#style](View.md)
|
||||
+ `color`
|
||||
+ `fontFamily`
|
||||
+ `fontFeatureSettings` ‡
|
||||
+ `fontSize`
|
||||
+ `fontStyle`
|
||||
+ `fontWeight`
|
||||
+ `letterSpacing`
|
||||
+ `lineHeight`
|
||||
+ `textAlign`‡
|
||||
+ `textAlign`
|
||||
+ `textAlignVertical`
|
||||
+ `textDecorationLine`
|
||||
+ `textOverflow`
|
||||
+ `textRendering`
|
||||
+ `textIndent` ‡
|
||||
+ `textOverflow` ‡
|
||||
+ `textRendering` ‡
|
||||
+ `textShadowColor`
|
||||
+ `textShadowOffset`‡
|
||||
+ `textShadowOffset`
|
||||
+ `textShadowRadius`
|
||||
+ `textTransform`
|
||||
+ `unicodeBidi`
|
||||
+ `textTransform` ‡
|
||||
+ `unicodeBidi` ‡
|
||||
+ `whiteSpace`
|
||||
+ `wordWrap`
|
||||
+ `writingDirection`‡
|
||||
+ `wordWrap` ‡
|
||||
+ `writingDirection`
|
||||
|
||||
‡ This property can be suffixed with `$noI18n` to prevent automatic
|
||||
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
|
||||
‡ web only.
|
||||
|
||||
**testID**: string
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Unsupported React Native props:
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**autoCapitalize**: oneOf('characters', 'none', 'sentences', 'words') = 'sentences'
|
||||
**autoCapitalize**: ?enum('characters', 'none', 'sentences', 'words') = 'sentences'
|
||||
|
||||
Automatically capitalize certain characters (only available in Chrome and iOS Safari).
|
||||
|
||||
@@ -27,21 +27,21 @@ Automatically capitalize certain characters (only available in Chrome and iOS Sa
|
||||
* `sentences`: Automatically capitalize the first letter of sentences.
|
||||
* `words`: Automatically capitalize the first letter of words.
|
||||
|
||||
(web) **autoComplete**: string
|
||||
(web) **autoComplete**: ?string
|
||||
|
||||
Indicates whether the value of the control can be automatically completed by
|
||||
the browser. [Accepted values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
|
||||
|
||||
**autoCorrect**: bool = true
|
||||
**autoCorrect**: ?boolean = true
|
||||
|
||||
Automatically correct spelling mistakes (only available in iOS Safari).
|
||||
|
||||
**autoFocus**: bool = false
|
||||
**autoFocus**: ?boolean = false
|
||||
|
||||
If `true`, focuses the input on `componentDidMount`. Only the first form element
|
||||
in a document with `autofocus` is focused.
|
||||
|
||||
**blurOnSubmit**: bool
|
||||
**blurOnSubmit**: ?boolean
|
||||
|
||||
If `true`, the text field will blur when submitted. The default value is `true`
|
||||
for single-line fields and `false` for multiline fields. Note, for multiline
|
||||
@@ -49,108 +49,107 @@ fields setting `blurOnSubmit` to `true` means that pressing return will blur
|
||||
the field and trigger the `onSubmitEditing` event instead of inserting a
|
||||
newline into the field.
|
||||
|
||||
**clearTextOnFocus**: bool = false
|
||||
**clearTextOnFocus**: ?boolean = false
|
||||
|
||||
If `true`, clears the text field automatically when focused.
|
||||
|
||||
**defaultValue**: string
|
||||
**defaultValue**: ?string
|
||||
|
||||
Provides an initial value that will change when the user starts typing. Useful
|
||||
for simple use-cases where you don't want to deal with listening to events and
|
||||
updating the `value` prop to keep the controlled state in sync.
|
||||
|
||||
**editable**: bool = true
|
||||
**editable**: ?boolean = true
|
||||
|
||||
If `false`, text is not editable (i.e., read-only).
|
||||
|
||||
**keyboardType**: oneOf('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
|
||||
**keyboardType**: enum('default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search') = 'default'
|
||||
|
||||
Determines which keyboard to open. (NOTE: Safari iOS requires an ancestral
|
||||
`<form action>` element to display the `search` keyboard).
|
||||
|
||||
(Not available when `multiline` is `true`.)
|
||||
|
||||
**maxLength**: number
|
||||
**maxLength**: ?number
|
||||
|
||||
Limits the maximum number of characters that can be entered.
|
||||
|
||||
(web) **maxNumberOfLines**: number = numberOfLines
|
||||
|
||||
Limits the maximum number of lines for a multiline `TextInput`.
|
||||
|
||||
(Requires `multiline` to be `true`.)
|
||||
|
||||
**multiline**: bool = false
|
||||
**multiline**: ?boolean = false
|
||||
|
||||
If true, the text input can be multiple lines.
|
||||
|
||||
**numberOfLines**: number = 2
|
||||
**numberOfLines**: ?number = 2
|
||||
|
||||
Sets the initial number of lines for a multiline `TextInput`.
|
||||
Sets the number of lines for a multiline `TextInput`.
|
||||
|
||||
(Requires `multiline` to be `true`.)
|
||||
|
||||
**onBlur**: function
|
||||
**onBlur**: ?function
|
||||
|
||||
Callback that is called when the text input is blurred.
|
||||
|
||||
**onChange**: function
|
||||
**onChange**: ?function
|
||||
|
||||
Callback that is called when the text input's text changes.
|
||||
|
||||
**onChangeText**: function
|
||||
**onChangeText**: ?function
|
||||
|
||||
Callback that is called when the text input's text changes. The text is passed
|
||||
as an argument to the callback handler.
|
||||
|
||||
**onFocus**: function
|
||||
**onFocus**: ?function
|
||||
|
||||
Callback that is called when the text input is focused.
|
||||
|
||||
**onKeyPress**: function
|
||||
**onKeyPress**: ?function
|
||||
|
||||
Callback that is called when a key is pressed. Pressed key value is passed as
|
||||
an argument to the callback handler. Fires before `onChange` callbacks.
|
||||
Callback that is called when a key is pressed. This will be called with `{
|
||||
nativeEvent: { key: keyValue } }` where keyValue is 'Enter` or 'Backspace' for
|
||||
respective keys and the typed-in character otherwise including ' ' for space.
|
||||
Modifier keys are also included in the nativeEvent. Fires before onChange
|
||||
callbacks.
|
||||
|
||||
**onSelectionChange**: function
|
||||
**onSelectionChange**: ?function
|
||||
|
||||
Callback that is called when the text input's selection changes. This will be called with
|
||||
`{ nativeEvent: { selection: { start, end } } }`.
|
||||
|
||||
**onSubmitEditing**: function
|
||||
**onSubmitEditing**: ?function
|
||||
|
||||
Callback that is called when the keyboard's submit button is pressed.
|
||||
|
||||
**placeholder**: string
|
||||
**placeholder**: ?string
|
||||
|
||||
The string that will be rendered in an empty `TextInput` before text has been
|
||||
entered.
|
||||
|
||||
**secureTextEntry**: bool = false
|
||||
**secureTextEntry**: ?boolean = false
|
||||
|
||||
If true, the text input obscures the text entered so that sensitive text like
|
||||
passwords stay secure.
|
||||
|
||||
(Not available when `multiline` is `true`.)
|
||||
|
||||
**selection**: { start: number, end: ?number }
|
||||
**selection**: ?{ start: number, end: ?number }
|
||||
|
||||
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
|
||||
|
||||
**selectTextOnFocus**: bool = false
|
||||
**selectTextOnFocus**: ?boolean = false
|
||||
|
||||
If `true`, all text will automatically be selected on focus.
|
||||
|
||||
**style**: style
|
||||
**style**: ?style
|
||||
|
||||
+ ...[Text#style](./Text.md)
|
||||
+ `outline`
|
||||
+ `resize` ‡
|
||||
|
||||
**testID**: string
|
||||
‡ web only.
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
**value**: string
|
||||
**value**: ?string
|
||||
|
||||
The value to show for the text input. `TextInput` is a controlled component,
|
||||
which means the native `value` will be forced to match this prop if provided.
|
||||
|
||||
@@ -11,60 +11,33 @@ several child components, wrap them in a View.
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**accessibilityLabel**: string
|
||||
|
||||
Overrides the text that's read by the screen reader when the user interacts
|
||||
with the element.
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles) = 'button'
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
|
||||
**accessible**: bool = true
|
||||
|
||||
When `false`, the view is hidden from screenreaders.
|
||||
|
||||
**children**: View
|
||||
|
||||
**delayLongPress**: number
|
||||
**delayLongPress**: ?number
|
||||
|
||||
Delay in ms, from `onPressIn`, before `onLongPress` is called.
|
||||
|
||||
**delayPressIn**: number
|
||||
**delayPressIn**: ?number
|
||||
|
||||
Delay in ms, from the start of the touch, before `onPressIn` is called.
|
||||
|
||||
**delayPressOut**: number
|
||||
**delayPressOut**: ?number
|
||||
|
||||
Delay in ms, from the release of the touch, before `onPressOut` is called.
|
||||
|
||||
**disabled**: bool
|
||||
**disabled**: ?boolean
|
||||
|
||||
If true, disable all interactions for this component.
|
||||
If `true`, disable all interactions for this component.
|
||||
|
||||
**hitSlop**: `{top: number, left: number, bottom: number, right: number}`
|
||||
**onLongPress**: ?function
|
||||
|
||||
This defines how far your touch can start away from the button. This is added
|
||||
to `pressRetentionOffset` when moving off of the button. **NOTE**: The touch
|
||||
area never extends past the parent view bounds and the z-index of sibling views
|
||||
always takes precedence if a touch hits two overlapping views.
|
||||
|
||||
**onLayout**: function
|
||||
|
||||
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
|
||||
height } } }`, where `x` and `y` are the offsets from the parent node.
|
||||
|
||||
**onLongPress**: function
|
||||
|
||||
**onPress**: function
|
||||
**onPress**: ?function
|
||||
|
||||
Called when the touch is released, but not if cancelled (e.g. by a scroll that steals the responder lock).
|
||||
|
||||
**onPressIn**: function
|
||||
**onPressIn**: ?function
|
||||
|
||||
**onPressOut**: function
|
||||
**onPressOut**: ?function
|
||||
|
||||
**pressRetentionOffset**: `{top: number, left: number, bottom: number, right: number}`
|
||||
**pressRetentionOffset**: ?`{top: number, left: number, bottom: number, right: number}`
|
||||
|
||||
When the scroll view is disabled, this defines how far your touch may move off
|
||||
of the button, before deactivating the button. Once deactivated, try moving it
|
||||
|
||||
@@ -7,21 +7,20 @@ inside another `View` and has 0-to-many children of any type.
|
||||
Also, refer to React Native's documentation about the [Gesture Responder
|
||||
System](http://facebook.github.io/react-native/releases/0.22/docs/gesture-responder-system.html).
|
||||
|
||||
Unsupported React Native props:
|
||||
`onAccessibilityTap`,
|
||||
`hitSlop`,
|
||||
`onMagicTap`
|
||||
Unsupported React Native props: `collapsable`, `onAccessibilityTap`, `onMagicTap`.
|
||||
|
||||
## Props
|
||||
|
||||
NOTE: `View` will transfer all other props to the rendered HTML element.
|
||||
|
||||
**accessibilityLabel**: string
|
||||
**accessibilityLabel**: ?string
|
||||
|
||||
Defines the text available to assistive technologies upon interaction with the
|
||||
element. (This is implemented using `aria-label`.)
|
||||
Overrides the text that's read by a screen reader when the user interacts
|
||||
with the element. (This is implemented using `aria-label`.)
|
||||
|
||||
**accessibilityLiveRegion**: oneOf('assertive', 'off', 'polite') = 'off'
|
||||
See the [Accessibility guide](../guides/accessibility.md) for more information.
|
||||
|
||||
**accessibilityLiveRegion**: ?enum('assertive', 'none', 'polite')
|
||||
|
||||
Indicates to assistive technologies whether to notify the user when the view
|
||||
changes. The values of this attribute are expressed in degrees of importance.
|
||||
@@ -30,122 +29,196 @@ priority. When regions are specified as `assertive`, assistive technologies
|
||||
will interrupt and immediately notify the user. (This is implemented using
|
||||
[`aria-live`](http://www.w3.org/TR/wai-aria/states_and_properties#aria-live).)
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles)
|
||||
See the [Accessibility guide](../guides/accessibility.md) for more information.
|
||||
|
||||
(web) **accessibilityRole**: ?enum(roles)
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
in a manner that is consistent with user expectations for similar views of that
|
||||
type. For example, marking a touchable view with an `accessibilityRole` of
|
||||
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
See the [Accessibility guide](../guides/accessibility.md) for more information.
|
||||
|
||||
**accessible**: bool = true
|
||||
**accessible**: ?boolean
|
||||
|
||||
When `false`, the view is hidden from assistive technologies. (This is
|
||||
implemented using `aria-hidden`.)
|
||||
When `true`, indicates that the view is an accessibility element (i.e.,
|
||||
focusable) and groups its child content. By default, all the touchable elements
|
||||
and elements with `accessibilityRole` of `button` and `link` are accessible.
|
||||
(This is implemented using `tabindex`.)
|
||||
|
||||
**onLayout**: function
|
||||
See the [Accessibility guide](../guides/accessibility.md) for more information.
|
||||
|
||||
**children**: ?element
|
||||
|
||||
Child content.
|
||||
|
||||
**hitSlop**: ?object
|
||||
|
||||
This defines how far a touch event can start away from the view (in pixels).
|
||||
Typical interface guidelines recommend touch targets that are at least 30 - 40
|
||||
points/density-independent pixels.
|
||||
|
||||
For example, if a touchable view has a height of `20` the touchable height can
|
||||
be extended to `40` with `hitSlop={{top: 10, bottom: 10, left: 0, right: 0}}`.
|
||||
|
||||
**importantForAccessibility**: ?enum('auto', 'no', 'no-hide-descendants', 'yes')
|
||||
|
||||
A value of `no` will remove the element from the tab flow.
|
||||
|
||||
A value of `no-hide-descendants` will hide the element and its children from
|
||||
assistive technologies. (This is implemented using `aria-hidden`.)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility.md) for more information.
|
||||
|
||||
**onLayout**: ?function
|
||||
|
||||
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
|
||||
height } } }`, where `x` and `y` are the offsets from the parent node.
|
||||
|
||||
**onMoveShouldSetResponder**: function
|
||||
**onMoveShouldSetResponder**: ?function => boolean
|
||||
|
||||
**onMoveShouldSetResponderCapture**: function
|
||||
Does this view want to "claim" touch responsiveness? This is called for every
|
||||
touch move on the `View` when it is not the responder.
|
||||
|
||||
**onResponderGrant**: function
|
||||
**onMoveShouldSetResponderCapture**: ?function => boolean
|
||||
|
||||
For most touch interactions, you'll simply want to wrap your component in
|
||||
`TouchableHighlight` or `TouchableOpacity`.
|
||||
If a parent `View` wants to prevent a child `View` from becoming responder on a
|
||||
move, it should have this handler return `true`.
|
||||
|
||||
**onResponderMove**: function
|
||||
**onResponderGrant**: ?function
|
||||
|
||||
**onResponderReject**: function
|
||||
The `View` is now responding to touch events. This is the time to highlight and
|
||||
show the user what is happening. For most touch interactions, you'll simply
|
||||
want to wrap your component in `TouchableHighlight` or `TouchableOpacity`.
|
||||
|
||||
**onResponderRelease**: function
|
||||
**onResponderMove**: ?function
|
||||
|
||||
**onResponderTerminate**: function
|
||||
The user is moving their finger.
|
||||
|
||||
**onResponderTerminationRequest**: function
|
||||
**onResponderReject**: ?function
|
||||
|
||||
**onStartShouldSetResponder**: function
|
||||
Another responder is already active and will not release it to the `View`
|
||||
asking to be the responder.
|
||||
|
||||
**onStartShouldSetResponderCapture**: function
|
||||
**onResponderRelease**: ?function
|
||||
|
||||
**pointerEvents**: oneOf('auto', 'box-only', 'box-none', 'none') = 'auto'
|
||||
Fired at the end of the touch.
|
||||
|
||||
Configure the `pointerEvents` of the view. The enhanced `pointerEvents` modes
|
||||
provided are not part of the CSS spec, therefore, `pointerEvents` is excluded
|
||||
from `style`.
|
||||
**onResponderTerminate**: ?function
|
||||
|
||||
The responder has been taken from the `View`.
|
||||
|
||||
**onResponderTerminationRequest**: ?function
|
||||
|
||||
Some other `View` wants to become responder and is asking this `View` to
|
||||
release its responder. Returning `true` allows its release.
|
||||
|
||||
**onStartShouldSetResponder**: ?function => boolean
|
||||
|
||||
Does this view want to become responder on the start of a touch?
|
||||
|
||||
**onStartShouldSetResponderCapture**: ?function => boolean
|
||||
|
||||
If a parent `View` wants to prevent a child `View` from becoming the responder
|
||||
on a touch start, it should have this handler return `true`.
|
||||
|
||||
**pointerEvents**: ?enum('auto', 'box-only', 'box-none', 'none') = 'auto'
|
||||
|
||||
Controls whether the View can be the target of touch events. The enhanced
|
||||
`pointerEvents` modes provided are not part of the CSS spec, therefore,
|
||||
`pointerEvents` is excluded from `style`.
|
||||
|
||||
`box-none` is the equivalent of:
|
||||
|
||||
```css
|
||||
.box-none { pointer-events: none }
|
||||
.box-none * { pointer-events: auto }
|
||||
.box-none { pointer-events: none !important; }
|
||||
.box-none > * { pointer-events: auto; }
|
||||
```
|
||||
|
||||
`box-only` is the equivalent of:
|
||||
|
||||
```css
|
||||
.box-only { pointer-events: auto }
|
||||
.box-only * { pointer-events: none }
|
||||
.box-only { pointer-events: auto !important; }
|
||||
.box-only > * { pointer-events: none; }
|
||||
```
|
||||
|
||||
**style**: style
|
||||
**style**: ?style
|
||||
|
||||
+ `alignContent`
|
||||
+ `alignItems`
|
||||
+ `alignSelf`
|
||||
+ `animationDelay` ‡
|
||||
+ `animationDirection` ‡
|
||||
+ `animationDuration` ‡
|
||||
+ `animationFillMode` ‡
|
||||
+ `animationIterationCount` ‡
|
||||
+ `animationName` ‡
|
||||
+ `animationPlayState` ‡
|
||||
+ `animationTimingFunction` ‡
|
||||
+ `backfaceVisibility`
|
||||
+ `backgroundAttachment`
|
||||
+ `backgroundClip`
|
||||
+ `backgroundAttachment` ‡
|
||||
+ `backgroundBlendMode` ‡
|
||||
+ `backgroundClip` ‡
|
||||
+ `backgroundColor`
|
||||
+ `backgroundImage`
|
||||
+ `backgroundOrigin`
|
||||
+ `backgroundPosition`
|
||||
+ `backgroundRepeat`
|
||||
+ `backgroundSize`
|
||||
+ `backgroundImage` ‡
|
||||
+ `backgroundOrigin` ‡
|
||||
+ `backgroundPosition` ‡
|
||||
+ `backgroundRepeat` ‡
|
||||
+ `backgroundSize` ‡
|
||||
+ `borderColor` (single value)
|
||||
+ `borderTopColor`
|
||||
+ `borderBottomColor`
|
||||
+ `borderRightColor`‡
|
||||
+ `borderLeftColor`‡
|
||||
+ `borderRightColor`
|
||||
+ `borderLeftColor`
|
||||
+ `borderRadius` (single value)
|
||||
+ `borderTopLeftRadius`‡
|
||||
+ `borderTopRightRadius`‡
|
||||
+ `borderBottomLeftRadius`‡
|
||||
+ `borderBottomRightRadius`‡
|
||||
+ `borderTopLeftRadius`
|
||||
+ `borderTopRightRadius`
|
||||
+ `borderBottomLeftRadius`
|
||||
+ `borderBottomRightRadius`
|
||||
+ `borderStyle` (single value)
|
||||
+ `borderTopStyle`
|
||||
+ `borderRightStyle`‡
|
||||
+ `borderRightStyle`
|
||||
+ `borderBottomStyle`
|
||||
+ `borderLeftStyle`‡
|
||||
+ `borderLeftStyle`
|
||||
+ `borderWidth` (single value)
|
||||
+ `borderBottomWidth`
|
||||
+ `borderLeftWidth`‡
|
||||
+ `borderRightWidth`‡
|
||||
+ `borderLeftWidth`
|
||||
+ `borderRightWidth`
|
||||
+ `borderTopWidth`
|
||||
+ `bottom`
|
||||
+ `boxShadow`
|
||||
+ `boxShadow` ‡
|
||||
+ `boxSizing`
|
||||
+ `cursor`
|
||||
+ `clip` ‡
|
||||
+ `cursor` ‡
|
||||
+ `display`
|
||||
+ `filter` ‡
|
||||
+ `flex` (number)
|
||||
+ `flexBasis`
|
||||
+ `flexDirection`
|
||||
+ `flexGrow`
|
||||
+ `flexShrink`
|
||||
+ `flexWrap`
|
||||
+ `gridAutoColumns` ‡
|
||||
+ `gridAutoFlow` ‡
|
||||
+ `gridAutoRows` ‡
|
||||
+ `gridColumnEnd` ‡
|
||||
+ `gridColumnGap` ‡
|
||||
+ `gridColumnStart` ‡
|
||||
+ `gridRowEnd` ‡
|
||||
+ `gridRowGap` ‡
|
||||
+ `gridRowStart` ‡
|
||||
+ `gridTemplateColumns` ‡
|
||||
+ `gridTemplateRows` ‡
|
||||
+ `gridTemplateAreas` ‡
|
||||
+ `height`
|
||||
+ `justifyContent`
|
||||
+ `left`‡
|
||||
+ `left`
|
||||
+ `margin` (single value)
|
||||
+ `marginBottom`
|
||||
+ `marginHorizontal`
|
||||
+ `marginLeft`‡
|
||||
+ `marginRight`‡
|
||||
+ `marginLeft`
|
||||
+ `marginRight`
|
||||
+ `marginTop`
|
||||
+ `marginVertical`
|
||||
+ `maxHeight`
|
||||
@@ -154,27 +227,40 @@ from `style`.
|
||||
+ `minWidth`
|
||||
+ `opacity`
|
||||
+ `order`
|
||||
+ `outline` ‡
|
||||
+ `outlineColor` ‡
|
||||
+ `overflow`
|
||||
+ `overflowX`
|
||||
+ `overflowY`
|
||||
+ `overflowX` ‡
|
||||
+ `overflowY` ‡
|
||||
+ `padding` (single value)
|
||||
+ `paddingBottom`
|
||||
+ `paddingHorizontal`
|
||||
+ `paddingLeft`‡
|
||||
+ `paddingRight`‡
|
||||
+ `paddingLeft`
|
||||
+ `paddingRight`
|
||||
+ `paddingTop`
|
||||
+ `paddingVertical`
|
||||
+ `perspective` ‡
|
||||
+ `perspectiveOrigin` ‡
|
||||
+ `position`
|
||||
+ `right`‡
|
||||
+ `right`
|
||||
+ `shadowColor`
|
||||
+ `shadowOffset`
|
||||
+ `shadowOpacity`
|
||||
+ `shadowRadius`
|
||||
+ `top`
|
||||
+ `transform`
|
||||
+ `userSelect`
|
||||
+ `visibility`
|
||||
+ `transformOrigin` ‡
|
||||
+ `transitionDelay` ‡
|
||||
+ `transitionDuration` ‡
|
||||
+ `transitionProperty` ‡
|
||||
+ `transitionTimingFunction` ‡
|
||||
+ `userSelect` ‡
|
||||
+ `visibility` ‡
|
||||
+ `width`
|
||||
+ `willChange` ‡
|
||||
+ `zIndex`
|
||||
|
||||
‡ This property can be suffixed with `$noI18n` to prevent automatic
|
||||
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
|
||||
‡ web only.
|
||||
|
||||
Default:
|
||||
|
||||
@@ -194,9 +280,9 @@ Default:
|
||||
};
|
||||
```
|
||||
|
||||
(See [facebook/css-layout](https://github.com/facebook/css-layout)).
|
||||
(See [facebook/yoga](https://github.com/facebook/yoga)).
|
||||
|
||||
**testID**: string
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
|
||||
@@ -1,32 +1,124 @@
|
||||
# Accessibility
|
||||
|
||||
On the Web, assistive technologies derive useful information about the
|
||||
structure, purpose, and interactivity of apps from their [HTML
|
||||
elements][html-accessibility-url], attributes, and [ARIA in
|
||||
HTML][aria-in-html-url].
|
||||
On the Web, assistive technologies (e.g., VoiceOver, TalkBack screen readers)
|
||||
derive useful information about the structure, purpose, and interactivity of
|
||||
apps from their [HTML elements][html-accessibility-url], attributes, and [ARIA
|
||||
in HTML][aria-in-html-url]. React Native for Web includes APIs designed to
|
||||
provide developers with support for making apps more accessible. The most
|
||||
common and best supported accessibility features of the Web are exposed as the
|
||||
props: `accessible`, `accessibilityLabel`, `accessibilityLiveRegion`,
|
||||
`accessibilityRole`, and `importantForAccessibility`.
|
||||
|
||||
The most common and best supported accessibility features of the Web are
|
||||
exposed as the props: `accessible`, `accessibilityLabel`,
|
||||
`accessibilityLiveRegion`, and `accessibilityRole`.
|
||||
## Accessibility properties
|
||||
|
||||
React Native for Web does not provide a way to directly control the type of the
|
||||
rendered HTML element. The `accessibilityRole` prop is used to infer an
|
||||
[analogous HTML element][html-aria-url] to use in addition to the resulting
|
||||
ARIA `role`, where possible. While this may contradict some ARIA
|
||||
### accessible
|
||||
|
||||
When `true`, indicates that the view is an accessibility element. When a view
|
||||
is an accessibility element, it groups its children into a single focusable
|
||||
component. By default, all touchable elements, buttons, and links are
|
||||
"accessible". Prefer using `accessibilityRole` (e.g., `button`, `link`) to
|
||||
create focusable HTML elements wherever possible. On web, `accessible={true}`
|
||||
is implemented using `tabIndex`.
|
||||
|
||||
### accessibilityLabel
|
||||
|
||||
When a view is marked as `accessible`, it is a good practice to set an
|
||||
`accessibilityLabel` on the view, so that people who use screen readers know
|
||||
what element they have selected. On web, `accessibilityLabel` is implemented
|
||||
using `aria-label`.
|
||||
|
||||
```
|
||||
<TouchableOpacity accessibilityLabel={'Tap me!'} accessible={true} onPress={this._onPress}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Press me!</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
```
|
||||
|
||||
### accessibilityRole
|
||||
|
||||
In some cases, we also want to alert the end user of the type of selected
|
||||
component (i.e., that it is a “button”). To provide more context to screen
|
||||
readers, you should specify the `accessibilityRole` property. (Note that React
|
||||
Native for Web also provides a compatibility mapping of equivalent
|
||||
`accessibilityTraits` and `accessibilityComponentType` values to
|
||||
`accessibilityRole`).
|
||||
|
||||
The `accessibilityRole` prop is used to infer an [analogous HTML
|
||||
element][html-aria-url] and ARIA `role`, where possible. In most cases, both
|
||||
the element and ARIA `role` are rendered. While this may contradict some ARIA
|
||||
recommendations, it also helps avoid certain HTML5 conformance errors and
|
||||
accessibility anti-patterns (e.g., giving a `heading` role to a `button`
|
||||
element).
|
||||
element) and browser bugs.
|
||||
|
||||
For example:
|
||||
|
||||
* `<View accessibilityRole='article' />` => `<article role='article' />`.
|
||||
* `<View accessibilityRole='banner' />` => `<header role='banner' />`.
|
||||
* `<View accessibilityRole='button' />` => `<button type='button' role='button' />`.
|
||||
* `<View accessibilityRole='button' />` => `<div role='button' tabIndex='0' />`.
|
||||
* `<Text accessibilityRole='label' />` => `<label />`.
|
||||
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
|
||||
* `<View accessibilityRole='main' />` => `<main role='main' />`.
|
||||
|
||||
Other ARIA properties should be set via [direct
|
||||
manipulation](./direct-manipulation.md).
|
||||
In the example below, the `TouchableHighlight` is announced by screen
|
||||
readers as a button.
|
||||
|
||||
```js
|
||||
<TouchableHighlight accessibilityRole="button" onPress={this._handlePress}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Press me!</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
```
|
||||
|
||||
Note: The `button` role is not implemented using the native `button` element
|
||||
due to browsers limiting the use of flexbox layout on its children.
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
|
||||
### accessibilityLiveRegion
|
||||
|
||||
When components dynamically change we may need to inform the user. The
|
||||
`accessibilityLiveRegion` property serves this purpose and can be set to
|
||||
`none`, `polite` and `assertive`. On web, `accessibilityLiveRegion` is
|
||||
implemented using `aria-live`.
|
||||
|
||||
* `none`: Accessibility services should not announce changes to this view.
|
||||
* `polite`: Accessibility services should announce changes to this view.
|
||||
* `assertive`: Accessibility services should interrupt ongoing speech to immediately announce changes to this view.
|
||||
|
||||
```
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded}>
|
||||
<Text>Click me</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
<Text accessibilityLiveRegion="polite">
|
||||
Clicked {this.state.count} times
|
||||
</Text>
|
||||
```
|
||||
|
||||
In the above example, method `_addOne` changes the `state.count` variable. As
|
||||
soon as an end user clicks the `TouchableWithoutFeedback`, screen readers
|
||||
announce text in the `Text` view because of its
|
||||
`accessibilityLiveRegion="polite"` property.
|
||||
|
||||
### importantForAccessibility
|
||||
|
||||
The `importantForAccessibility` property controls if a view appears in the
|
||||
accessibility tree and if it is reported to accessibility services. On web, a
|
||||
value of `no` will remove a focusable element from the tab flow, and a value of
|
||||
`no-hide-descendants` will also hide the entire subtree from assistive
|
||||
technologies (this is implemented using `aria-hidden`).
|
||||
|
||||
### Other
|
||||
|
||||
Other ARIA properties can be set via [direct
|
||||
manipulation](./direct-manipulation.md) or props (this may change in the
|
||||
future).
|
||||
|
||||
[aria-in-html-url]: https://w3c.github.io/aria-in-html/
|
||||
[html-accessibility-url]: http://www.html5accessibility.com/
|
||||
|
||||
104
docs/guides/advanced.md
Normal file
104
docs/guides/advanced.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Advanced use
|
||||
|
||||
## Use with existing React DOM components
|
||||
|
||||
React Native for Web exports a web-specific module called `createDOMElement`,
|
||||
which can be used to wrap React DOM components. This allows you to use React
|
||||
Native's accessibility and style optimizations.
|
||||
|
||||
In the example below, `Video` will now accept common React Native props such as
|
||||
`accessibilityLabel`, `accessible`, `style`, and even the Responder event
|
||||
props.
|
||||
|
||||
```js
|
||||
import { createDOMElement } from 'react-native';
|
||||
|
||||
const Video = (props) => createDOMElement('video', props);
|
||||
```
|
||||
|
||||
This also works with composite components defined in your existing component
|
||||
gallery or dependencies ([live example](https://www.webpackbin.com/bins/-KiTSGFw3fB9Szg7quLI)).
|
||||
|
||||
```js
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import { createDOMElement, StyleSheet } from 'react-native';
|
||||
|
||||
const CustomButton = (props) => createDOMElement(RaisedButton, {
|
||||
...props,
|
||||
style: [ styles.button, props.style ]
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
padding: 20
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Remember that React Native styles are not the same as React DOM styles, and
|
||||
care needs to be taken not to pass React DOM styles into your React Native
|
||||
wrapped components.
|
||||
|
||||
## Use as a library framework
|
||||
|
||||
The React Native (for Web) building blocks can be used to create higher-level
|
||||
components and abstractions. In the example below, a `styled` function provides
|
||||
an API inspired by styled-components ([live
|
||||
example](https://www.webpackbin.com/bins/-KjT9ziwv4O7FDZdvsnX)).
|
||||
|
||||
```js
|
||||
const { createDOMElement, StyleSheet } = ReactNative;
|
||||
|
||||
/**
|
||||
* styled API
|
||||
*/
|
||||
const styled = (Component, styler) => {
|
||||
const isDOMComponent = typeof Component === 'string';
|
||||
|
||||
class Styled extends React.Component {
|
||||
static contextTypes = {
|
||||
getTheme: React.PropTypes.func
|
||||
};
|
||||
|
||||
render() {
|
||||
const theme = this.context.getTheme && this.context.getTheme();
|
||||
const localProps = { ...this.props, theme };
|
||||
const nextProps = { ...this.props }
|
||||
const style = typeof styler === 'function' ? styler(localProps) : styler;
|
||||
nextProps.style = [ style, this.props.style ];
|
||||
|
||||
return (
|
||||
isDOMComponent
|
||||
? createDOMElement(Component, nextProps)
|
||||
: <Component {...nextProps} />
|
||||
);
|
||||
}
|
||||
}
|
||||
return Styled;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#2196F3',
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
const StyledView = styled(View, styles.container);
|
||||
```
|
||||
|
||||
## Use with react-sketchapp
|
||||
|
||||
Use with [react-sketchapp](http://airbnb.io/react-sketchapp/) requires that you
|
||||
alias `react-native` to `react-sketchapp`. This will allow you to render your
|
||||
existing React Native components in Sketch. Sketch-specific components like
|
||||
`Artboard` should be imported from `react-sketchapp`.
|
||||
|
||||
If you're using `skpm`, you can rely on an [undocumented
|
||||
feature](https://github.com/sketch-pm/skpm/blob/master/lib/utils/webpackConfig.js)
|
||||
which will merge your `webpack.config.js`, `.babelrc`, or `package.json` Babel
|
||||
config into its internal webpack config. The simplest option may be to use the
|
||||
[babel-plugin-module-alias](https://www.npmjs.com/package/babel-plugin-module-alias)
|
||||
and configure it in your `package.json`.
|
||||
@@ -1,27 +1,68 @@
|
||||
# Direct manipulation
|
||||
|
||||
It is sometimes necessary to make changes directly to a component without using
|
||||
state/props to trigger a re-render of the entire subtree – in the browser, this
|
||||
is done by directly modifying a DOM node. `setNativeProps` is the React Native
|
||||
equivalent to setting properties directly on a DOM node. Use direct
|
||||
manipulation when frequent re-rendering creates a performance bottleneck Direct
|
||||
manipulation will not be a tool that you reach for frequently.
|
||||
React Native for Web provides several methods to directly access the underlying
|
||||
DOM node. This can be useful when you need to make changes directly to a
|
||||
component without using state/props to trigger a re-render of the entire
|
||||
subtree, or when you want to focus a view or measure its on-screen dimensions.
|
||||
|
||||
## `setNativeProps` and `shouldComponentUpdate`
|
||||
The methods described are available on most of the default components provided
|
||||
by React Native for Web. Note, however, that they are *not* available on the
|
||||
composite components that you define in your own app.
|
||||
|
||||
## Instance methods
|
||||
|
||||
**blur**()
|
||||
|
||||
Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
|
||||
**focus**()
|
||||
|
||||
Requests focus for the given input or view. The exact behavior triggered will
|
||||
depend the type of view.
|
||||
|
||||
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
|
||||
|
||||
For a given view, `measure` determines the offset relative to the parent view,
|
||||
width, height, and the offset relative to the viewport. Returns the values via
|
||||
an async callback.
|
||||
|
||||
Note that these measurements are not available until after the rendering has
|
||||
been completed.
|
||||
|
||||
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
|
||||
|
||||
Like `measure`, but measures the view relative to another view, specified as
|
||||
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
|
||||
the origin `x`, `y` of the ancestor view.
|
||||
|
||||
**setNativeProps**(nativeProps: Object)
|
||||
|
||||
This function sends props straight to the underlying DOM node. See the [direct
|
||||
manipulation](../guides/direct-manipulation.md) guide for cases where
|
||||
`setNativeProps` should be used.
|
||||
|
||||
## About `setNativeProps`
|
||||
|
||||
`setNativeProps` is the React Native equivalent to setting properties directly
|
||||
on a DOM node. Use direct manipulation when frequent re-rendering creates a
|
||||
performance bottleneck. Direct manipulation will not be a tool that you reach
|
||||
for frequently.
|
||||
|
||||
### `setNativeProps` and `shouldComponentUpdate`
|
||||
|
||||
`setNativeProps` is imperative and stores state in the native layer (DOM,
|
||||
UIView, etc.) and not within your React components, which makes your code more
|
||||
difficult to reason about. Before you use it, try to solve your problem with
|
||||
`setState` and `shouldComponentUpdate`.
|
||||
|
||||
## Avoiding conflicts with the render function
|
||||
### Avoiding conflicts with the render function
|
||||
|
||||
If you update a property that is also managed by the render function, you might
|
||||
end up with some unpredictable and confusing bugs because anytime the component
|
||||
re-renders and that property changes, whatever value was previously set from
|
||||
`setNativeProps` will be completely ignored and overridden.
|
||||
|
||||
## Why use `setNativeProps` on Web?
|
||||
### Why use `setNativeProps` on Web?
|
||||
|
||||
Using `setNativeProps` in web-specific code is required when making changes to
|
||||
`className` or `style`, as these properties are controlled by React Native for
|
||||
@@ -35,7 +76,7 @@ setOpacityTo(value) {
|
||||
}
|
||||
```
|
||||
|
||||
## Composite components and `setNativeProps`
|
||||
### Composite components and `setNativeProps`
|
||||
|
||||
Composite components are not backed by a DOM node, so you cannot call
|
||||
`setNativeProps` on them. Consider this example:
|
||||
@@ -63,7 +104,7 @@ prop on it and have that work - you would need to pass the style prop down to a
|
||||
child, unless you are wrapping a native component. Similarly, we are going to
|
||||
forward `setNativeProps` to a native-backed child component.
|
||||
|
||||
## Forward `setNativeProps` to a child
|
||||
### Forward `setNativeProps` to a child
|
||||
|
||||
All we need to do is provide a `setNativeProps` method on our component that
|
||||
calls `setNativeProps` on the appropriate child with the given arguments.
|
||||
@@ -86,7 +127,7 @@ class MyButton extends React.Component {
|
||||
|
||||
You can now use `MyButton` inside of `TouchableOpacity`!
|
||||
|
||||
## `setNativeProps` to clear `TextInput` value
|
||||
### `setNativeProps` to clear `TextInput` value
|
||||
|
||||
Another very common use case of `setNativeProps` is to clear the value of a
|
||||
`TextInput`. For example, the following code demonstrates clearing the input
|
||||
|
||||
211
docs/guides/getting-started.md
Normal file
211
docs/guides/getting-started.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# Getting started
|
||||
|
||||
This guide will help you to correctly configure build and test tools to work
|
||||
with React Native for Web. (Alternatively, you can quickly setup a local
|
||||
project using the starter kits listed in the README.)
|
||||
|
||||
It is recommended that your application provide a `Promise` and `Array.from`
|
||||
polyfill.
|
||||
|
||||
## Web packager
|
||||
|
||||
[Webpack](https://webpack.js.org) is a popular build tool for web apps. Below is an
|
||||
example of how to configure a build that uses [Babel](https://babeljs.io/) to
|
||||
compile your JavaScript for the web.
|
||||
|
||||
Create a `web/webpack.config.js` file:
|
||||
|
||||
```js
|
||||
// web/webpack.config.js
|
||||
|
||||
// This is needed for webpack to compile JavaScript.
|
||||
// Many OSS React Native packages are not compiled to ES5 before being
|
||||
// published. If you depend on uncompiled packages they may cause webpack build
|
||||
// errors. To fix this webpack can be configured to compile to the necessary
|
||||
// `node_module`.
|
||||
const babelLoaderConfiguration = {
|
||||
test: /\.js$/,
|
||||
// Add every directory that needs to be compiled by Babel during the build
|
||||
include: [
|
||||
path.resolve(__dirname, 'src'),
|
||||
path.resolve(__dirname, 'node_modules/react-native-uncompiled')
|
||||
],
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
// The 'react-native' preset is recommended (or use your own .babelrc)
|
||||
presets: ['react-native']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed for webpack to import static images in JavaScript files
|
||||
const imageLoaderConfiguration = {
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
use: {
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
name: '[name].[ext]'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
// ...the rest of your config
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
babelLoaderConfiguration,
|
||||
imageLoaderConfiguration
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
// `process.env.NODE_ENV === 'production'` must be `true` for production
|
||||
// builds to eliminate development checks and reduce build size. You may
|
||||
// wish to include additional optimizations.
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
})
|
||||
],
|
||||
|
||||
resolve: {
|
||||
// Maps the 'react-native' import to 'react-native-web'.
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
},
|
||||
// If you're working on a multi-platform React Native app, web-specific
|
||||
// module implementations should be written in files using the extension
|
||||
// `.web.js`.
|
||||
extensions: [ '.web.js', '.js' ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To run in development:
|
||||
|
||||
```
|
||||
./node_modules/.bin/webpack-dev-server -d --config web/webpack.config.js --inline --hot --colors
|
||||
```
|
||||
|
||||
To build for production:
|
||||
|
||||
```
|
||||
./node_modules/.bin/webpack -p --config web/webpack.config.js
|
||||
```
|
||||
|
||||
Please refer to the Webpack documentation for more information on configuration.
|
||||
|
||||
## Web entry
|
||||
|
||||
Create a `index.web.js` file (or simply `index.js` for web-only apps).
|
||||
|
||||
### Client-side rendering
|
||||
|
||||
Rendering using `AppRegistry`:
|
||||
|
||||
```js
|
||||
// index.web.js
|
||||
|
||||
import App from './src/App';
|
||||
import React from 'react';
|
||||
import ReactNative, { AppRegistry } from 'react-native';
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => App);
|
||||
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-app')
|
||||
});
|
||||
```
|
||||
|
||||
Rendering within existing web apps is also possible using `ReactNative`:
|
||||
|
||||
```js
|
||||
import AppHeader from './src/AppHeader';
|
||||
import React from 'react';
|
||||
import ReactNative from 'react-native';
|
||||
|
||||
ReactNative.render(<AppHeader />, document.getElementById('react-app-header'))
|
||||
```
|
||||
|
||||
And finally, `react-native-web` components will also be rendering within a tree
|
||||
produced by calling `ReactDOM.render` (i.e., an existing web app), but
|
||||
otherwise it is not recommended.
|
||||
|
||||
### Server-side rendering
|
||||
|
||||
Server-side rendering is supported using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import App from './src/App';
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => App)
|
||||
|
||||
// prerender the app
|
||||
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
|
||||
const initialHTML = ReactDOMServer.renderToString(element);
|
||||
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
|
||||
|
||||
// construct HTML document
|
||||
const document = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
${initialStyles}
|
||||
</head>
|
||||
<body>
|
||||
${initialHTML}
|
||||
`
|
||||
```
|
||||
|
||||
## Web-specific code
|
||||
|
||||
Minor platform differences can use the `Platform` module.
|
||||
|
||||
```js
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
height: (Platform.OS === 'web') ? 200 : 100,
|
||||
});
|
||||
```
|
||||
|
||||
More significant platform differences should use platform-specific files (see
|
||||
the webpack configuration above for resolving `*.web.js` files):
|
||||
|
||||
For example, with the following files in your project:
|
||||
|
||||
```
|
||||
MyComponent.android.js
|
||||
MyComponent.ios.js
|
||||
MyComponent.web.js
|
||||
```
|
||||
|
||||
And the following import:
|
||||
|
||||
```js
|
||||
import MyComponent from './MyComponent';
|
||||
```
|
||||
|
||||
React Native will automatically import the correct variant for each specific
|
||||
target platform.
|
||||
|
||||
## Testing with Jest
|
||||
|
||||
[Jest](https://facebook.github.io/jest/) also needs to map `react-native` to `react-native-web`.
|
||||
|
||||
```
|
||||
"jest": {
|
||||
"moduleNameMapper": {
|
||||
"react-native": "<rootDir>/node_modules/react-native-web"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to the Jest documentation for more information.
|
||||
@@ -4,11 +4,6 @@ To support right-to-left languages, application layout can be automatically
|
||||
flipped from LTR to RTL. The `I18nManager` API can be used to help with more
|
||||
fine-grained control and testing of RTL layouts.
|
||||
|
||||
React Native for Web provides an experimental feature to support "true left"
|
||||
and "true right" styles. For example, `left` will be flipped to `right` in RTL
|
||||
mode, but `left$noI18n` will not. More information is available in the `Text`
|
||||
and `View` documentation.
|
||||
|
||||
## Working with icons and images
|
||||
|
||||
Icons and images that must match the LTR or RTL layout of the app need to be manually flipped.
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
# Known issues
|
||||
|
||||
## Missing modules and Views
|
||||
## Safari flexbox performance
|
||||
|
||||
This is an initial release of React Native for Web, therefore, not all of the
|
||||
views present on iOS/Android are released on Web. We are very much interested in
|
||||
the community's feedback on the next set of modules and views.
|
||||
Safari version prior to 10.1 can suffer from extremely [poor flexbox
|
||||
performance](https://bugs.webkit.org/show_bug.cgi?id=150445). The recommended
|
||||
way to work around this issue (as used on mobile.twitter.com) is to set
|
||||
`display:block` on Views in your element hierarchy that you know don't need
|
||||
flexbox layout.
|
||||
|
||||
## Missing modules and components
|
||||
|
||||
Not all of the views present on iOS/Android are currently available on Web. We
|
||||
are very much interested in the community's feedback on the next set of modules
|
||||
and views.
|
||||
|
||||
Not all the modules or views for iOS/Android can be implemented on Web. In some
|
||||
cases it will be necessary to use a Web counterpart or to guard the use of a
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# React Native
|
||||
|
||||
Use a module loader that supports package aliases (e.g., webpack), and alias
|
||||
`react-native` to `react-native-web`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Image assets
|
||||
|
||||
In order to require image assets (e.g. `require('assets/myimage.png')`), add
|
||||
the `url-loader` to the webpack config:
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
module: {
|
||||
loaders: {
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: { name: '[name].[hash:16].[ext]' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Web-specific code
|
||||
|
||||
Minor platform differences can use the `Platform` module.
|
||||
|
||||
```js
|
||||
import { AppRegistry, Platform } from 'react-native'
|
||||
|
||||
AppRegistry.registerComponent('MyApp', () => MyApp)
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
AppRegistry.runApplication('MyApp', {
|
||||
rootTag: document.getElementById('react-root')
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
More substantial Web-specific implementation code should be written in files
|
||||
with the extension `.web.js`, which webpack will automatically resolve.
|
||||
|
||||
## Optimizations
|
||||
|
||||
Production builds can benefit from dead-code elimination by defining the
|
||||
following variables:
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,87 +0,0 @@
|
||||
# Client and Server rendering
|
||||
|
||||
It's recommended that you use a module loader that supports package aliases
|
||||
(e.g., webpack), and alias `react-native` to `react-native-web`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...other configuration
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `react-native-web` package also includes a `core` module that exports only
|
||||
`ReactNative`, `Image`, `StyleSheet`, `Text`, `TextInput`, and `View`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...other configuration
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web/core'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Client-side rendering
|
||||
|
||||
Rendering without using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import ReactNative from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppHeaderContainer = (props) => { /* ... */ }
|
||||
|
||||
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
|
||||
```
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppContainer = (props) => { /* ... */ }
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => AppContainer)
|
||||
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-app')
|
||||
})
|
||||
|
||||
// prerender the app
|
||||
const { html, styleElement } = AppRegistry.prerenderApplication('App', { initialProps })
|
||||
```
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
```
|
||||
import ReactDOMServer from 'react-dom/server'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppContainer = (props) => { /* ... */ }
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => AppContainer)
|
||||
|
||||
// prerender the app
|
||||
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
|
||||
const initialHTML = ReactDOMServer.renderToString(element);
|
||||
```
|
||||
@@ -1,80 +1,90 @@
|
||||
# Style
|
||||
|
||||
React Native for Web relies on JavaScript to define styles for your
|
||||
application. This allows you to avoid issues arising from the [7 deadly sins of
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js):
|
||||
React Native relies on JavaScript to define and resolve the styles of your
|
||||
application. React Native for Web implements the React Native style API in a
|
||||
way that avoids *all* the [problems with CSS at
|
||||
scale](https://speakerdeck.com/vjeux/react-css-in-js):
|
||||
|
||||
1. Global namespace
|
||||
2. Dependency hell
|
||||
1. No local variables
|
||||
2. Implicit dependencies
|
||||
3. No dead code elimination
|
||||
4. No code minification
|
||||
5. No sharing of constants
|
||||
6. Non-deterministic resolution
|
||||
7. Lack of isolation
|
||||
7. No isolation
|
||||
|
||||
At the same time, it has several benefits:
|
||||
|
||||
1. Simple API and expressive subset of CSS
|
||||
2. Generates CSS; the minimum required
|
||||
3. Good runtime performance
|
||||
4. Support for static and dynamic styles
|
||||
5. Support for RTL layouts
|
||||
6. Easy pre-rendering of critical CSS
|
||||
|
||||
## Defining styles
|
||||
|
||||
Styles should be defined outside of the component:
|
||||
Styles should be defined outside of the component. Using `StyleSheet.create` is
|
||||
optional but provides the best performance (by relying on generated CSS
|
||||
stylesheets). Avoid creating unregistered style objects.
|
||||
|
||||
```js
|
||||
class Example extends React.Component {}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
heading: {
|
||||
color: 'gray',
|
||||
fontSize: '2rem'
|
||||
},
|
||||
text: {
|
||||
color: 'gray',
|
||||
fontSize: '1.25rem'
|
||||
marginTop: '1rem',
|
||||
margin: 10
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Using `StyleSheet.create` is optional but provides some key advantages: styles
|
||||
are immutable in development, certain declarations are automatically converted
|
||||
to CSS rather than applied as inline styles, and styles are only created once
|
||||
for the application and not on every render.
|
||||
|
||||
The attribute names and values are a subset of CSS. See the `style`
|
||||
documentation of individual components.
|
||||
See the `style` documentation of individual components for supported properties.
|
||||
|
||||
## Using styles
|
||||
|
||||
All the React Native components accept a `style` attribute.
|
||||
All the React Native components accept a `style` property. The value can be a
|
||||
registered object, a plain object, or an array.
|
||||
|
||||
```js
|
||||
<Text style={styles.text} />
|
||||
// registered object
|
||||
<View style={styles.view} />
|
||||
|
||||
// plain object
|
||||
<View style={{ transform: [ { translateX } ] }} />
|
||||
|
||||
// array of registered or plain objects
|
||||
<View style={[ styles.container, this.props.style ]} />
|
||||
```
|
||||
|
||||
A common pattern is to conditionally add style based on a condition:
|
||||
The array syntax will merge styles from left-to-right as normal JavaScript
|
||||
objects, and can be used to conditionally apply styles:
|
||||
|
||||
```js
|
||||
// either
|
||||
<View style={[
|
||||
styles.base,
|
||||
styles.container,
|
||||
this.state.active && styles.active
|
||||
]} />
|
||||
|
||||
// or
|
||||
<View style={{
|
||||
...styles.base,
|
||||
...(this.state.active && styles.active)
|
||||
}} />
|
||||
```
|
||||
|
||||
When styles are registered with `StyleSheet.create`, the return value is a
|
||||
number and not a style object. This is important for performance optimizations,
|
||||
but still allows you to merge styles in a deterministic manner at runtime. If
|
||||
you need access to the underlying style objects you need to use
|
||||
`StyleSheet.flatten` (but be aware that this is not the optimized path).
|
||||
|
||||
## Composing styles
|
||||
|
||||
In order to let a call site customize the style of your component children, you
|
||||
can pass styles around. Use `View.propTypes.style` and `Text.propTypes.style` in
|
||||
order to make sure only valid styles are being passed.
|
||||
To let other components customize the style of a component's children you can
|
||||
expose a prop so styles can be explicitly passed into the component.
|
||||
|
||||
```js
|
||||
class List extends React.Component {
|
||||
static propTypes = {
|
||||
style: View.propTypes.style,
|
||||
elementStyle: View.propTypes.style,
|
||||
style: ViewPropTypes.style,
|
||||
elementStyle: ViewPropTypes.style,
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -97,56 +107,126 @@ In another file:
|
||||
|
||||
You also have much greater control over how styles are composed when compared
|
||||
to using class names. For example, you may choose to accept a limited subset
|
||||
of style props in the component's API, and control when they are applied:
|
||||
of style props in the component's API, and control when they are applied.
|
||||
|
||||
```js
|
||||
class List extends React.Component {
|
||||
static propTypes = {
|
||||
children: React.PropTypes.any,
|
||||
// limit which styles are accepted
|
||||
style: React.PropTypes.shape({
|
||||
borderColor: View.propTypes.borderColor,
|
||||
borderWidth: View.propTypes.borderWidth
|
||||
})
|
||||
}
|
||||
## How styles are resolved
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
children={children}
|
||||
style={[
|
||||
this.props.style,
|
||||
// override border-color when scrolling
|
||||
isScrolling && { borderColor: 'transparent' }
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
React Native style resolution is deterministic and slightly different from CSS.
|
||||
|
||||
In the following HTML/CSS example, the `.margin` selector is defined last in
|
||||
the CSS and takes precedence over the previous rules, resulting in a margin of
|
||||
`0, 0, 0, 0`.
|
||||
|
||||
```html
|
||||
<style>
|
||||
.marginTop { margin-top: 10px; }
|
||||
.marginBottom { margin-bottom: 20px; }
|
||||
.margin { margin: 0; }
|
||||
</style>
|
||||
<div class="marginTop marginBottom margin"></div>
|
||||
```
|
||||
|
||||
## Media Queries
|
||||
But in React Native the most *specific* style property takes precedence,
|
||||
resulting in margins of `10, 0, 20, 0`.
|
||||
|
||||
```js
|
||||
const style = [
|
||||
{ marginTop: 10 },
|
||||
{ marginBottom: 20 },
|
||||
{ margin: 0 }
|
||||
];
|
||||
|
||||
const Box = () => <View style={style} />
|
||||
```
|
||||
|
||||
## Implementation details
|
||||
|
||||
React Native for Web transforms React Native styles into React DOM styles. Any
|
||||
styles defined using `StyleSheet.create` will ultimately be rendered using CSS
|
||||
class names.
|
||||
|
||||
React Native for Web introduced a novel strategy to achieve this. Each rule is
|
||||
broken down into declarations, properties are expanded to their long-form, and
|
||||
the resulting key-value pairs are mapped to unique "atomic CSS" class names.
|
||||
|
||||
Input:
|
||||
|
||||
```js
|
||||
const Box = () => <View style={styles.box} />
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
margin: 0
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```html
|
||||
<style>
|
||||
.rn-1mnahxq { margin-top: 0px; }
|
||||
.rn-61z16t { margin-right: 0px; }
|
||||
.rn-p1pxzi { margin-bottom: 0px; }
|
||||
.rn-11wrixw { margin-left: 0px; }
|
||||
</style>
|
||||
|
||||
<div class="rn-156q2ks rn-61z16t rn-p1pxzi rn-11wrixw"></div>
|
||||
```
|
||||
|
||||
This ensures that CSS order doesn't impact rendering and CSS rules are
|
||||
efficiently deduplicated. Rather than the total CSS growing in proportion to
|
||||
the number of *rules*, it grows in proportion to the number of *unique
|
||||
declarations*. As a result, the DOM style sheet is only written to when new
|
||||
unique declarations are defined and it is usually small enough to be
|
||||
pre-rendered and inlined.
|
||||
|
||||
Class names are deterministic, which means that the resulting CSS and HTML is
|
||||
consistent across builds – important for large apps using code-splitting and
|
||||
deploying incremental updates.
|
||||
|
||||
At runtime registered styles are resolved to DOM style props and memoized.
|
||||
Any dynamic styles that contain declarations previously registered as static
|
||||
styles can also be converted to CSS class names. Otherwise, they render as
|
||||
inline styles.
|
||||
|
||||
All this allows React Native for Web to support the rich functionality of React
|
||||
Native styles (including RTL layouts and `setNativeProps`) while providing one
|
||||
of the [fastest](https://github.com/necolas/react-native-web/blob/master/benchmarks/README.md),
|
||||
safest, and most efficient styles-in-JavaScript solutions.
|
||||
|
||||
## FAQs
|
||||
|
||||
### What about Media Queries?
|
||||
|
||||
`StyleSheet.create` is a way of defining the styles your application requires;
|
||||
it does not concern itself with _where_ or _when_ those styles are applied to
|
||||
elements.
|
||||
|
||||
There are various React libraries wrapping JavaScript Media Query API's, e.g.,
|
||||
Media Queries may not be most appropriate for component-based designs. React
|
||||
Native provides the `Dimensions` API and `onLayout` props. If you do need Media
|
||||
Queries, using the `matchMedia` DOM API has the benefit of allowing you to swap
|
||||
out entire components, not just styles. There are also many React libraries
|
||||
wrapping JavaScript Media Query API's, e.g.,
|
||||
[react-media](https://github.com/reacttraining/react-media),
|
||||
[react-media-queries](https://github.com/bloodyowl/react-media-queries),
|
||||
[media-query-fascade](https://github.com/tanem/media-query-facade), or
|
||||
[react-responsive](https://github.com/contra/react-responsive). This has the
|
||||
benefit of co-locating breakpoint-specific DOM and style changes.
|
||||
[react-responsive](https://github.com/contra/react-responsive).
|
||||
|
||||
## Pseudo-classes and pseudo-elements
|
||||
### What about pseudo-classes and pseudo-elements?
|
||||
|
||||
Pseudo-classes like `:hover` and `:focus` can be implemented with events (e.g.
|
||||
`onFocus`). Pseudo-elements are not supported; elements should be used instead.
|
||||
|
||||
### Reset
|
||||
### Do I need a CSS reset?
|
||||
|
||||
You **do not** need to include a CSS reset or
|
||||
[normalize.css](https://necolas.github.io/normalize.css/).
|
||||
No. React Native for Web includes a very small CSS reset that removes unwanted
|
||||
User Agent styles from (pseudo-)elements beyond the reach of React (e.g.,
|
||||
`html`, `body`) or inline styles (e.g., `::-moz-focus-inner`). The rest is
|
||||
handled at the component-level.
|
||||
|
||||
React Native for Web includes a very small CSS reset taken from normalize.css.
|
||||
It removes unwanted User Agent styles from (pseudo-)elements beyond the reach
|
||||
of React (e.g., `html`, `body`) or inline styles (e.g., `::-moz-focus-inner`).
|
||||
### What about using Dev Tools?
|
||||
|
||||
React Dev Tools supports inspecting and editing of React Native styles. It's
|
||||
recommended that you rely more on React Dev Tools and live/hot-reloading rather
|
||||
than inspecting and editing the DOM directly.
|
||||
|
||||
11
docs/package.json
Normal file
11
docs/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "yarn && build-storybook -o ./dist -c ./storybook/.storybook",
|
||||
"start": "start-storybook -p 9001 -c ./storybook/.storybook",
|
||||
"release": "yarn build && git checkout gh-pages && rm -rf ../storybook && mv dist ../storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addon-options": "^3.1.6",
|
||||
"@storybook/react": "^3.1.9"
|
||||
}
|
||||
}
|
||||
BIN
docs/static/components.png
vendored
BIN
docs/static/components.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
BIN
docs/static/styling-strategy.png
vendored
BIN
docs/static/styling-strategy.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
1
docs/storybook/.storybook/addons.js
Normal file
1
docs/storybook/.storybook/addons.js
Normal file
@@ -0,0 +1 @@
|
||||
import '@storybook/addon-options/register';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user