mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-30 23:23:35 +08:00
Compare commits
671 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
8c7cdbf4be | ||
|
|
e5d8857bcc | ||
|
|
cda8d05bb7 | ||
|
|
049edc4611 | ||
|
|
e76d5a4e6c | ||
|
|
f71dae7d93 | ||
|
|
94d31beaf4 | ||
|
|
f5f9389728 | ||
|
|
fdbd19a4af | ||
|
|
36eafbc2f5 | ||
|
|
bca3398c1c | ||
|
|
722d77e8e5 | ||
|
|
d65c92eea9 | ||
|
|
8dd39c681c | ||
|
|
0b1759363d | ||
|
|
abd1227a94 | ||
|
|
8352c7cbda | ||
|
|
89f5a13891 | ||
|
|
4005f9ddde | ||
|
|
f192a9ba26 | ||
|
|
e688a949fb | ||
|
|
77605cb27c | ||
|
|
4f71833aec | ||
|
|
fa14995a35 | ||
|
|
4beae0dd78 | ||
|
|
5598961d2c | ||
|
|
4613baf0e8 | ||
|
|
3b661d8d6d | ||
|
|
22d20706e3 | ||
|
|
0b2813b186 | ||
|
|
b248de552d | ||
|
|
2b826dc7f4 | ||
|
|
b46acd4f50 | ||
|
|
5a03cb25cb | ||
|
|
44e60d12e3 | ||
|
|
fc60f8d332 | ||
|
|
2a65ca6fc0 | ||
|
|
9db3bd7e67 | ||
|
|
1963e9109a | ||
|
|
14072c7471 | ||
|
|
0af6dc00fc | ||
|
|
c9d401f09a | ||
|
|
8aeeed0ef7 | ||
|
|
f5d0f73b4f | ||
|
|
ee7d367062 | ||
|
|
dbd607ce47 | ||
|
|
373cb38ca9 | ||
|
|
4576b42365 | ||
|
|
5a5707855b | ||
|
|
0c76cc5d80 | ||
|
|
d64df129b2 | ||
|
|
763c5444ce | ||
|
|
88d13f06f8 | ||
|
|
3dd94880e0 | ||
|
|
e1080d72d7 | ||
|
|
55849cdd0d | ||
|
|
0aef117733 | ||
|
|
977d8729f5 | ||
|
|
9222cbf4bd | ||
|
|
103560fc11 | ||
|
|
3a2daf386d | ||
|
|
6640b61b3e | ||
|
|
07d1124d60 | ||
|
|
c7b3a8e60b | ||
|
|
d32eec7239 | ||
|
|
f8f2898095 | ||
|
|
201bfd2e4d | ||
|
|
496839d19a | ||
|
|
6fe3f1f533 | ||
|
|
aa53d931d5 | ||
|
|
88b184d540 | ||
|
|
011affb110 | ||
|
|
87a4f56a89 | ||
|
|
2f94295739 | ||
|
|
fdf4a88251 | ||
|
|
acc7032d60 | ||
|
|
0fc5644959 | ||
|
|
be923ec453 | ||
|
|
248c2e1258 | ||
|
|
2e822c068d | ||
|
|
fb5406e4ec | ||
|
|
638479991e | ||
|
|
fb41be1bf7 | ||
|
|
2291544373 | ||
|
|
6416166bc3 | ||
|
|
f1e221e51e | ||
|
|
27edfd9d88 | ||
|
|
6cadc22ad5 | ||
|
|
5c8cdd7742 | ||
|
|
e3450ed26c | ||
|
|
d54a84701a | ||
|
|
8201906703 | ||
|
|
10f88670ed | ||
|
|
1be2c810d1 | ||
|
|
6f75fb3e0d | ||
|
|
424e6b5994 | ||
|
|
769931061a | ||
|
|
7aa760506a | ||
|
|
9401eb9b47 | ||
|
|
6a9212df40 | ||
|
|
f2772b89bf | ||
|
|
9e970b3c34 | ||
|
|
9677e9da0a | ||
|
|
2440e74e99 | ||
|
|
66b0387023 | ||
|
|
94f37740af | ||
|
|
e1991f8f6b | ||
|
|
21eeafabd5 | ||
|
|
249f157ed9 | ||
|
|
0f8cff6124 | ||
|
|
30bf00a3bc | ||
|
|
f4515a3995 | ||
|
|
17b30aceb2 | ||
|
|
5f3f4db7a6 | ||
|
|
eb8aa0a9db | ||
|
|
af60504ca4 | ||
|
|
41159bcb10 | ||
|
|
640e41dc34 | ||
|
|
c609a6ff2b | ||
|
|
294d94d869 | ||
|
|
179d624917 | ||
|
|
61860b6d49 | ||
|
|
597fcc65e8 | ||
|
|
5e1e0ec8e5 | ||
|
|
0ac243038f | ||
|
|
c9d68fe93e | ||
|
|
77f72aa129 | ||
|
|
216885406f | ||
|
|
f15bf2664a | ||
|
|
79998e0acc | ||
|
|
44fc48f7a0 | ||
|
|
37f2d78f34 | ||
|
|
1dc769bfb1 | ||
|
|
4b3cb41107 | ||
|
|
ed2cbfd5d3 | ||
|
|
8c4b5b68c3 | ||
|
|
3564bbf840 | ||
|
|
297b2e5afb | ||
|
|
215697234e | ||
|
|
9efa7e94bd | ||
|
|
c44da41497 | ||
|
|
331c92fb3a | ||
|
|
26758e905c | ||
|
|
a15b15c55d | ||
|
|
f0202dbe61 | ||
|
|
d4d67dafc0 | ||
|
|
579bdeb8a5 | ||
|
|
7132a18440 | ||
|
|
18881b1edb | ||
|
|
4d1e7d8c0b | ||
|
|
a7158aeb6f | ||
|
|
03d413bca4 | ||
|
|
aef5efbad3 | ||
|
|
8fb8645723 | ||
|
|
d69406b4b1 | ||
|
|
2c2a96a183 | ||
|
|
b4a3053b5b | ||
|
|
24836afd6a | ||
|
|
c46f242f6b | ||
|
|
1940868065 | ||
|
|
65a9317756 | ||
|
|
3da05c48b0 | ||
|
|
f33312a4dd | ||
|
|
4516c72296 | ||
|
|
7f94c4bf06 | ||
|
|
37781171aa | ||
|
|
22f45e350b | ||
|
|
af40f98f23 | ||
|
|
eca2f69593 | ||
|
|
d03d89ac71 | ||
|
|
393a6ef835 | ||
|
|
36e89d5275 | ||
|
|
d53d1e6e56 | ||
|
|
2cb68a45be | ||
|
|
b56b8e494a | ||
|
|
60ad0e9ec5 | ||
|
|
f2ea7c089c | ||
|
|
a3b59ed2b4 | ||
|
|
a378d3cce2 | ||
|
|
462f9793ea | ||
|
|
ae38bb538c | ||
|
|
93d1488cc7 | ||
|
|
a16e542bd8 | ||
|
|
62cd335788 | ||
|
|
288e14cd70 | ||
|
|
71cfd23624 | ||
|
|
77b8e4a1fc | ||
|
|
9543a79c3f | ||
|
|
e3eea6e132 | ||
|
|
4d3418a968 | ||
|
|
ea9bc734f1 | ||
|
|
e03af435ac | ||
|
|
97c0a31ce6 | ||
|
|
25d11ded46 | ||
|
|
6a73d77030 | ||
|
|
0b63ba4e89 | ||
|
|
51109d0768 | ||
|
|
ac04ecd69e | ||
|
|
1a670ba6a7 | ||
|
|
7a16d5711c | ||
|
|
9dde70fff5 | ||
|
|
203980ab66 | ||
|
|
924dc36d4a | ||
|
|
9b2421cdfa | ||
|
|
36ea662402 | ||
|
|
69962ae815 | ||
|
|
62d1a0f83d | ||
|
|
910286303a | ||
|
|
706fa887e6 | ||
|
|
c589d79035 | ||
|
|
83e4c68461 | ||
|
|
54597edbaf | ||
|
|
fc31287566 | ||
|
|
21cc8f47ba | ||
|
|
bf7beb4102 | ||
|
|
127d103c0a | ||
|
|
ae6132af56 | ||
|
|
3c4d7655db | ||
|
|
190966f411 | ||
|
|
8d5ecb84d5 | ||
|
|
b4a9177ce3 | ||
|
|
ad4a6c5be7 | ||
|
|
5f795dfc6c | ||
|
|
949cb75894 | ||
|
|
2e1914080f | ||
|
|
49e9e0ab5b | ||
|
|
ee4c544957 | ||
|
|
56549cf794 | ||
|
|
e6811b2134 | ||
|
|
d8b7dcc60f | ||
|
|
62a08f09ab | ||
|
|
3e7cd1a001 | ||
|
|
8441755d61 | ||
|
|
ba9fa2a7a0 | ||
|
|
e26edfb9ea | ||
|
|
9a8a9ad209 | ||
|
|
efccbe41bb | ||
|
|
f6f8d30aba | ||
|
|
6d7d98c149 | ||
|
|
77d201988d | ||
|
|
b3d7332ddd | ||
|
|
651d519500 | ||
|
|
06d8614519 | ||
|
|
3eced7e842 | ||
|
|
e627e0cd77 | ||
|
|
7ab33727c4 | ||
|
|
7295a8fee8 | ||
|
|
9a09456532 | ||
|
|
4cd38552cd | ||
|
|
b0f35f6c66 | ||
|
|
2dff45b561 | ||
|
|
fd9232201d | ||
|
|
29f6bd363c | ||
|
|
4845de5cb5 | ||
|
|
267a9b55bf | ||
|
|
7add5c524a | ||
|
|
8e0d94e092 | ||
|
|
25f96ba8ae | ||
|
|
2b90bd736f | ||
|
|
791ede06dd | ||
|
|
0567232942 | ||
|
|
e5ecc26d21 | ||
|
|
715c71b215 | ||
|
|
f8554ecc1e | ||
|
|
3292ced765 | ||
|
|
1c7fb4cb45 | ||
|
|
60409bea18 | ||
|
|
5c74c0efb7 | ||
|
|
a0187f9b1a | ||
|
|
74ef265d83 | ||
|
|
97b3a91c0e | ||
|
|
c65aa8a943 | ||
|
|
0aa60a3c29 | ||
|
|
8ac84f6da5 | ||
|
|
69166b1502 | ||
|
|
cc10de43eb | ||
|
|
c850a5fa8c | ||
|
|
1efe5a533f | ||
|
|
804132ce36 | ||
|
|
5335bcfd48 | ||
|
|
c0e7afc495 | ||
|
|
fa88548c3c | ||
|
|
39b2b2f979 | ||
|
|
fd04d65b03 | ||
|
|
0ab984f507 | ||
|
|
3d1ad50a58 | ||
|
|
92554321df | ||
|
|
1c9270c4ea | ||
|
|
8a5f9cd7d9 | ||
|
|
aac6b796b2 | ||
|
|
c77ce19f1b | ||
|
|
25b74d30c4 | ||
|
|
4191d58694 | ||
|
|
11b861ae64 | ||
|
|
68bf08112a | ||
|
|
b277b3e509 | ||
|
|
c135dddbd1 | ||
|
|
ffc6368162 | ||
|
|
501c19fe9b | ||
|
|
e1da11fa1d | ||
|
|
b2a4d742a9 | ||
|
|
8b965fdfa0 | ||
|
|
8cfef85934 | ||
|
|
6db24e9358 | ||
|
|
13e36bee65 | ||
|
|
93e8e90a1a | ||
|
|
894fd0362d | ||
|
|
a1664927ce | ||
|
|
ae2abc578a | ||
|
|
5f7b3f5fef | ||
|
|
75f653818a | ||
|
|
2c52d41b75 | ||
|
|
83f749d983 | ||
|
|
bf5046415c | ||
|
|
885d4586a9 | ||
|
|
ea0a778ba3 | ||
|
|
0a7eda2505 | ||
|
|
35385e7b69 | ||
|
|
3fd29697c0 | ||
|
|
a26033be2d | ||
|
|
fdb4ee4aae | ||
|
|
08300f624f | ||
|
|
7f5a2807e2 | ||
|
|
292f045c52 | ||
|
|
a19b57df4d | ||
|
|
1c444569ae | ||
|
|
0b8c4b8746 | ||
|
|
6772233837 | ||
|
|
cd89f88d96 | ||
|
|
b59bdb17b2 | ||
|
|
2bfa579fe4 | ||
|
|
f4c6e33b2f | ||
|
|
39b273f9d8 | ||
|
|
ec9985a3b3 | ||
|
|
0aa29d8816 | ||
|
|
45777e0405 | ||
|
|
84e06564d4 | ||
|
|
1d1b633317 | ||
|
|
565ec2fee8 | ||
|
|
33082f988e | ||
|
|
a2fb65a79c | ||
|
|
e727193809 | ||
|
|
d6db206ec4 | ||
|
|
b3beea9bb3 | ||
|
|
ef4de789ae | ||
|
|
86037ab740 | ||
|
|
6c20646f10 | ||
|
|
b5aa68dc7a | ||
|
|
5668c40ee6 | ||
|
|
be86250ac6 | ||
|
|
1e04dfc306 | ||
|
|
283ab2fa2e | ||
|
|
09dd9a224a | ||
|
|
c7524b7b6f | ||
|
|
5453c8843a | ||
|
|
e0f836ccb5 | ||
|
|
eada8e7fc7 | ||
|
|
114fb5f8c7 | ||
|
|
4aa87c79fa | ||
|
|
9107fd3de9 | ||
|
|
c72173ff88 | ||
|
|
edf0fda75a | ||
|
|
3b848fe378 | ||
|
|
7eff1a644e | ||
|
|
2750d70a93 | ||
|
|
65559f50e6 | ||
|
|
77fd21ea44 | ||
|
|
38e4de76cd | ||
|
|
0f42cd83e1 | ||
|
|
e6cbea82c4 | ||
|
|
6d4c9e881f | ||
|
|
b9c8a560a0 | ||
|
|
357201e843 | ||
|
|
0ab345a490 | ||
|
|
2e087c10ad | ||
|
|
a7c1e105c6 | ||
|
|
969b57de53 | ||
|
|
50771fc5cd | ||
|
|
cd215a916d | ||
|
|
1668dc4635 | ||
|
|
f0b6576e80 | ||
|
|
7551596fa5 | ||
|
|
90e015112a | ||
|
|
cfdbd351f0 | ||
|
|
08013daa23 | ||
|
|
e7a5d56058 | ||
|
|
abf2c0307f | ||
|
|
6bb6a17046 | ||
|
|
b5c8af2694 | ||
|
|
17d993261b | ||
|
|
ef4064d966 | ||
|
|
b9e2007b59 | ||
|
|
247f489fdd | ||
|
|
d5b3b60c04 | ||
|
|
6b55032e49 | ||
|
|
77f73a8929 | ||
|
|
f951de43a2 | ||
|
|
fb7a997256 | ||
|
|
1417dd2e6a | ||
|
|
ff5c8f64cc | ||
|
|
653cfd71ce | ||
|
|
95d6b98e9d | ||
|
|
92dbadacb5 | ||
|
|
d3dce675df | ||
|
|
000bbf9f93 | ||
|
|
9d424fa529 | ||
|
|
344239bd8a | ||
|
|
8ec27cefab | ||
|
|
ecc23f46d4 | ||
|
|
39088affbc | ||
|
|
5494a8e191 | ||
|
|
95b1af9f1f | ||
|
|
1f3ac7a7b8 | ||
|
|
d86c2f4840 |
153
.eslintrc
Normal file
153
.eslintrc
Normal file
@@ -0,0 +1,153 @@
|
||||
{
|
||||
// babel parser to support ES6/7 features
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 7,
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"prettier",
|
||||
"prettier/react"
|
||||
],
|
||||
"plugins": [
|
||||
"promise",
|
||||
"react"
|
||||
],
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"document": false,
|
||||
"navigator": false,
|
||||
"window": false,
|
||||
// Flow global types
|
||||
"HTMLInputElement": false,
|
||||
"ReactClass": false,
|
||||
"ReactComponent": false,
|
||||
"ReactElement": false,
|
||||
"ReactPropsChainableTypeChecker": false,
|
||||
"ReactPropsCheckType": false,
|
||||
"ReactPropTypes": false,
|
||||
"SyntheticEvent": false
|
||||
},
|
||||
"rules": {
|
||||
"camelcase": 0,
|
||||
"constructor-super": 2,
|
||||
"default-case": [2, { commentPattern: '^no default$' }],
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
|
||||
"no-alert": 1,
|
||||
"no-array-constructor": 2,
|
||||
"no-caller": 2,
|
||||
"no-case-declarations": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-cond-assign": 2,
|
||||
"no-const-assign": 2,
|
||||
"no-control-regex": 2,
|
||||
"no-debugger": 2,
|
||||
"no-delete-var": 2,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-duplicate-imports": 2,
|
||||
"no-empty-character-class": 2,
|
||||
"no-empty-pattern": 2,
|
||||
"no-eval": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-boolean-cast": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-inner-declarations": [2, "functions"],
|
||||
"no-invalid-regexp": 2,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-iterator": 2,
|
||||
"no-label-var": 2,
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-new": 2,
|
||||
"no-new-func": 2,
|
||||
"no-new-object": 2,
|
||||
"no-new-require": 2,
|
||||
"no-new-symbol": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-obj-calls": 2,
|
||||
"no-octal": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-path-concat": 2,
|
||||
"no-proto": 2,
|
||||
"no-redeclare": 2,
|
||||
"no-regex-spaces": 2,
|
||||
"no-return-assign": [2, "except-parens"],
|
||||
"no-script-url": 2,
|
||||
"no-self-assign": 2,
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-sparse-arrays": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-undef": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unmodified-loop-condition": 2,
|
||||
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-finally": 2,
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
|
||||
"no-useless-call": 2,
|
||||
"no-useless-computed-key": 2,
|
||||
"no-useless-concat": 2,
|
||||
"no-useless-constructor": 2,
|
||||
"no-useless-escape": 2,
|
||||
"no-var": 2,
|
||||
"no-with": 2,
|
||||
"prefer-const": 2,
|
||||
"prefer-rest-params": 2,
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"radix": 2,
|
||||
"use-isnan": 2,
|
||||
"valid-typeof": 2,
|
||||
"yoda": [2, "never"],
|
||||
|
||||
// promise
|
||||
"promise/param-names": 2,
|
||||
|
||||
// react
|
||||
"react/display-name": 0,
|
||||
"react/jsx-no-bind": 2,
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-pascal-case": 2,
|
||||
"react/jsx-sort-props": 2,
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
"react/no-did-update-set-state": 2,
|
||||
"react/no-direct-mutation-state": 2,
|
||||
"react/no-multi-comp": 0,
|
||||
"react/no-string-refs": 2,
|
||||
"react/no-unknown-property": 2,
|
||||
"react/prefer-es6-class": 2,
|
||||
"react/prop-types": 2,
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/sort-comp": 0,
|
||||
"react/sort-prop-types": 2,
|
||||
"react/wrap-multilines": 0
|
||||
}
|
||||
}
|
||||
13
.flowconfig
Normal file
13
.flowconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
[ignore]
|
||||
.*/__tests__/.*
|
||||
.*/benchmarks/.*
|
||||
.*/docs/.*
|
||||
.*/node_modules/animated/*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
types
|
||||
|
||||
[options]
|
||||
unsafe.enable_getters_and_setters=true
|
||||
109
.github/CONTRIBUTING.md
vendored
Normal file
109
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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
|
||||
```
|
||||
|
||||
## Unit tests
|
||||
|
||||
To run the unit tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
…in watch mode:
|
||||
|
||||
```
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
## Visual tests
|
||||
|
||||
Run the interactive storybook:
|
||||
|
||||
```
|
||||
npm run docs:start
|
||||
```
|
||||
|
||||
Run generate a static build of the storybook:
|
||||
|
||||
```
|
||||
npm run docs:build
|
||||
```
|
||||
|
||||
Run the performance benchmarks in a browser (opening `./performance/index.html`):
|
||||
|
||||
```
|
||||
npm run benchmarks
|
||||
```
|
||||
|
||||
## Compile and build
|
||||
|
||||
Compile the source code to `dist`:
|
||||
|
||||
```
|
||||
npm run compile
|
||||
```
|
||||
|
||||
To create a UMD bundle of the library:
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Pre-commit
|
||||
|
||||
Before creating a commit run:
|
||||
|
||||
```
|
||||
npm run precommit
|
||||
```
|
||||
|
||||
To format and lint the entire project:
|
||||
|
||||
```
|
||||
npm run fmt
|
||||
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!
|
||||
30
.github/ISSUE_TEMPLATE.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
**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**
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
**Environment (include versions)**
|
||||
|
||||
OS:
|
||||
Device:
|
||||
Browser:
|
||||
React Native for Web (version):
|
||||
React (version):
|
||||
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<!--
|
||||
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 benchmark reports
|
||||
- [ ] includes an interactive example
|
||||
- [ ] includes screenshots/videos
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,2 @@
|
||||
/dist
|
||||
/node_modules
|
||||
node_modules
|
||||
dist
|
||||
|
||||
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
31
LICENSE
Normal file
31
LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
BSD License
|
||||
|
||||
For React Native software
|
||||
|
||||
Copyright (c) 2015-present, Nicolas Gallagher. All rights reserved.
|
||||
Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
219
README.md
219
README.md
@@ -1,137 +1,146 @@
|
||||
# react-web-sdk
|
||||
# React Native for Web
|
||||
|
||||
**Experimental / Proof of concept**
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![npm version][npm-image]][npm-url]
|
||||
|
||||
A React SDK (~9KB gzipped) for creating web applications and toolkits. Inspired by `react-native`.
|
||||
[React Native][react-native-url] components and APIs for the Web.
|
||||
|
||||
It includes the following components:
|
||||
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
|
||||
|
||||
* `View`: a flexbox primitive
|
||||
* `Text`: a text primitive
|
||||
* `Image`: an image primitive
|
||||
* (`Component`: base / implementation)
|
||||
[npm-image]: https://badge.fury.io/js/react-native-web.svg
|
||||
[npm-url]: https://npmjs.org/package/react-native-web
|
||||
[react-native-url]: https://facebook.github.io/react-native/
|
||||
[travis-image]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/necolas/react-native-web
|
||||
|
||||
And uses a [styling strategy](docs/styling-strategy.md) that maps inline styles
|
||||
to single-purpose CSS rules.
|
||||
## Overview
|
||||
|
||||
This proof of concept uses a CSS bundle (~4.5KB gzipped) of 300+ precomputed
|
||||
declarations. A more sophisticated implementation is likely to produce a
|
||||
slightly larger CSS file and fewer inline styles.
|
||||
"React Native for Web" is a project to bring React Native's building blocks and
|
||||
touch handling to the Web. [Read more](#why).
|
||||
|
||||
## Components
|
||||
Browse the [UI Explorer](https://necolas.github.io/react-native-web/storybook/)
|
||||
to see React Native examples running on Web. Or try it out online with [React
|
||||
Native for Web: Playground](https://www.webpackbin.com/bins/-KgucwxRbn7HRU-V-3Bc).
|
||||
|
||||
All components define explicit `style` PropTypes according to the [`StyleProp`
|
||||
spec](docs/StyleProp.spec.md).
|
||||
## Quick start
|
||||
|
||||
### Component
|
||||
To install in your app:
|
||||
|
||||
TODO
|
||||
```
|
||||
npm install --save react@15.5 react-dom@15.5 react-native-web
|
||||
```
|
||||
|
||||
[`Component`](docs/Component.md)
|
||||
Read the [Getting Started](docs/guides/getting-started.md) guide.
|
||||
|
||||
### View
|
||||
## Documentation
|
||||
|
||||
TODO
|
||||
Guides:
|
||||
|
||||
A flexbox container; foundational layout building block.
|
||||
* [Getting started](docs/guides/getting-started.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
* [Accessibility](docs/guides/accessibility.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)
|
||||
|
||||
[`View` spec](docs/View.spec.md)
|
||||
Exported modules:
|
||||
|
||||
See this [guide to flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/).
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Button`](docs/components/Button.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)
|
||||
* [`Clipboard`](docs/apis/Clipboard.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)
|
||||
|
||||
### Text
|
||||
<span id="#why"></span>
|
||||
|
||||
TODO
|
||||
## Why?
|
||||
|
||||
[`Text` spec](docs/View.spec.md)
|
||||
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: component styles, animation, touch
|
||||
interactions, layout adaptation, accessibility, RTL layout, theming, and build-
|
||||
or server-rendering.
|
||||
|
||||
### Image
|
||||
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. 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.
|
||||
|
||||
TODO
|
||||
|
||||
[`Image` spec](docs/View.spec.md)
|
||||
|
||||
## Styling
|
||||
|
||||
Styling is identical to using inline styles in React, but most inline styles
|
||||
are converted to unique CSS classes. **This is only true for the SDK
|
||||
components**.
|
||||
|
||||
The companion stylesheet can be referenced as an external resource, inlined, or
|
||||
injected by JS.
|
||||
|
||||
See the [styling strategy docs](docs/styling-strategy.md) for more details.
|
||||
|
||||
### Use plain JavaScript objects
|
||||
|
||||
Use JavaScript to write style definitions in React components:
|
||||
## Example code
|
||||
|
||||
```js
|
||||
const style = {
|
||||
common: {
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '1em'
|
||||
import React from 'react'
|
||||
import { AppRegistry, Image, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
// Components
|
||||
const Card = ({ children }) => <View style={styles.card}>{children}</View>
|
||||
const Title = ({ children }) => <Text style={styles.title}>{children}</Text>
|
||||
const Photo = ({ uri }) => <Image source={{ uri }} style={styles.image} />
|
||||
const App = () => (
|
||||
<Card>
|
||||
<Title>App Card</Title>
|
||||
<Photo uri="/some-photo.jpg" />
|
||||
</Card>
|
||||
)
|
||||
|
||||
// Styles
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
title: {
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
image: {
|
||||
opacity: 0.5
|
||||
},
|
||||
text: {
|
||||
fontWeight: '300'
|
||||
height: 40,
|
||||
marginVertical: 10,
|
||||
width: 40
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
// App registration and rendering
|
||||
AppRegistry.registerComponent('MyApp', () => App)
|
||||
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
|
||||
```
|
||||
|
||||
### Use the `style` attribute
|
||||
## Related projects
|
||||
|
||||
The `style` attribute is a simple API for creating element-scoped styles.
|
||||
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
|
||||
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
|
||||
* [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack)
|
||||
* [reactxp](https://github.com/microsoft/reactxp)
|
||||
* [react-web](https://github.com/taobaofed/react-web)
|
||||
|
||||
```js
|
||||
import {View} from 'react-web-sdk';
|
||||
## License
|
||||
|
||||
class Example extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={style.root}>...</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Combine and override style objects:
|
||||
|
||||
```js
|
||||
import baseStyle from './baseStyle';
|
||||
|
||||
const buttonStyle = {
|
||||
...baseStyle,
|
||||
backgroundColor: '#333',
|
||||
color: '#fff'
|
||||
}
|
||||
```
|
||||
|
||||
## Utilities
|
||||
|
||||
#### `getOtherProps(reactComponentInstance)`
|
||||
|
||||
Returns an object containing all the props from `this.props` that are not
|
||||
defined in `propTypes`.
|
||||
|
||||
#### `omitProps(obj, arrayOfExcludedProps)`
|
||||
|
||||
Returns an object with the specified props excluded.
|
||||
|
||||
#### `pickProps(obj, arrayOfIncludedProps)`
|
||||
|
||||
Returns an object with the specified props included.
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run build:example:watch
|
||||
open example/index.html
|
||||
```
|
||||
React Native for Web is [BSD licensed](LICENSE).
|
||||
|
||||
40
benchmarks/README.md
Normal file
40
benchmarks/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Performance
|
||||
|
||||
To run these benchmarks:
|
||||
|
||||
```
|
||||
npm run build:performance
|
||||
open ./performance/index.html
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
`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` | `87.67` `±15.22` | `170.85` `±16.87` | |
|
||||
| `react-native-web/stylesheet@0.0.84` | `90.02` `±13.16` | `186.66` `±19.23` | |
|
||||
| `react-native-web@0.0.84` | `102.72` `±19.26` | `222.35` `±18.95` | `12.81` `±5.45ms` |
|
||||
|
||||
Other libraries
|
||||
|
||||
| Implementation | Deep tree (ms) | Wide tree (ms) |
|
||||
| :--- | ---: | ---: |
|
||||
| `styletron@2.5.1` | `88.48` `±12.00` | `171.89` `±13.28` |
|
||||
| `aphrodite@1.2.0` | `101.32` `±20.33` | `220.33` `±31.41` |
|
||||
| `glamor@3.0.0-1` | `129.00` `±14.92` | `264.57` `±28.54` |
|
||||
| `react-jss@5.4.1` | `137.33` `±21.55` | `314.91` `±29.03` |
|
||||
| `reactxp@0.34.3` | `223.82` `±32.77` | `461.56` `±34.43` |
|
||||
| `styled-components@2.0.0-11` | `277.53` `±28.83` | `627.91` `±43.13` |
|
||||
|
||||
*MacBook Pro (13-inch, Early 2011); 2.7 GHz Intel Core i7; 16 GB 1600 MHz DDR3. Google Chrome 56.
|
||||
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;
|
||||
20
benchmarks/createRenderBenchmark.js
Normal file
20
benchmarks/createRenderBenchmark.js
Normal file
@@ -0,0 +1,20 @@
|
||||
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>
|
||||
49
benchmarks/index.js
Normal file
49
benchmarks/index.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import aphrodite from './src/aphrodite';
|
||||
import cssModules from './src/css-modules';
|
||||
import glamor from './src/glamor';
|
||||
import jss from './src/jss';
|
||||
import reactNative from './src/react-native';
|
||||
import reactNativeStyleSheet from './src/react-native-stylesheet';
|
||||
import styledComponents from './src/styled-components';
|
||||
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 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)
|
||||
];
|
||||
|
||||
/**
|
||||
* Optionally run tests using other libraries
|
||||
*/
|
||||
const extraTests = [
|
||||
() => renderDeepTree('styletron', styletron),
|
||||
() => renderWideTree('styletron', styletron),
|
||||
() => renderDeepTree('aphrodite', aphrodite),
|
||||
() => renderWideTree('aphrodite', aphrodite),
|
||||
() => renderDeepTree('glamor', glamor),
|
||||
() => renderWideTree('glamor', glamor),
|
||||
() => renderDeepTree('react-jss', jss),
|
||||
() => renderWideTree('react-jss', jss),
|
||||
() => renderDeepTree('reactxp', xp),
|
||||
() => renderWideTree('reactxp', xp),
|
||||
() => renderDeepTree('styled-components', styledComponents),
|
||||
() => renderWideTree('styled-components', styledComponents)
|
||||
];
|
||||
|
||||
const tests = testAll ? coreTests.concat(extraTests) : coreTests;
|
||||
|
||||
// run benchmarks
|
||||
tests.reduce((promise, test) => promise.then(test()), Promise.resolve());
|
||||
20
benchmarks/package.json
Normal file
20
benchmarks/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "performance",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"aphrodite": "^1.2.1",
|
||||
"classnames": "^2.2.5",
|
||||
"glamor": "2.20.25",
|
||||
"marky": "^1.2.0",
|
||||
"react-jss": "^6.1.1",
|
||||
"reactxp": "^0.34.3",
|
||||
"styled-components": "2.0.0-17",
|
||||
"styletron-client": "^2.5.7",
|
||||
"styletron-utils": "^2.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^0.28.1",
|
||||
"react-addons-perf": "^15.4.2",
|
||||
"style-loader": "^0.17.0"
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
||||
107
benchmarks/src/components/AppText/index.js
Normal file
107
benchmarks/src/components/AppText/index.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import theme from '../theme';
|
||||
import React, { PropTypes, 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;
|
||||
40
benchmarks/src/components/AspectRatio/index.js
Normal file
40
benchmarks/src/components/AspectRatio/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React, { PureComponent, PropTypes } 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;
|
||||
49
benchmarks/src/components/Box/aphrodite.js
Normal file
49
benchmarks/src/components/Box/aphrodite.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* 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
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Box;
|
||||
18
benchmarks/src/components/Box/css-modules.js
Normal file
18
benchmarks/src/components/Box/css-modules.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/* 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'
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
module.exports = Box;
|
||||
48
benchmarks/src/components/Box/glamor.js
Normal file
48
benchmarks/src/components/Box/glamor.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Box;
|
||||
50
benchmarks/src/components/Box/jss.js
Normal file
50
benchmarks/src/components/Box/jss.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* 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
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = injectSheet(styles)(Box);
|
||||
49
benchmarks/src/components/Box/react-native-stylesheet.js
vendored
Normal file
49
benchmarks/src/components/Box/react-native-stylesheet.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/* 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
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Box;
|
||||
48
benchmarks/src/components/Box/react-native.js
vendored
Normal file
48
benchmarks/src/components/Box/react-native.js
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Box;
|
||||
48
benchmarks/src/components/Box/reactxp.js
Normal file
48
benchmarks/src/components/Box/reactxp.js
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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
|
||||
})
|
||||
};
|
||||
|
||||
module.exports = 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)};
|
||||
`;
|
||||
|
||||
module.exports = 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;
|
||||
}
|
||||
49
benchmarks/src/components/Box/styletron.js
Normal file
49
benchmarks/src/components/Box/styletron.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* 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'
|
||||
})
|
||||
};
|
||||
|
||||
module.exports = Box;
|
||||
52
benchmarks/src/components/GridView/index.js
Normal file
52
benchmarks/src/components/GridView/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import React, { Component, PropTypes } 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;
|
||||
39
benchmarks/src/components/NestedTree/index.js
Normal file
39
benchmarks/src/components/NestedTree/index.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { Component, PropTypes } 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;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = 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 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, PropTypes } 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;
|
||||
77
benchmarks/src/components/TweetAction/index.js
Normal file
77
benchmarks/src/components/TweetAction/index.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import IconReply from '../Icons/Reply';
|
||||
import IconHeart from '../Icons/Heart';
|
||||
import IconRetweet from '../Icons/Retweet';
|
||||
import IconDirectMessage from '../Icons/DirectMessage';
|
||||
import { Text, View, StyleSheet } from 'react-native';
|
||||
import React, { PropTypes } from 'react';
|
||||
import theme from '../theme';
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
51
benchmarks/src/components/TweetActionsBar/index.js
Normal file
51
benchmarks/src/components/TweetActionsBar/index.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import TweetAction from '../TweetAction';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import React, { PropTypes, 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%'
|
||||
}
|
||||
});
|
||||
28
benchmarks/src/components/TweetText/index.js
Normal file
28
benchmarks/src/components/TweetText/index.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import AppText from '../AppText';
|
||||
import TweetTextPart from '../TweetTextPart';
|
||||
import React, { PropTypes } from 'react';
|
||||
|
||||
class TweetText extends React.Component {
|
||||
static displayName = 'TweetText';
|
||||
|
||||
static propTypes = {
|
||||
displayMode: TweetTextPart.propTypes.displayMode,
|
||||
lang: PropTypes.string,
|
||||
numberOfLines: PropTypes.number,
|
||||
textParts: PropTypes.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 React, { PropTypes } 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;
|
||||
64
benchmarks/src/components/UserAvatar/index.js
Normal file
64
benchmarks/src/components/UserAvatar/index.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import AspectRatio from '../AspectRatio';
|
||||
import { Image, StyleSheet } from 'react-native';
|
||||
import React, { PropTypes, 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;
|
||||
49
benchmarks/src/components/UserNames/index.js
Normal file
49
benchmarks/src/components/UserNames/index.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import AppText from '../AppText';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import React, { PropTypes, 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
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = 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)} />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = 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
|
||||
};
|
||||
|
||||
module.exports = 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
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = injectSheet(styles)(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
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = 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;
|
||||
`;
|
||||
|
||||
module.exports = 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;
|
||||
37
benchmarks/src/components/theme.js
Normal file
37
benchmarks/src/components/theme.js
Normal file
@@ -0,0 +1,37 @@
|
||||
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'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
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}`;
|
||||
}
|
||||
};
|
||||
7
benchmarks/src/css-modules.js
Normal file
7
benchmarks/src/css-modules.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/css-modules';
|
||||
import View from './components/View/css-modules';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
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
|
||||
};
|
||||
7
benchmarks/src/jss.js
Normal file
7
benchmarks/src/jss.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/jss';
|
||||
import View from './components/View/jss';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
7
benchmarks/src/react-native-stylesheet.js
vendored
Normal file
7
benchmarks/src/react-native-stylesheet.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import Box from './components/Box/react-native-stylesheet';
|
||||
import View from './components/View/react-native-stylesheet';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
View
|
||||
};
|
||||
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.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, components) =>
|
||||
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')
|
||||
}
|
||||
}
|
||||
};
|
||||
1120
benchmarks/yarn.lock
Normal file
1120
benchmarks/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
# `Component`
|
||||
|
||||
This component is part of the implementation for managing styles across the
|
||||
`className` and `style` properties. It is the building block upon which all
|
||||
other components in `react-web-sdk` are built.
|
||||
|
||||
## PropTypes
|
||||
|
||||
All other props are transferred directly to the `element`.
|
||||
|
||||
+ `element`: `func` or `string`
|
||||
+ `style`: `object`
|
||||
|
||||
#### Examples
|
||||
|
||||
```js
|
||||
import {Component, pickProps} from 'react-web-sdk';
|
||||
import React, {PropTypes} from 'react';
|
||||
|
||||
const ExampleStylePropTypes = { opacity: PropTypes.number };
|
||||
const ExampleStyleDefaultProps = { opacity: 1 };
|
||||
|
||||
class Example extends React.Component {
|
||||
static propTypes = {
|
||||
style: PropTypes.shape(ExampleStylePropTypes)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
// only apply supported styles
|
||||
const supportedStyle = pickProps(style, ExampleStylePropTypes);
|
||||
// merge with default styles
|
||||
const mergedStyle = { ...ExampleStyleDefaultProps, ...supportedStyle }
|
||||
|
||||
return (
|
||||
<Component
|
||||
...other
|
||||
element="main"
|
||||
style={mergedStyle}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,66 +0,0 @@
|
||||
# Image spec
|
||||
|
||||
#### PropTypes
|
||||
|
||||
All other props are transferred directly to the `element`.
|
||||
|
||||
+ `accessibilityLabel`: `string`
|
||||
+ `async`: `bool` (TODO)
|
||||
+ `className`: `string`
|
||||
+ `source`: `string`
|
||||
+ `style`: `ImageStylePropTypes`
|
||||
|
||||
#### ImageStylePropTypes
|
||||
|
||||
+ `BackgroundPropTypes`
|
||||
+ `BorderThemePropTypes`
|
||||
+ `LayoutPropTypes`
|
||||
+ `opacity`: `string`
|
||||
|
||||
#### Examples
|
||||
|
||||
```js
|
||||
import {Image} from 'react-web-sdk';
|
||||
import React, {PropTypes} from 'react';
|
||||
|
||||
class Avatar extends React.Component {
|
||||
static propTypes = {
|
||||
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
||||
user: PropTypes.object
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
size: 'normal'
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Image
|
||||
accessibilityLabel={`${user.name}'s profile picture`}
|
||||
source={user.avatarUrl}
|
||||
style={ ...style.base, ...style[this.props.size] }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const style = {
|
||||
base: {
|
||||
borderColor: 'white',
|
||||
borderRadius: '5px',
|
||||
borderWidth: '5px'
|
||||
},
|
||||
small: {
|
||||
height: '32px',
|
||||
width: '32px'
|
||||
},
|
||||
normal: {
|
||||
height: '48px',
|
||||
width: '48px'
|
||||
},
|
||||
large: {
|
||||
height: '64px',
|
||||
width: '32px'
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,89 +0,0 @@
|
||||
## StyleProp spec
|
||||
|
||||
### Background
|
||||
|
||||
+ `backgroundColor`: `string`
|
||||
+ `backgroundImage`: `string`
|
||||
+ `backgroundPosition`: `string`
|
||||
+ `backgroundRepeat`: `string`
|
||||
+ `backgroundSize`: `string`
|
||||
|
||||
### BorderTheme
|
||||
|
||||
+ `borderColor`: `string`
|
||||
+ `borderTopColor`: `string`
|
||||
+ `borderRightColor`: `string`
|
||||
+ `borderBottomColor`: `string`
|
||||
+ `borderLeftColor`: `string`
|
||||
+ `borderStyle`: `string`
|
||||
+ `borderRadius`: `number` or `string`
|
||||
+ `borderTopLeftRadius`: `number` or `string`
|
||||
+ `borderTopRightRadius`: `number` or `string`
|
||||
+ `borderBottomLeftRadius`: `number` or `string`
|
||||
+ `borderBottomRightRadius`: `number` or `string`
|
||||
|
||||
### BoxModel
|
||||
|
||||
+ `borderWidth`: `number` or `string`
|
||||
+ `borderTopWidth`: `number` or `string`
|
||||
+ `borderRightWidth`: `number` or `string`
|
||||
+ `borderBottomWidth`: `number` or `string`
|
||||
+ `borderLeftWidth`: `number` or `string`
|
||||
+ `boxSizing`: `oneOf('border-box', 'content-box')`
|
||||
+ `display`: `oneOf('block', 'flex', 'inline', 'inline-block', 'inline-flex')`
|
||||
+ `height`: `number` or `string`
|
||||
+ `margin`: `number` or `string`
|
||||
+ `marginTop`: `number` or `string`
|
||||
+ `marginRight`: `number` or `string`
|
||||
+ `marginBottom`: `number` or `string`
|
||||
+ `marginLeft`: `number` or `string`
|
||||
+ `padding`: `number` or `string`
|
||||
+ `paddingTop`: `number` or `string`
|
||||
+ `paddingRight`: `number` or `string`
|
||||
+ `paddingBottom`: `number` or `string`
|
||||
+ `paddingLeft`: `number` or `string`
|
||||
+ `width`: `number` or `string`
|
||||
|
||||
### Flexbox
|
||||
|
||||
* `alignContent`: `oneOf('center', 'flex-end', 'flex-start', 'stretch', 'space-around', 'space-between')`
|
||||
* `alignItems`: `oneOf('baseline', 'center', 'flex-end', 'flex-start', 'stretch')`
|
||||
* `alignSelf`: `oneOf('auto', 'baseline', 'center', 'flex-end', 'flex-start', 'stretch')`
|
||||
* `flex`: `string`
|
||||
* `flexBasis`: `string`
|
||||
* `flexDirection`: `oneOf('column', 'column-reverse', 'row', 'row-reverse')`
|
||||
* `flexGrow`: `number`
|
||||
* `flexShrink`: `number`
|
||||
* `flexWrap`: `oneOf('nowrap', 'wrap', 'wrap-reverse')`
|
||||
* `justifyContent`: `oneOf('center', 'flex-end', 'flex-start', 'space-around', 'space-between')`
|
||||
* `order`: `number`
|
||||
|
||||
### Layout
|
||||
|
||||
* BoxModel
|
||||
* Flexbox
|
||||
* Position
|
||||
|
||||
### Position
|
||||
|
||||
* `position`: `oneOf('absolute', 'fixed', 'relative')`
|
||||
* `bottom`: `number` or `string`
|
||||
* `left`: `number` or `string`
|
||||
* `right`: `number` or `string`
|
||||
* `top`: `number` or `string`
|
||||
* `zIndex`: `number`
|
||||
|
||||
### Typographic
|
||||
|
||||
* `direction`: `oneOf('auto', 'ltr', 'rtl')`
|
||||
* `fontFamily`: `string`
|
||||
* `fontSize`: `string`
|
||||
* `fontWeight`: `oneOf('100', '200', '300', '400', '500', '600', '700', '800', '900', 'bold', 'normal')`
|
||||
* `fontStyle`: `oneOf('normal', 'italic')`
|
||||
* `letterSpacing`: `string`
|
||||
* `lineHeight`: `number` or `string`
|
||||
* `textAlign`: `oneOf('auto', 'left', 'right', 'center')`
|
||||
* `textDecoration`: `oneOf('none', 'underline')`
|
||||
* `textTransform`: `oneOf('capitalize', 'lowercase', 'none', 'uppercase')`
|
||||
* `wordWrap`: `oneOf('break-word', 'normal')`
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
# Text spec
|
||||
|
||||
Text layout and styles.
|
||||
|
||||
#### PropTypes
|
||||
|
||||
All other props are transferred directly to the `element`.
|
||||
|
||||
+ `element`: `func` or `string` (default `"div"`)
|
||||
+ `style`: `TextStylePropTypes`
|
||||
|
||||
#### TextStylePropTypes
|
||||
|
||||
+ ViewStylePropTypes
|
||||
+ TypographicPropTypes
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import {Text} from 'react-web-sdk';
|
||||
import React, {PropTypes} from 'react';
|
||||
|
||||
class PrettyText extends React.Component {
|
||||
static propTypes = {
|
||||
color: PropTypes.oneOf(['white', 'gray', 'red']),
|
||||
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
||||
weight: PropTypes.oneOf(['light', 'normal', 'bold'])
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
color: 'gray',
|
||||
size: 'normal',
|
||||
weight: 'normal'
|
||||
}
|
||||
|
||||
render() {
|
||||
const { color, size, style, weight, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<Text
|
||||
...other
|
||||
style={{
|
||||
...style,
|
||||
...localStyle.color[color],
|
||||
...localStyle.size[size],
|
||||
...localStyle.weight[weight]
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const localStyle = {
|
||||
color: {
|
||||
white: { color: 'white' },
|
||||
gray: { color: 'gray' },
|
||||
red: { color: 'red' }
|
||||
},
|
||||
size: {
|
||||
small: { fontSize: '0.85rem', padding: '0.5rem' },
|
||||
normal: { fontSize: '1rem', padding: '0.75rem' },
|
||||
large: { fontSize: '1.5rem', padding: '1rem' }
|
||||
},
|
||||
weight: {
|
||||
light: { fontWeight: '300' },
|
||||
normal: { fontWeight: '400' },
|
||||
bold: { fontWeight: '700' }
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,66 +0,0 @@
|
||||
# View spec
|
||||
|
||||
`View` is a flexbox container and the fundamental building block for UI. It is
|
||||
designed to be nested inside other `View`'s and to have 0-to-many children of
|
||||
any type.
|
||||
|
||||
## PropTypes
|
||||
|
||||
All other props are transferred directly to the `element`.
|
||||
|
||||
+ `element`: `func` or `string` (default `"div"`)
|
||||
+ `pointerEvents`: `oneOf('all', 'box-only', 'box-none', 'none')`
|
||||
+ `style`: `ViewStylePropTypes`
|
||||
|
||||
## ViewStylePropTypes
|
||||
|
||||
+ BackgroundPropTypes
|
||||
+ BorderThemePropTypes
|
||||
+ LayoutPropTypes
|
||||
+ `boxShadow`: `string`
|
||||
+ `color`: `string`
|
||||
+ `opacity`: `number`
|
||||
|
||||
## ViewStyleDefaultProps
|
||||
|
||||
Implements the default styles from
|
||||
[facebook/css-layout](https://github.com/facebook/css-layout).
|
||||
|
||||
1. All the flex elements are oriented from top-to-bottom, left-to-right and do
|
||||
not shrink. This is how things are laid out using the default CSS settings
|
||||
and what you'd expect.
|
||||
|
||||
2. The most convenient way to express the relation between width and other
|
||||
box-model properties.
|
||||
|
||||
3. Everything is `display:flex` by default. All the behaviors of `block` and
|
||||
`inline-block` can be expressed in term of flex but not the opposite.
|
||||
|
||||
4. Everything is `position:relative`. This makes `position:absolute` target the
|
||||
direct parent and not some parent which is either relative or absolute. If
|
||||
you want to position an element relative to something else, you should move
|
||||
it in the DOM instead of relying of CSS. It also makes `top`, `left`,
|
||||
`right`, `bottom` do something when not specifying `position:absolute`.
|
||||
|
||||
```js
|
||||
const ViewStyleDefaultProps = {
|
||||
alignItems: 'stretch', // 1
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box', // 2
|
||||
display: 'flex', // 3
|
||||
flexBasis: 'auto', // 1
|
||||
flexDirection: 'column', // 1
|
||||
flexShrink: 0, // 1
|
||||
listStyle: 'none',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative' // 4
|
||||
};
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
// TODO
|
||||
```
|
||||
58
docs/apis/AppRegistry.md
Normal file
58
docs/apis/AppRegistry.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# AppRegistry
|
||||
|
||||
`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` (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
|
||||
into `runApplication`. These should always be used as a pair.
|
||||
|
||||
## Methods
|
||||
|
||||
(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
|
||||
|
||||
static **registerConfig**(config: Array<AppConfig>)
|
||||
|
||||
Registry multiple applications. `AppConfig` is of type `{ appKey: string;
|
||||
component: ComponentProvider; run?: Function }`.
|
||||
|
||||
static **registerComponent**(appKey: string, getComponentFunc: ComponentProvider)
|
||||
|
||||
Register a component provider under the given `appKey`.
|
||||
|
||||
static **registerRunnable**(appKey: string, run: Function)
|
||||
|
||||
Register a custom render function for an application. The function will receive
|
||||
the `appParameters` passed to `runApplication`.
|
||||
|
||||
static **getAppKeys**()
|
||||
|
||||
Returns all registered app keys.
|
||||
|
||||
static **runApplication**(appKey: string, appParameters?: object)
|
||||
|
||||
Runs the application that was registered under `appKey`. The `appParameters`
|
||||
must include the `rootTag` into which the application is rendered, and
|
||||
optionally any `initialProps`.
|
||||
|
||||
static **unmountApplicationComponentAtRootTag**(rootTag: HTMLElement)
|
||||
|
||||
To "stop" an application when a view should be destroyed, call
|
||||
`AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was passed
|
||||
into `runApplication`
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
AppRegistry.registerComponent('MyApp', () => AppComponent)
|
||||
AppRegistry.runApplication('MyApp', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-root')
|
||||
})
|
||||
```
|
||||
60
docs/apis/AppState.md
Normal file
60
docs/apis/AppState.md
Normal file
@@ -0,0 +1,60 @@
|
||||
## AppState
|
||||
|
||||
`AppState` can tell you if the app is in the foreground or background, and
|
||||
notify you when the state changes.
|
||||
|
||||
States
|
||||
|
||||
* `active` - The app is running in the foreground
|
||||
* `background` - The app is running in the background (i.e., the user has not focused the app's tab).
|
||||
|
||||
## Properties
|
||||
|
||||
static **currentState**
|
||||
|
||||
Returns the current state of the app: `active` or `background`.
|
||||
|
||||
## Methods
|
||||
|
||||
static **addEventListener**(type: string, handler: Function)
|
||||
|
||||
Add a handler to `AppState` changes by listening to the `change` event type and
|
||||
providing the `handler`. The handler is called with the app state value.
|
||||
|
||||
static **removeEventListener**(type: string, handler: Function)
|
||||
|
||||
Remove a handler by passing the change event `type` and the `handler`.
|
||||
|
||||
## Examples
|
||||
|
||||
To see the current state, you can check `AppState.currentState`, which will be
|
||||
kept up-to-date. This example will only ever appear to say "Current state is:
|
||||
active" because the app is only visible to the user when in the `active` state,
|
||||
and the null state will happen only momentarily.
|
||||
|
||||
```js
|
||||
class Example extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { currentAppState: AppState.currentState }
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
}
|
||||
|
||||
_handleAppStateChange = (currentAppState) => {
|
||||
this.setState({ currentAppState });
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Text>Current state is: {this.state.currentAppState}</Text>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
71
docs/apis/AsyncStorage.md
Normal file
71
docs/apis/AsyncStorage.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# AsyncStorage
|
||||
|
||||
`AsyncStorage` is a simple, asynchronous, persistent, key-value storage system
|
||||
that is global to the domain. It's a facade over, and should be used instead of
|
||||
`window.localStorage` to provide an asynchronous API and multi functions. Each
|
||||
method returns a `Promise` object.
|
||||
|
||||
It is recommended that you use an abstraction on top of `AsyncStorage` instead
|
||||
of `AsyncStorage` directly for anything more than light usage since it operates
|
||||
globally.
|
||||
|
||||
The batched functions are useful for executing a lot of operations at once,
|
||||
allowing for optimizations to provide the convenience of a single promise after
|
||||
all operations are complete.
|
||||
|
||||
## Methods
|
||||
|
||||
static **clear**()
|
||||
|
||||
Erases all AsyncStorage. You probably don't want to call this - use
|
||||
`removeItem` or `multiRemove` to clear only your own keys instead. Returns a
|
||||
Promise object.
|
||||
|
||||
static **getAllKeys**()
|
||||
|
||||
Gets all known keys. Returns a Promise object.
|
||||
|
||||
static **getItem**(key: string)
|
||||
|
||||
Fetches the value of the given key. Returns a Promise object.
|
||||
|
||||
static **mergeItem**(key: string, value: string)
|
||||
|
||||
Merges existing value with input value, assuming they are stringified JSON.
|
||||
Returns a Promise object.
|
||||
|
||||
static **multiGet**(keys: Array<string>)
|
||||
|
||||
`multiGet` results in an array of key-value pair arrays that matches the input
|
||||
format of `multiSet`. Returns a Promise object.
|
||||
|
||||
```js
|
||||
multiGet(['k1', 'k2']) -> [['k1', 'val1'], ['k2', 'val2']]
|
||||
```
|
||||
|
||||
static **multiMerge**(keyValuePairs: Array<Array<string>>)
|
||||
|
||||
multiMerge takes an array of key-value array pairs that match the output of
|
||||
`multiGet`. It merges existing values with input values, assuming they are
|
||||
stringified JSON. Returns a Promise object.
|
||||
|
||||
static **multiRemove**(keys: Array<string>)
|
||||
|
||||
Delete all the keys in the keys array. Returns a Promise object.
|
||||
|
||||
static **multiSet**(keyValuePairs: Array<Array<string>>)
|
||||
|
||||
`multiSet` takes an array of key-value array pairs that match the output of
|
||||
`multiGet`. Returns a Promise object.
|
||||
|
||||
```js
|
||||
multiSet([['k1', 'val1'], ['k2', 'val2']]);
|
||||
```
|
||||
|
||||
static **removeItem**(key: string)
|
||||
|
||||
Removes the value of the given key. Returns a Promise object.
|
||||
|
||||
static **setItem**(key: string, value: string)
|
||||
|
||||
Sets the value of the given key. Returns a Promise object.
|
||||
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.
|
||||
13
docs/apis/Dimensions.md
Normal file
13
docs/apis/Dimensions.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Dimensions
|
||||
|
||||
Note: dimensions may change (e.g due to device rotation) so any rendering logic
|
||||
or styles that depend on these constants should try to call this function on
|
||||
every render, rather than caching the value.
|
||||
|
||||
## Methods
|
||||
|
||||
static **get**(dimension: string)
|
||||
|
||||
Get a dimension (e.g., `"window"` or `"screen"`).
|
||||
|
||||
Example: `const { height, width } = Dimensions.get('window')`
|
||||
25
docs/apis/I18nManager.md
Normal file
25
docs/apis/I18nManager.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# I18nManager
|
||||
|
||||
Control and set the layout and writing direction of the application.
|
||||
|
||||
## Properties
|
||||
|
||||
**isRTL**: bool = false
|
||||
|
||||
Whether the application is currently in RTL mode.
|
||||
|
||||
## Methods
|
||||
|
||||
static **allowRTL**(allowRTL: bool)
|
||||
|
||||
Allow the application to display in RTL mode.
|
||||
|
||||
static **forceRTL**(forceRTL: bool)
|
||||
|
||||
Force the application to display in RTL mode.
|
||||
|
||||
static **setPreferredLanguageRTL**(isRTL: bool)
|
||||
|
||||
Set the application's preferred writing direction to RTL. You will need to
|
||||
determine the user's preferred locale server-side (from HTTP headers) and
|
||||
decide whether it's an RTL language.
|
||||
42
docs/apis/NativeMethods.md
Normal file
42
docs/apis/NativeMethods.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 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.
|
||||
77
docs/apis/NetInfo.md
Normal file
77
docs/apis/NetInfo.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# NetInfo
|
||||
|
||||
`NetInfo` asynchronously determines the online/offline status of the
|
||||
application.
|
||||
|
||||
Connection types:
|
||||
|
||||
* `bluetooth` - The user agent is using a Bluetooth connection.
|
||||
* `cellular` - The user agent is using a cellular connection (e.g., EDGE, HSPA, LTE, etc.).
|
||||
* `ethernet` - The user agent is using an Ethernet connection.
|
||||
* `mixed` - The user agent is using multiple connection types.
|
||||
* `none` - The user agent will not contact the network (offline).
|
||||
* `other` - The user agent is using a connection type that is not one of enumerated connection types.
|
||||
* `unknown` - The user agent has established a network connection, but is unable to determine what is the underlying connection technology.
|
||||
* `wifi` - The user agent is using a Wi-Fi connection.
|
||||
* `wimax` - The user agent is using a WiMAX connection.
|
||||
|
||||
## Methods
|
||||
|
||||
Note that support for retrieving the connection type depends upon browswer
|
||||
support (and is limited to mobile browsers). It will default to `unknown` when
|
||||
support is missing.
|
||||
|
||||
static **addEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
|
||||
static **fetch**(): Promise
|
||||
|
||||
static **removeEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
|
||||
## Properties
|
||||
|
||||
**isConnected**: bool = true
|
||||
|
||||
Available on all user agents. Asynchronously fetch a boolean to determine
|
||||
internet connectivity.
|
||||
|
||||
**isConnected.addEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
|
||||
**isConnected.fetch**(): Promise
|
||||
|
||||
**isConnected.removeEventListener**(eventName: ChangeEventName, handler: Function)
|
||||
|
||||
## Examples
|
||||
|
||||
Fetching the connection type:
|
||||
|
||||
```js
|
||||
NetInfo.fetch().then((connectionType) => {
|
||||
console.log('Connection type:', connectionType);
|
||||
});
|
||||
```
|
||||
|
||||
Subscribing to changes in the connection type:
|
||||
|
||||
```js
|
||||
const handleConnectivityTypeChange = (connectionType) => {
|
||||
console.log('Current connection type:', connectionType);
|
||||
}
|
||||
NetInfo.addEventListener('change', handleConnectivityTypeChange);
|
||||
```
|
||||
|
||||
Fetching the connection status:
|
||||
|
||||
```js
|
||||
NetInfo.isConnected.fetch().then((isConnected) => {
|
||||
console.log('Connection status:', (isConnected ? 'online' : 'offline'));
|
||||
});
|
||||
```
|
||||
|
||||
Subscribing to changes in the connection status:
|
||||
|
||||
```js
|
||||
const handleConnectivityStatusChange = (isConnected) => {
|
||||
console.log('Current connection status:', (isConnected ? 'online' : 'offline'));
|
||||
}
|
||||
NetInfo.isConnected.addEventListener('change', handleConnectivityStatusChange);
|
||||
```
|
||||
51
docs/apis/PixelRatio.md
Normal file
51
docs/apis/PixelRatio.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# PixelRatio
|
||||
|
||||
`PixelRatio` gives access to the device pixel density.
|
||||
|
||||
## Methods
|
||||
|
||||
static **get**()
|
||||
|
||||
Returns the device pixel density. Some examples:
|
||||
|
||||
* PixelRatio.get() === 1
|
||||
* mdpi Android devices (160 dpi)
|
||||
* PixelRatio.get() === 1.5
|
||||
* hdpi Android devices (240 dpi)
|
||||
* PixelRatio.get() === 2
|
||||
* iPhone 4, 4S
|
||||
* iPhone 5, 5c, 5s
|
||||
* iPhone 6
|
||||
* xhdpi Android devices (320 dpi)
|
||||
* PixelRatio.get() === 3
|
||||
* iPhone 6 plus
|
||||
* xxhdpi Android devices (480 dpi)
|
||||
* PixelRatio.get() === 3.5
|
||||
* Nexus 6
|
||||
|
||||
static **getPixelSizeForLayoutSize**(layoutSize: number)
|
||||
|
||||
Converts a layout size (dp) to pixel size (px). Guaranteed to return an integer
|
||||
number.
|
||||
|
||||
static **roundToNearestPixel**(layoutSize: number)
|
||||
|
||||
Rounds a layout size (dp) to the nearest layout size that corresponds to an
|
||||
integer number of pixels. For example, on a device with a PixelRatio of 3,
|
||||
`PixelRatio.roundToNearestPixel(8.4)` = `8.33`, which corresponds to exactly
|
||||
`(8.33 * 3)` = `25` pixels.
|
||||
|
||||
## Examples
|
||||
|
||||
Fetching a correctly sized image. You should get a higher resolution image if
|
||||
you are on a high pixel density device. A good rule of thumb is to multiply the
|
||||
size of the image you display by the pixel ratio.
|
||||
|
||||
```js
|
||||
const image = getImage({
|
||||
width: PixelRatio.getPixelSizeForLayoutSize(200),
|
||||
height: PixelRatio.getPixelSizeForLayoutSize(100),
|
||||
});
|
||||
|
||||
<Image source={image} style={{ width: 200, height: 100 }} />
|
||||
```
|
||||
45
docs/apis/Platform.md
Normal file
45
docs/apis/Platform.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Platform
|
||||
|
||||
Detect what is the platform in which the app is running. This piece of
|
||||
functionality can be useful when only small parts of a component are platform
|
||||
specific.
|
||||
|
||||
## Properties
|
||||
|
||||
**OS**: string
|
||||
|
||||
`Platform.OS` will be `web` when running in a Web browser.
|
||||
|
||||
```js
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
height: (Platform.OS === 'web') ? 200 : 100,
|
||||
});
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
**select**(object): any
|
||||
|
||||
`Platform.select` takes an object containing `Platform.OS` as keys and returns
|
||||
the value for the platform you are currently running on.
|
||||
|
||||
```js
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
const containerStyles = {
|
||||
flex: 1,
|
||||
...Platform.select({
|
||||
android: {
|
||||
backgroundColor: 'blue'
|
||||
},
|
||||
ios: {
|
||||
backgroundColor: 'red'
|
||||
},
|
||||
web: {
|
||||
backgroundColor: 'green'
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
79
docs/apis/StyleSheet.md
Normal file
79
docs/apis/StyleSheet.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# StyleSheet
|
||||
|
||||
The `StyleSheet` abstraction converts predefined styles to (vendor-prefixed)
|
||||
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
|
||||
|
||||
**create**(obj: {[key: string]: any})
|
||||
|
||||
Each key of the object passed to `create` must define a style object.
|
||||
|
||||
**flatten**: function
|
||||
|
||||
Flattens an array of styles into a single style object.
|
||||
|
||||
(web) **renderToString**: function
|
||||
|
||||
Returns a string of the stylesheet for use in server-side rendering.
|
||||
|
||||
## Properties
|
||||
|
||||
**absoluteFill**: number
|
||||
|
||||
A very common pattern is to create overlays with position absolute and zero positioning,
|
||||
so `absoluteFill` can be used for convenience and to reduce duplication of these repeated
|
||||
styles.
|
||||
|
||||
```js
|
||||
<View style={StyleSheet.absoluteFill} />
|
||||
```
|
||||
|
||||
**absoluteFillObject**: object
|
||||
|
||||
Sometimes you may want `absoluteFill` but with a couple tweaks - `absoluteFillObject` can be
|
||||
used to create a customized entry in a `StyleSheet`, e.g.:
|
||||
|
||||
```js
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'transparent',
|
||||
top: 10
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**hairlineWidth**: number
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
<View style={styles.container}>
|
||||
<Text
|
||||
children={'Title text'}
|
||||
style={[
|
||||
styles.title,
|
||||
this.props.isActive && styles.activeTitle
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
borderRadius: 4,
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#d6d7da',
|
||||
},
|
||||
title: {
|
||||
fontSize: 19,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
activeTitle: {
|
||||
color: 'red',
|
||||
}
|
||||
})
|
||||
```
|
||||
35
docs/apis/Vibration.md
Normal file
35
docs/apis/Vibration.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Vibration
|
||||
|
||||
Vibration is described as a pattern of on-off pulses, which may be of varying
|
||||
lengths. The pattern may consist of either a single integer, describing the
|
||||
number of milliseconds to vibrate, or an array of integers describing a pattern
|
||||
of vibrations and pauses. Vibration is controlled with a single method:
|
||||
`Vibration.vibrate()`.
|
||||
|
||||
The vibration is asynchronous so this method will return immediately. There
|
||||
will be no effect on devices that do not support vibration.
|
||||
|
||||
## Methods
|
||||
|
||||
static **cancel**()
|
||||
|
||||
Stop the vibration.
|
||||
|
||||
static **vibrate**(pattern)
|
||||
|
||||
Start the vibration pattern.
|
||||
|
||||
## Examples
|
||||
|
||||
Vibrate once for 200ms:
|
||||
|
||||
```js
|
||||
Vibration.vibrate(200);
|
||||
Vibration.vibrate([200]);
|
||||
```
|
||||
|
||||
Vibrate for 200ms, pause for 100ms, vibrate for 200ms:
|
||||
|
||||
```js
|
||||
Vibration.vibrate([200, 100, 200]);
|
||||
```
|
||||
70
docs/components/ActivityIndicator.md
Normal file
70
docs/components/ActivityIndicator.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# ActivityIndicator
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**animating**: boolean = true
|
||||
|
||||
Whether to show the indicator or hide it.
|
||||
|
||||
**color**: ?color = '#1976D2'
|
||||
|
||||
The foreground color of the spinner.
|
||||
|
||||
**hidesWhenStopped**: ?boolean = true
|
||||
|
||||
Whether the indicator should hide when not animating.
|
||||
|
||||
**size**: ?enum('small, 'large') | number = 'small'
|
||||
|
||||
Size of the indicator. Small has a height of `20`, large has a height of `36`.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import { ActivityIndicator, StyleSheet, View } from 'react-native'
|
||||
|
||||
class ToggleAnimatingActivityIndicator extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { animating: true }
|
||||
}
|
||||
|
||||
componentDidMount: function() {
|
||||
this.setToggleTimeout();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
animating={this.state.animating}
|
||||
size="large"
|
||||
style={[styles.centering, { height: 80 }]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_setToggleTimeout() {
|
||||
setTimeout(() => {
|
||||
this.setState({ animating: !this.state.animating })
|
||||
this._setToggleTimeout()
|
||||
}, 1200)
|
||||
}
|
||||
})
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centering: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
gray: {
|
||||
backgroundColor: '#cccccc'
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
})
|
||||
```
|
||||
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"
|
||||
/>
|
||||
```
|
||||
167
docs/components/Image.md
Normal file
167
docs/components/Image.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# Image
|
||||
|
||||
An accessibile image component with support for image resizing, default image,
|
||||
and child content.
|
||||
|
||||
Unsupported React Native props:
|
||||
`capInsets` (ios),
|
||||
`onProgress` (ios)
|
||||
|
||||
## Props
|
||||
|
||||
**accessibilityLabel**: ?string
|
||||
|
||||
The text that's read by a screenreader when someone interacts with the image.
|
||||
|
||||
**accessible**: ?boolean
|
||||
|
||||
When `true`, indicates the image is an accessibility element.
|
||||
|
||||
**children**: ?any
|
||||
|
||||
Content to display over the image.
|
||||
|
||||
**defaultSource**: ?object
|
||||
|
||||
An image to display as a placeholder while downloading the final image off the
|
||||
network. `{ uri: string, width, height }`
|
||||
|
||||
**onError**: ?function
|
||||
|
||||
Invoked on load error with `{nativeEvent: {error}}`.
|
||||
|
||||
**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
|
||||
|
||||
Invoked when load completes successfully.
|
||||
|
||||
**onLoadEnd**: ?function
|
||||
|
||||
Invoked when load either succeeds or fails,
|
||||
|
||||
**onLoadStart**: ?function
|
||||
|
||||
Invoked on load start.
|
||||
|
||||
**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**: ?object
|
||||
|
||||
`uri` is a string representing the resource identifier for the image, which
|
||||
could be an http address or a base64 encoded image. `{ uri: string, width, height }`
|
||||
|
||||
**style**: ?style
|
||||
|
||||
+ ...[View#style](./View.md)
|
||||
+ `resizeMode`
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate a view in end-to-end tests.
|
||||
|
||||
## Properties
|
||||
|
||||
static **resizeMode**: object
|
||||
|
||||
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
|
||||
import placeholderAvatar from './placeholderAvatar.png'
|
||||
import React, { Component } from 'react'
|
||||
import { Image, PropTypes, StyleSheet } from 'react-native'
|
||||
|
||||
export default class ImageExample extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = { loading: true }
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
||||
testID: Image.propTypes.testID,
|
||||
user: PropTypes.object
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
size: 'normal'
|
||||
}
|
||||
|
||||
_onLoad(e) {
|
||||
console.log('Avatar.onLoad', e)
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { size, testID, user } = this.props
|
||||
const loadingStyle = this.state.loading ? { styles.loading } : { }
|
||||
|
||||
return (
|
||||
<Image
|
||||
accessibilityLabel={`${user.name}'s profile picture`}
|
||||
defaultSource={{ uri: placeholderAvatar }}
|
||||
onLoad={this._onLoad.bind(this)}
|
||||
resizeMode='cover'
|
||||
source={{ uri: user.avatarUrl }}
|
||||
style={[
|
||||
styles.base,
|
||||
styles[size],
|
||||
loadingStyle
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
borderColor: 'white',
|
||||
borderRadius: 5,
|
||||
borderWidth: 5
|
||||
},
|
||||
loading: {
|
||||
opacity: 0.5
|
||||
},
|
||||
small: {
|
||||
height: 32,
|
||||
width: 32
|
||||
},
|
||||
normal: {
|
||||
height: 48,
|
||||
width: 48
|
||||
},
|
||||
large: {
|
||||
height: 64,
|
||||
width: 64
|
||||
}
|
||||
})
|
||||
```
|
||||
34
docs/components/ListView.md
Normal file
34
docs/components/ListView.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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 />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
27
docs/components/ProgressBar.md
Normal file
27
docs/components/ProgressBar.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# ProgressBar
|
||||
|
||||
Display an activity progress bar.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**color**: ?string = '#1976D2'
|
||||
|
||||
Color of the progress bar.
|
||||
|
||||
**indeterminate**: ?boolean = true
|
||||
|
||||
Whether the progress bar will show indeterminate progress.
|
||||
|
||||
**progress**: ?number
|
||||
|
||||
The progress value (between 0 and 1).
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
(web) **trackColor**: ?string = 'transparent'
|
||||
|
||||
Color of the track bar.
|
||||
134
docs/components/ScrollView.md
Normal file
134
docs/components/ScrollView.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# ScrollView
|
||||
|
||||
A scrollable `View` that provides itegration with the touch-locking "responder"
|
||||
system. `ScrollView`'s must have a bounded height: either set the height of the
|
||||
view directly (discouraged) or make sure all parent views have bounded height
|
||||
(e.g., transfer `{ flex: 1}` down the view stack).
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**contentContainerStyle**: ?style
|
||||
|
||||
These styles will be applied to the scroll view content container which wraps
|
||||
all of the child views.
|
||||
|
||||
**horizontal**: ?boolean = false
|
||||
|
||||
When `true`, the scroll view's children are arranged horizontally in a row
|
||||
instead of vertically in a column.
|
||||
|
||||
**keyboardDismissMode**: ?enum('none', 'on-drag') = 'none'
|
||||
|
||||
Determines whether the keyboard gets dismissed in response to a scroll drag.
|
||||
|
||||
* `none` (the default), drags do not dismiss the keyboard.
|
||||
* `on-drag`, the keyboard is dismissed when a drag begins.
|
||||
* `interactive` (not supported on web; same as `none`)
|
||||
|
||||
**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
|
||||
|
||||
Fires at most once per frame during scrolling. The frequency of the events can
|
||||
be contolled using the `scrollEventThrottle` prop.
|
||||
|
||||
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**: ?boolean = true
|
||||
|
||||
When false, the content does not scroll.
|
||||
|
||||
**scrollEventThrottle**: ?number = 0
|
||||
|
||||
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.
|
||||
|
||||
## Instance methods
|
||||
|
||||
**getInnerViewNode()**: any
|
||||
|
||||
Returns a reference to the underlying content container DOM node within the `ScrollView`.
|
||||
|
||||
**getScrollableNode()**: any
|
||||
|
||||
Returns a reference to the underlying scrollable DOM node.
|
||||
|
||||
**getScrollResponder()**: Component
|
||||
|
||||
Returns a reference to the underlying scroll responder, which supports
|
||||
operations like `scrollTo`. All `ScrollView`-like components should implement
|
||||
this method so that they can be composed while providing access to the
|
||||
underlying scroll responder's methods.
|
||||
|
||||
**scrollTo(options: { x: number = 0; y: number = 0; animated: boolean = true })**
|
||||
|
||||
Scrolls to a given `x`, `y` offset (animation is not currently supported).
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import { ScrollView, StyleSheet } from 'react-native'
|
||||
import Item from './Item'
|
||||
|
||||
export default class ScrollViewExample extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
items: Array.from(new Array(20)).map((_, i) => ({ id: i }))
|
||||
}
|
||||
}
|
||||
|
||||
onScroll(e) {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ScrollView
|
||||
children={this.state.items.map((item) => <Item {...item} />)}
|
||||
contentContainerStyle={styles.container}
|
||||
horizontal
|
||||
onScroll={(e) => this.onScroll(e)}
|
||||
scrollEventThrottle={100}
|
||||
style={styles.root}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderWidth: 1
|
||||
},
|
||||
container: {
|
||||
padding: 10
|
||||
}
|
||||
})
|
||||
```
|
||||
76
docs/components/Switch.md
Normal file
76
docs/components/Switch.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Switch
|
||||
|
||||
This is a controlled component that requires an `onValueChange` callback that
|
||||
updates the value prop in order for the component to reflect user actions. If
|
||||
the `value` prop is not updated, the component will continue to render the
|
||||
supplied `value` prop instead of the expected result of any user actions.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**disabled**: ?boolean = false
|
||||
|
||||
If `true` the user won't be able to interact with the switch.
|
||||
|
||||
**onValueChange**: ?function
|
||||
|
||||
Invoked with the new value when the value changes.
|
||||
|
||||
**value**: ?boolean = false
|
||||
|
||||
The value of the switch. If `true` the switch will be turned on.
|
||||
|
||||
(web) **activeThumbColor**: ?color = #009688
|
||||
|
||||
The color of the thumb grip when the switch is turned on.
|
||||
|
||||
(web) **activeTrackColor**: ?color = #A3D3CF
|
||||
|
||||
The color of the track when the switch is turned on.
|
||||
|
||||
(web) **thumbColor**: ?color = #FAFAFA
|
||||
|
||||
The color of the thumb grip when the switch is turned off.
|
||||
|
||||
(web) **trackColor**: ?color = #939393
|
||||
|
||||
The color of the track when the switch is turned off.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import { Switch, View } from 'react-native'
|
||||
|
||||
class ColorSwitchExample extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
colorTrueSwitchIsOn: true,
|
||||
colorFalseSwitchIsOn: false
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
activeThumbColor='#428BCA'
|
||||
activeTrackColor='#A0C4E3'
|
||||
onValueChange={(value) => this.setState({ colorFalseSwitchIsOn: value })}
|
||||
value={this.state.colorFalseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
activeThumbColor='#5CB85C'
|
||||
activeTrackColor='#ADDAAD'
|
||||
onValueChange={(value) => this.setState({ colorTrueSwitchIsOn: value })}
|
||||
thumbColor='#EBA9A7'
|
||||
trackColor='#D9534F'
|
||||
value={this.state.colorTrueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
161
docs/components/Text.md
Normal file
161
docs/components/Text.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Text
|
||||
|
||||
`Text` is component for displaying text. It supports style, basic touch
|
||||
handling, and inherits typographic styles from ancestor elements.
|
||||
|
||||
The `Text` is unique relative to layout: child elements use text layout
|
||||
(`inline`) rather than flexbox layout. This means that elements inside of a
|
||||
`Text` are not rectangles, as they wrap when reaching the edge of their
|
||||
container.
|
||||
|
||||
Unsupported React Native props:
|
||||
`allowFontScaling` (ios),
|
||||
`suppressHighlighting` (ios)
|
||||
|
||||
## Props
|
||||
|
||||
NOTE: `Text` will transfer all other props to the rendered HTML element.
|
||||
|
||||
(web) **accessibilityLabel**: ?string
|
||||
|
||||
Overrides the text that's read by a screen reader when the user interacts
|
||||
with the element. (This is implemented using `aria-label`.)
|
||||
|
||||
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)).
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**accessible**: ?boolean
|
||||
|
||||
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`.)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**children**: ?any
|
||||
|
||||
Child content.
|
||||
|
||||
**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
|
||||
|
||||
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
|
||||
|
||||
This function is called on press.
|
||||
|
||||
**selectable**: ?boolean
|
||||
|
||||
When `false`, the text is not selectable.
|
||||
|
||||
**style**: ?style
|
||||
|
||||
+ ...[View#style](View.md)
|
||||
+ `color`
|
||||
+ `fontFamily`
|
||||
+ `fontFeatureSettings` ‡
|
||||
+ `fontSize`
|
||||
+ `fontStyle`
|
||||
+ `fontWeight`
|
||||
+ `letterSpacing`
|
||||
+ `lineHeight`
|
||||
+ `textAlign`
|
||||
+ `textAlignVertical`
|
||||
+ `textDecorationLine`
|
||||
+ `textIndent` ‡
|
||||
+ `textOverflow` ‡
|
||||
+ `textRendering` ‡
|
||||
+ `textShadowColor`
|
||||
+ `textShadowOffset`
|
||||
+ `textShadowRadius`
|
||||
+ `textTransform` ‡
|
||||
+ `unicodeBidi` ‡
|
||||
+ `whiteSpace`
|
||||
+ `wordWrap` ‡
|
||||
+ `writingDirection`
|
||||
|
||||
‡ web only.
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
|
||||
export default class PrettyText extends Component {
|
||||
static propTypes = {
|
||||
...Text.propTypes,
|
||||
color: PropTypes.oneOf(['white', 'gray', 'red']),
|
||||
size: PropTypes.oneOf(['small', 'normal', 'large']),
|
||||
weight: PropTypes.oneOf(['light', 'normal', 'bold'])
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
...Text.defaultProps,
|
||||
color: 'gray',
|
||||
size: 'normal',
|
||||
weight: 'normal'
|
||||
};
|
||||
|
||||
render() {
|
||||
const { color, size, style, weight, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<Text
|
||||
...other
|
||||
style={[
|
||||
style,
|
||||
colorStyles[color],
|
||||
sizeStyles[size],
|
||||
weightStyles[weight]
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const colorStyles = StyleSheet.create({
|
||||
white: { color: 'white' },
|
||||
gray: { color: 'gray' },
|
||||
red: { color: 'red' }
|
||||
})
|
||||
|
||||
const sizeStyles = StyleSheet.create({
|
||||
small: { fontSize: '0.85rem', padding: '0.5rem' },
|
||||
normal: { fontSize: '1rem', padding: '0.75rem' },
|
||||
large: { fontSize: '1.5rem', padding: '1rem' }
|
||||
})
|
||||
|
||||
const weightStyles = StyleSheet.create({
|
||||
light: { fontWeight: '300' },
|
||||
normal: { fontWeight: '400' },
|
||||
bold: { fontWeight: '700' }
|
||||
})
|
||||
```
|
||||
223
docs/components/TextInput.md
Normal file
223
docs/components/TextInput.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# TextInput
|
||||
|
||||
Accessible single- and multi-line text input via a keyboard. Supports features
|
||||
such as auto-complete, auto-focus, placeholder text, and event callbacks.
|
||||
|
||||
Note: some props are exclusive to or excluded from `multiline`.
|
||||
|
||||
Unsupported React Native props:
|
||||
`onEndEditing`,
|
||||
`clearButtonMode` (ios),
|
||||
`enablesReturnKeyAutomatically` (ios),
|
||||
`placeholderTextColor`,
|
||||
`returnKeyType` (ios),
|
||||
`selectionState` (ios),
|
||||
`underlineColorAndroid` (android)
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**autoCapitalize**: ?enum('characters', 'none', 'sentences', 'words') = 'sentences'
|
||||
|
||||
Automatically capitalize certain characters (only available in Chrome and iOS Safari).
|
||||
|
||||
* `characters`: Automatically capitalize all characters.
|
||||
* `none`: Completely disables automatic capitalization
|
||||
* `sentences`: Automatically capitalize the first letter of sentences.
|
||||
* `words`: Automatically capitalize the first letter of words.
|
||||
|
||||
(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**: ?boolean = true
|
||||
|
||||
Automatically correct spelling mistakes (only available in iOS Safari).
|
||||
|
||||
**autoFocus**: ?boolean = false
|
||||
|
||||
If `true`, focuses the input on `componentDidMount`. Only the first form element
|
||||
in a document with `autofocus` is focused.
|
||||
|
||||
**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
|
||||
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**: ?boolean = false
|
||||
|
||||
If `true`, clears the text field automatically when focused.
|
||||
|
||||
**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**: ?boolean = true
|
||||
|
||||
If `false`, text is not editable (i.e., read-only).
|
||||
|
||||
**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
|
||||
|
||||
Limits the maximum number of characters that can be entered.
|
||||
|
||||
**multiline**: ?boolean = false
|
||||
|
||||
If true, the text input can be multiple lines.
|
||||
|
||||
**numberOfLines**: ?number = 2
|
||||
|
||||
Sets the number of lines for a multiline `TextInput`.
|
||||
|
||||
(Requires `multiline` to be `true`.)
|
||||
|
||||
**onBlur**: ?function
|
||||
|
||||
Callback that is called when the text input is blurred.
|
||||
|
||||
**onChange**: ?function
|
||||
|
||||
Callback that is called when the text input's text changes.
|
||||
|
||||
**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
|
||||
|
||||
Callback that is called when the text input is focused.
|
||||
|
||||
**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.
|
||||
|
||||
**onSelectionChange**: ?function
|
||||
|
||||
Callback that is called when the text input's selection changes. This will be called with
|
||||
`{ nativeEvent: { selection: { start, end } } }`.
|
||||
|
||||
**onSubmitEditing**: ?function
|
||||
|
||||
Callback that is called when the keyboard's submit button is pressed.
|
||||
|
||||
**placeholder**: ?string
|
||||
|
||||
The string that will be rendered in an empty `TextInput` before text has been
|
||||
entered.
|
||||
|
||||
**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 }
|
||||
|
||||
The start and end of the text input's selection. Set start and end to the same value to position the cursor.
|
||||
|
||||
**selectTextOnFocus**: ?boolean = false
|
||||
|
||||
If `true`, all text will automatically be selected on focus.
|
||||
|
||||
**style**: ?style
|
||||
|
||||
+ ...[Text#style](./Text.md)
|
||||
+ `resize` ‡
|
||||
|
||||
‡ web only.
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
**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.
|
||||
Read about how [React form
|
||||
components](https://facebook.github.io/react/docs/forms.html) work. To prevent
|
||||
user edits to the value set `editable={false}`.
|
||||
|
||||
## Instance methods
|
||||
|
||||
**blur()**
|
||||
|
||||
Blur the underlying DOM input.
|
||||
|
||||
**clear()**
|
||||
|
||||
Clear the text from the underlying DOM input.
|
||||
|
||||
**focus()**
|
||||
|
||||
Focus the underlying DOM input.
|
||||
|
||||
**isFocused()**
|
||||
|
||||
Returns `true` if the input is currently focused; `false` otherwise.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component } from 'react'
|
||||
import { StyleSheet, TextInput } from 'react-native'
|
||||
|
||||
export default class TextInputExample extends Component {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = { isFocused: false }
|
||||
}
|
||||
|
||||
_onBlur(e) {
|
||||
this.setState({ isFocused: false })
|
||||
}
|
||||
|
||||
_onFocus(e) {
|
||||
this.setState({ isFocused: true })
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TextInput
|
||||
accessibilityLabel='Write a status update'
|
||||
maxNumberOfLines={5}
|
||||
multiline
|
||||
numberOfLines={2}
|
||||
onBlur={this._onBlur.bind(this)}
|
||||
onFocus={this._onFocus.bind(this)}
|
||||
placeholder={`What's happening?`}
|
||||
style={[
|
||||
styles.default
|
||||
this.state.isFocused && styles.focused
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
default: {
|
||||
borderColor: 'gray',
|
||||
borderBottomWidth: 2
|
||||
},
|
||||
focused: {
|
||||
borderColor: 'blue'
|
||||
}
|
||||
})
|
||||
```
|
||||
46
docs/components/TouchableWithoutFeedback.md
Normal file
46
docs/components/TouchableWithoutFeedback.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# TouchableWithoutFeedback
|
||||
|
||||
Do not use unless you have a very good reason. All the elements that respond to
|
||||
press should have a visual feedback when touched. This is one of the primary
|
||||
reason a "web" app doesn't feel "native".
|
||||
|
||||
**NOTE: `TouchableWithoutFeedback` supports only one child**. If you wish to have
|
||||
several child components, wrap them in a View.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**delayLongPress**: ?number
|
||||
|
||||
Delay in ms, from `onPressIn`, before `onLongPress` is called.
|
||||
|
||||
**delayPressIn**: ?number
|
||||
|
||||
Delay in ms, from the start of the touch, before `onPressIn` is called.
|
||||
|
||||
**delayPressOut**: ?number
|
||||
|
||||
Delay in ms, from the release of the touch, before `onPressOut` is called.
|
||||
|
||||
**disabled**: ?boolean
|
||||
|
||||
If `true`, disable all interactions for this component.
|
||||
|
||||
**onLongPress**: ?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
|
||||
|
||||
**onPressOut**: ?function
|
||||
|
||||
**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
|
||||
back and you'll see that the button is once again reactivated! Move it back and
|
||||
forth several times while the scroll view is disabled. Ensure you pass in a
|
||||
constant to reduce memory allocations.
|
||||
316
docs/components/View.md
Normal file
316
docs/components/View.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# View
|
||||
|
||||
`View` is the fundamental UI building block. It is a component that supports
|
||||
style, layout with flexbox, and accessibility controls. It can be nested
|
||||
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: `collapsable`, `onAccessibilityTap`, `onMagicTap`.
|
||||
|
||||
## Props
|
||||
|
||||
NOTE: `View` will transfer all other props to the rendered HTML element.
|
||||
|
||||
**accessibilityLabel**: ?string
|
||||
|
||||
Overrides the text that's read by a screen reader when the user interacts
|
||||
with the element. (This is implemented using `aria-label`.)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) 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.
|
||||
When regions are specified as `polite` (recommended), updates take low
|
||||
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).)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) 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)).
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) for more information.
|
||||
|
||||
**accessible**: ?boolean
|
||||
|
||||
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`.)
|
||||
|
||||
See the [Accessibility guide](../guides/accessibility) 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) 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 => boolean
|
||||
|
||||
Does this view want to "claim" touch responsiveness? This is called for every
|
||||
touch move on the `View` when it is not the responder.
|
||||
|
||||
**onMoveShouldSetResponderCapture**: ?function => boolean
|
||||
|
||||
If a parent `View` wants to prevent a child `View` from becoming responder on a
|
||||
move, it should have this handler return `true`.
|
||||
|
||||
**onResponderGrant**: ?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`.
|
||||
|
||||
**onResponderMove**: ?function
|
||||
|
||||
The user is moving their finger.
|
||||
|
||||
**onResponderReject**: ?function
|
||||
|
||||
Another responder is already active and will not release it to the `View`
|
||||
asking to be the responder.
|
||||
|
||||
**onResponderRelease**: ?function
|
||||
|
||||
Fired at the end of the touch.
|
||||
|
||||
**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-only` is the equivalent of:
|
||||
|
||||
```css
|
||||
.box-only { pointer-events: auto }
|
||||
.box-only * { pointer-events: none }
|
||||
```
|
||||
|
||||
**style**: ?style
|
||||
|
||||
+ `alignContent`
|
||||
+ `alignItems`
|
||||
+ `alignSelf`
|
||||
+ `animationDelay` ‡
|
||||
+ `animationDirection` ‡
|
||||
+ `animationDuration` ‡
|
||||
+ `animationFillMode` ‡
|
||||
+ `animationIterationCount` ‡
|
||||
+ `animationName` ‡
|
||||
+ `animationPlayState` ‡
|
||||
+ `animationTimingFunction` ‡
|
||||
+ `backfaceVisibility`
|
||||
+ `backgroundAttachment` ‡
|
||||
+ `backgroundClip` ‡
|
||||
+ `backgroundColor`
|
||||
+ `backgroundImage` ‡
|
||||
+ `backgroundOrigin` ‡
|
||||
+ `backgroundPosition` ‡
|
||||
+ `backgroundRepeat` ‡
|
||||
+ `backgroundSize` ‡
|
||||
+ `borderColor` (single value)
|
||||
+ `borderTopColor`
|
||||
+ `borderBottomColor`
|
||||
+ `borderRightColor`
|
||||
+ `borderLeftColor`
|
||||
+ `borderRadius` (single value)
|
||||
+ `borderTopLeftRadius`
|
||||
+ `borderTopRightRadius`
|
||||
+ `borderBottomLeftRadius`
|
||||
+ `borderBottomRightRadius`
|
||||
+ `borderStyle` (single value)
|
||||
+ `borderTopStyle`
|
||||
+ `borderRightStyle`
|
||||
+ `borderBottomStyle`
|
||||
+ `borderLeftStyle`
|
||||
+ `borderWidth` (single value)
|
||||
+ `borderBottomWidth`
|
||||
+ `borderLeftWidth`
|
||||
+ `borderRightWidth`
|
||||
+ `borderTopWidth`
|
||||
+ `bottom`
|
||||
+ `boxShadow` ‡
|
||||
+ `boxSizing`
|
||||
+ `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`
|
||||
+ `margin` (single value)
|
||||
+ `marginBottom`
|
||||
+ `marginHorizontal`
|
||||
+ `marginLeft`
|
||||
+ `marginRight`
|
||||
+ `marginTop`
|
||||
+ `marginVertical`
|
||||
+ `maxHeight`
|
||||
+ `maxWidth`
|
||||
+ `minHeight`
|
||||
+ `minWidth`
|
||||
+ `opacity`
|
||||
+ `order`
|
||||
+ `outline` ‡
|
||||
+ `outlineColor` ‡
|
||||
+ `overflow`
|
||||
+ `overflowX` ‡
|
||||
+ `overflowY` ‡
|
||||
+ `padding` (single value)
|
||||
+ `paddingBottom`
|
||||
+ `paddingHorizontal`
|
||||
+ `paddingLeft`
|
||||
+ `paddingRight`
|
||||
+ `paddingTop`
|
||||
+ `paddingVertical`
|
||||
+ `perspective` ‡
|
||||
+ `perspectiveOrigin` ‡
|
||||
+ `position`
|
||||
+ `right`
|
||||
+ `shadowColor`
|
||||
+ `shadowOffset`
|
||||
+ `shadowOpacity`
|
||||
+ `shadowRadius`
|
||||
+ `top`
|
||||
+ `transform`
|
||||
+ `transformOrigin` ‡
|
||||
+ `transitionDelay` ‡
|
||||
+ `transitionDuration` ‡
|
||||
+ `transitionProperty` ‡
|
||||
+ `transitionTimingFunction` ‡
|
||||
+ `userSelect` ‡
|
||||
+ `visibility` ‡
|
||||
+ `width`
|
||||
+ `willChange` ‡
|
||||
+ `zIndex`
|
||||
|
||||
‡ web only.
|
||||
|
||||
Default:
|
||||
|
||||
```js
|
||||
{
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative'
|
||||
};
|
||||
```
|
||||
|
||||
(See [facebook/css-layout](https://github.com/facebook/css-layout)).
|
||||
|
||||
**testID**: ?string
|
||||
|
||||
Used to locate this view in end-to-end tests.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
|
||||
export default class ViewExample extends Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
{
|
||||
['1', '2', '3', '4', '5'].map((value, i) => {
|
||||
return <View children={value} key={i} style={styles.cell} />
|
||||
})
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
cell: {
|
||||
flexGrow: 1
|
||||
}
|
||||
})
|
||||
```
|
||||
120
docs/guides/accessibility.md
Normal file
120
docs/guides/accessibility.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Accessibility
|
||||
|
||||
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`.
|
||||
|
||||
## Accessibility properties
|
||||
|
||||
### 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 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] to use in addition to the resulting ARIA `role`, where
|
||||
possible. 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).
|
||||
|
||||
For example:
|
||||
|
||||
* `<View accessibilityRole='article' />` => `<article role='article' />`.
|
||||
* `<View accessibilityRole='banner' />` => `<header role='banner' />`.
|
||||
* `<View accessibilityRole='button' />` => `<button type='button' role='button' />`.
|
||||
* `<Text accessibilityRole='link' href='/' />` => `<a role='link' href='/' />`.
|
||||
* `<View accessibilityRole='main' />` => `<main role='main' />`.
|
||||
|
||||
In the example below, the `TouchableWithoutFeedback` is announced by screen
|
||||
readers as a native Button.
|
||||
|
||||
```
|
||||
<TouchableWithoutFeedback accessibilityRole="button" onPress={this._onPress}>
|
||||
<View style={styles.button}>
|
||||
<Text style={styles.buttonText}>Press me!</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
```
|
||||
|
||||
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/
|
||||
[html-aria-url]: http://www.w3.org/TR/html-aria/
|
||||
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`.
|
||||
115
docs/guides/direct-manipulation.md
Normal file
115
docs/guides/direct-manipulation.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# 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.
|
||||
|
||||
## `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
|
||||
|
||||
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?
|
||||
|
||||
Using `setNativeProps` in web-specific code is required when making changes to
|
||||
`className` or `style`, as these properties are controlled by React Native for
|
||||
Web and setting them directly may cause unintended rendering issues.
|
||||
|
||||
```js
|
||||
setOpacityTo(value) {
|
||||
this._childElement.setNativeProps({
|
||||
style: { opacity: value }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Composite components and `setNativeProps`
|
||||
|
||||
Composite components are not backed by a DOM node, so you cannot call
|
||||
`setNativeProps` on them. Consider this example:
|
||||
|
||||
```js
|
||||
const MyButton = (props) => (
|
||||
<View>
|
||||
<Text>{props.label}</Text>
|
||||
</View>
|
||||
)
|
||||
|
||||
const App = () => (
|
||||
<TouchableOpacity>
|
||||
<MyButton label="Press me!" />
|
||||
</TouchableOpacity>
|
||||
)
|
||||
```
|
||||
|
||||
If you run this you will immediately see this error: `Touchable` child must
|
||||
either be native or forward `setNativeProps` to a native component. This occurs
|
||||
because `MyButton` isn't directly backed by a native view whose opacity should
|
||||
be set. You can think about it like this: if you define a component with
|
||||
`React.Component/createClass` you would not expect to be able to set a style
|
||||
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
|
||||
|
||||
All we need to do is provide a `setNativeProps` method on our component that
|
||||
calls `setNativeProps` on the appropriate child with the given arguments.
|
||||
|
||||
```js
|
||||
class MyButton extends React.Component {
|
||||
setNativeProps(nativeProps) {
|
||||
this._root.setNativeProps(nativeProps)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View ref={component => this._root = component}>
|
||||
<Text>{this.props.label}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can now use `MyButton` inside of `TouchableOpacity`!
|
||||
|
||||
## `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
|
||||
when you tap a button:
|
||||
|
||||
```js
|
||||
class App extends React.Component {
|
||||
_handlePress() {
|
||||
this._textInput.setNativeProps({ text: '' })
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TextInput
|
||||
ref={component => this._textInput = component}
|
||||
style={styles.textInput}
|
||||
/>
|
||||
<TouchableOpacity onPress={this._handlePress.bind(this)}>
|
||||
<Text>Clear text</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
203
docs/guides/getting-started.md
Normal file
203
docs/guides/getting-started.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# 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
|
||||
[create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
(which supports `react-native-web` out-of-the-box once installed),
|
||||
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter),
|
||||
or [react-native-web-webpack](https://github.com/ndbroadbent/react-native-web-webpack).
|
||||
|
||||
It is recommended that your application provide a `Promise` and `Array.from`
|
||||
polyfill.
|
||||
|
||||
## Webpack and Babel
|
||||
|
||||
[Webpack](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.
|
||||
|
||||
```js
|
||||
// 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
|
||||
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.
|
||||
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' ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to the Webpack documentation for more information.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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 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.
|
||||
|
||||
## Client-side rendering
|
||||
|
||||
Rendering using `ReactNative`:
|
||||
|
||||
```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 `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')
|
||||
})
|
||||
```
|
||||
|
||||
Rendering within `ReactDOM.render` also works when introducing
|
||||
`react-native-web` to an existing web app, but otherwise it is not recommended.
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
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);
|
||||
|
||||
// construct HTML document
|
||||
const document = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
${stylesheet}
|
||||
</head>
|
||||
<body>
|
||||
${initialHTML}
|
||||
`
|
||||
```
|
||||
29
docs/guides/internationalization.md
Normal file
29
docs/guides/internationalization.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Internationalization
|
||||
|
||||
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.
|
||||
|
||||
## Working with icons and images
|
||||
|
||||
Icons and images that must match the LTR or RTL layout of the app need to be manually flipped.
|
||||
|
||||
Either use a transform style:
|
||||
|
||||
```js
|
||||
<Image
|
||||
source={...}
|
||||
style={{ transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }] }}
|
||||
/>
|
||||
```
|
||||
|
||||
Or replace the source asset:
|
||||
|
||||
```js
|
||||
import imageSourceLTR from './back.png';
|
||||
import imageSourceRTL from './forward.png';
|
||||
|
||||
<Image
|
||||
source={I18nManager.isRTL ? imageSourceRTL : imageSourceLTR}
|
||||
/>
|
||||
```
|
||||
31
docs/guides/known-issues.md
Normal file
31
docs/guides/known-issues.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Known issues
|
||||
|
||||
## Safari flexbox performance
|
||||
|
||||
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
|
||||
module with `Platform.OS` (e.g. `NativeModules`)
|
||||
|
||||
## Missing component props
|
||||
|
||||
There are properties that do not work across all platforms. All web-specific
|
||||
props are annotated with `(web)` in the documentation.
|
||||
|
||||
## Platform parity
|
||||
|
||||
There are some known issues in React Native where APIs could be made more
|
||||
consistent between platforms. For example, React Native for Web includes
|
||||
`ActivityIndicator` and a horizontal `ProgressBar` for Web use, in anticipation
|
||||
of the convergence between the iOS and Android components in React Native.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user