mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-04-10 10:01:12 +08:00
Compare commits
722 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa22b06359 | ||
|
|
2aa565c7c3 | ||
|
|
7b9b57960d | ||
|
|
eae3ee9dca | ||
|
|
74e1a196b6 | ||
|
|
48da9814e7 | ||
|
|
4d391ef57c | ||
|
|
780df69a80 | ||
|
|
6c229da01f | ||
|
|
ae7aa818fb | ||
|
|
306cf67932 | ||
|
|
619c2048be | ||
|
|
b9f9a4f8d7 | ||
|
|
58bc18c2f5 | ||
|
|
073940fc4e | ||
|
|
5c462303de | ||
|
|
5fb92da317 | ||
|
|
cafe10d851 | ||
|
|
b28581f44e | ||
|
|
9333e7e887 | ||
|
|
b28cbbb37e | ||
|
|
a53372ceb3 | ||
|
|
239a43978f | ||
|
|
b4e4bfbb3c | ||
|
|
893963a799 | ||
|
|
e5adc5a37c | ||
|
|
6d908189a7 | ||
|
|
31db333ba3 | ||
|
|
9fe089ca21 | ||
|
|
a314d5b2e4 | ||
|
|
fb845ebf44 | ||
|
|
0d0c7e6e27 | ||
|
|
f37003a079 | ||
|
|
f1fc2a9e37 | ||
|
|
92794cdc9f | ||
|
|
b754776373 | ||
|
|
155b34e495 | ||
|
|
00c9dc4236 | ||
|
|
b66aba1a06 | ||
|
|
17f8a674b8 | ||
|
|
b8080ba775 | ||
|
|
7265736545 | ||
|
|
399f465e59 | ||
|
|
9ee89bc7f7 | ||
|
|
748b2d0f3f | ||
|
|
fb4635e013 | ||
|
|
73b459e770 | ||
|
|
a41af0f65f | ||
|
|
96eecc0da3 | ||
|
|
69d5373222 | ||
|
|
538ab88eda | ||
|
|
21b3f39c0b | ||
|
|
998e275e65 | ||
|
|
31d428a649 | ||
|
|
240cf7e05f | ||
|
|
2ad710d83a | ||
|
|
dcce72b66e | ||
|
|
083769d642 | ||
|
|
a53dba8c62 | ||
|
|
670d43ba04 | ||
|
|
73a731f2da | ||
|
|
1542f1f369 | ||
|
|
6f58d7abe7 | ||
|
|
7e0fbf9691 | ||
|
|
865034e8f7 | ||
|
|
6e96ee4f3c | ||
|
|
16d98b49f0 | ||
|
|
d04721c75a | ||
|
|
8512709251 | ||
|
|
efeaea70a9 | ||
|
|
a403244e67 | ||
|
|
985c1d63b6 | ||
|
|
9d8d4057f6 | ||
|
|
ec8843fe90 | ||
|
|
935970156c | ||
|
|
e4e6147081 | ||
|
|
3e1b68d801 | ||
|
|
1b493c9914 | ||
|
|
6ecdc1a517 | ||
|
|
619079cedf | ||
|
|
bbf7674b43 | ||
|
|
f163e4f16f | ||
|
|
bd8c2d6f24 | ||
|
|
3906b6b41b | ||
|
|
753ef963f6 | ||
|
|
0721245b3e | ||
|
|
b7adfd5f32 | ||
|
|
a9342daee2 | ||
|
|
ed0cafac7c | ||
|
|
6e6fd4b5d0 | ||
|
|
5cd533e6cc | ||
|
|
d5e8d85ce9 | ||
|
|
e234568a34 | ||
|
|
19cf0711bc | ||
|
|
067e3f346f | ||
|
|
2117e44e9d | ||
|
|
902ba22877 | ||
|
|
60c2cd65df | ||
|
|
fde29326f1 | ||
|
|
44d795437e | ||
|
|
03598d869b | ||
|
|
a3e44a5c60 | ||
|
|
02b124eceb | ||
|
|
91472bc3d6 | ||
|
|
7f45c52ce7 | ||
|
|
b6ef1d3a36 | ||
|
|
fd6ccbcfb3 | ||
|
|
17614e348b | ||
|
|
c26ef0eb3b | ||
|
|
b78206d2f4 | ||
|
|
69e0396fb1 | ||
|
|
6d9154196e | ||
|
|
87fdd6c73b | ||
|
|
209bd3aee1 | ||
|
|
46e77d0b00 | ||
|
|
6f10f6be9c | ||
|
|
0d0fdc15ac | ||
|
|
bff3f50ae0 | ||
|
|
85aaa39206 | ||
|
|
b85a7062be | ||
|
|
af47d5f414 | ||
|
|
41d90e0238 | ||
|
|
86263a2fa0 | ||
|
|
f6d1caab9d | ||
|
|
1776891736 | ||
|
|
f52a851972 | ||
|
|
3026465ae3 | ||
|
|
14d87f4b30 | ||
|
|
5881f07323 | ||
|
|
b545fe47a7 | ||
|
|
4da4dd57c4 | ||
|
|
3e12ddfb2b | ||
|
|
3ecf5d2ed2 | ||
|
|
0a5acdb996 | ||
|
|
a712a58eba | ||
|
|
6de892c92b | ||
|
|
495defd69b | ||
|
|
1a20fcfce6 | ||
|
|
ed1e45a43d | ||
|
|
556dc8926e | ||
|
|
66cf45b90b | ||
|
|
d1e49e06e6 | ||
|
|
8bf28dbe43 | ||
|
|
9ae95d0797 | ||
|
|
321051b723 | ||
|
|
5f68542529 | ||
|
|
82c044ee33 | ||
|
|
9bcc67e73a | ||
|
|
f1ce6c2acb | ||
|
|
034108a2a0 | ||
|
|
f96d7b868f | ||
|
|
0dfe319d41 | ||
|
|
b7e970f4e6 | ||
|
|
02e62ad5d6 | ||
|
|
541d2458fb | ||
|
|
b1e860ab40 | ||
|
|
e8eab9b3ec | ||
|
|
6bc76c3c92 | ||
|
|
2acd8e477c | ||
|
|
ff2b0c9bdc | ||
|
|
79208720d1 | ||
|
|
fca04c4125 | ||
|
|
5b5b72cc19 | ||
|
|
217ad97bfd | ||
|
|
3e3cfc5325 | ||
|
|
da86ea98fc | ||
|
|
5f3e422b5c | ||
|
|
1f1f89b062 | ||
|
|
0f79960b85 | ||
|
|
117ce59f27 | ||
|
|
214121480e | ||
|
|
6261536f57 | ||
|
|
a748b7e606 | ||
|
|
92952ee746 | ||
|
|
c22a9aff7d | ||
|
|
dd8a3c8d59 | ||
|
|
4a1abee1df | ||
|
|
92ef3ffbb8 | ||
|
|
899763bc34 | ||
|
|
c69ad3c2d6 | ||
|
|
9a5b932139 | ||
|
|
ba96e457b4 | ||
|
|
bdfe943bd5 | ||
|
|
3870445b7e | ||
|
|
5395a3e8bc | ||
|
|
45b3d8b0df | ||
|
|
f1ee3c003a | ||
|
|
606181406c | ||
|
|
b537400f38 | ||
|
|
0a4fdc155e | ||
|
|
0b8e59974b | ||
|
|
22eebea633 | ||
|
|
5dd414f9aa | ||
|
|
72c72f6530 | ||
|
|
de970f9dbb | ||
|
|
e8d6c5b4dd | ||
|
|
e91a5ae13e | ||
|
|
b08bfb9ad5 | ||
|
|
5353011ee4 | ||
|
|
5faa3af19a | ||
|
|
c8461c9c11 | ||
|
|
3112e2ba56 | ||
|
|
fee03e101c | ||
|
|
c730a20a26 | ||
|
|
f7ed60ac67 | ||
|
|
417716391a | ||
|
|
ee5e3cb3ca | ||
|
|
2298c5d6e3 | ||
|
|
ca9f870ff6 | ||
|
|
e9b2fd8bc4 | ||
|
|
09a4985fd2 | ||
|
|
a9e61b4cd5 | ||
|
|
6ef19c3ccd | ||
|
|
0d29458874 | ||
|
|
1795bae8b5 | ||
|
|
c1152ee09a | ||
|
|
182149aac7 | ||
|
|
d6df440987 | ||
|
|
df16c24d68 | ||
|
|
8a2c259235 | ||
|
|
a7f265de11 | ||
|
|
581529540a | ||
|
|
8ad61d9061 | ||
|
|
3d79861970 | ||
|
|
0a84ccb299 | ||
|
|
3aa37450a0 | ||
|
|
450722153d | ||
|
|
fbba32defb | ||
|
|
d762d64b49 | ||
|
|
a5b1dda62d | ||
|
|
aad904b550 | ||
|
|
a01e895e30 | ||
|
|
52e5d41518 | ||
|
|
77a40b6237 | ||
|
|
4bbe1a40aa | ||
|
|
6942e4e417 | ||
|
|
63daff7f80 | ||
|
|
662b7c3d6e | ||
|
|
12fb588596 | ||
|
|
5cb09b1a9e | ||
|
|
c03cfdf8bd | ||
|
|
997b598de8 | ||
|
|
6fe796f9da | ||
|
|
0ad6ab948b | ||
|
|
32a23136af | ||
|
|
96f48226cb | ||
|
|
f1ef0f21af | ||
|
|
d42e8907ca | ||
|
|
90724b2cef | ||
|
|
dd0f1de3d1 | ||
|
|
fc751ed715 | ||
|
|
917b06a690 | ||
|
|
80cb7baf82 | ||
|
|
f08515b1f1 | ||
|
|
8591bf7ce5 | ||
|
|
b7c8f00fcc | ||
|
|
ed81b985a9 | ||
|
|
441dc8efff | ||
|
|
7e8ef5b72c | ||
|
|
641c8c47e0 | ||
|
|
87ead7f64e | ||
|
|
5ed2127175 | ||
|
|
e802026298 | ||
|
|
5d24f4c8fa | ||
|
|
a9f1afc07c | ||
|
|
d185d81560 | ||
|
|
66fa09da8e | ||
|
|
bae4dd806a | ||
|
|
4a680fd9b6 | ||
|
|
e34e4e38d3 | ||
|
|
b23a4f55dc | ||
|
|
26bdf44a4c | ||
|
|
5e98107617 | ||
|
|
86ea0e5eff | ||
|
|
6e02c5690c | ||
|
|
c50d808a2b | ||
|
|
130b10c3f7 | ||
|
|
03ddf074ea | ||
|
|
f201a0347d | ||
|
|
fffbdff6ca | ||
|
|
23e5c8479c | ||
|
|
17e5e374ee | ||
|
|
ef907dce22 | ||
|
|
586f134f76 | ||
|
|
70e2a75b43 | ||
|
|
8756c20ade | ||
|
|
4081d17f25 | ||
|
|
4fa6f77d25 | ||
|
|
17d723559d | ||
|
|
8d80885f5d | ||
|
|
3ca4becc41 | ||
|
|
cc077e9019 | ||
|
|
1225b00cdb | ||
|
|
ed70617e91 | ||
|
|
134114de83 | ||
|
|
08ee7c83bb | ||
|
|
5fad78dcad | ||
|
|
e04343e48e | ||
|
|
5e3a946f8b | ||
|
|
4e3d8dbb02 | ||
|
|
fee909d26a | ||
|
|
9e58a7b5f1 | ||
|
|
20e1febe21 | ||
|
|
ef209ca281 | ||
|
|
a35949fa71 | ||
|
|
f5ac856c2d | ||
|
|
4b557b1e0b | ||
|
|
d4e9d9d256 | ||
|
|
0ff3e91592 | ||
|
|
092d5d12f7 | ||
|
|
507e0d41f5 | ||
|
|
9e863d5402 | ||
|
|
1364b1dfdf | ||
|
|
7f81e313ed | ||
|
|
a1892ec8b8 | ||
|
|
e6232d5980 | ||
|
|
e93a2eb478 | ||
|
|
44ecf1fe87 | ||
|
|
faec2b4a83 | ||
|
|
3677f0dd57 | ||
|
|
36d161a959 | ||
|
|
1ca18ab056 | ||
|
|
b43717e797 | ||
|
|
801d5f8c68 | ||
|
|
30f2ec9bf5 | ||
|
|
6f3e29f630 | ||
|
|
2607cb25ab | ||
|
|
8f56454ed7 | ||
|
|
d03e06632e | ||
|
|
66732394cb | ||
|
|
077d2f3e63 | ||
|
|
f6ad9c3afb | ||
|
|
f91ecaa81d | ||
|
|
ad3dee0204 | ||
|
|
1a0a40d9be | ||
|
|
0bf6e893c6 | ||
|
|
1190ca20a7 | ||
|
|
8f4bed8cb9 | ||
|
|
5a5d142100 | ||
|
|
fb999b5467 | ||
|
|
8b06f28281 | ||
|
|
9376c72a40 | ||
|
|
d4b1fde9cf | ||
|
|
f237fc3094 | ||
|
|
8777e25d8e | ||
|
|
18d60047d2 | ||
|
|
535a7b7027 | ||
|
|
bdaeac964c | ||
|
|
20257afe88 | ||
|
|
99348eaedb | ||
|
|
6cb16d059d | ||
|
|
3c660e2ad7 | ||
|
|
e9101abefe | ||
|
|
dfa8087f9a | ||
|
|
eaccd8799d | ||
|
|
85b2afc313 | ||
|
|
4865c7bcce | ||
|
|
9e9ab78130 | ||
|
|
00b795a87e | ||
|
|
1edf5241a1 | ||
|
|
4cfcdef264 | ||
|
|
a264c0b956 | ||
|
|
0d8aa24ff3 | ||
|
|
1b77ac4b2f | ||
|
|
44b185ed4c | ||
|
|
d1d570268a | ||
|
|
997c92f841 | ||
|
|
8e60690877 | ||
|
|
7bab19ae6c | ||
|
|
c7f287b207 | ||
|
|
02cfbf8987 | ||
|
|
6203a3fec6 | ||
|
|
d1d5461b29 | ||
|
|
b0ff4489a9 | ||
|
|
635fda8d63 | ||
|
|
5a5eb5425f | ||
|
|
44d59f4996 | ||
|
|
868ab55bac | ||
|
|
65d5a89040 | ||
|
|
deb0a85440 | ||
|
|
19381da37f | ||
|
|
47ba46780c | ||
|
|
88ddeca0c6 | ||
|
|
a61f71133e | ||
|
|
dad2888f7e | ||
|
|
5e9e81eafe | ||
|
|
7ae2a5e188 | ||
|
|
d13f78622b | ||
|
|
6ae68e948f | ||
|
|
b1b70a420d | ||
|
|
43d297bf59 | ||
|
|
060d96b42d | ||
|
|
dd5f8cf641 | ||
|
|
7f256c6423 | ||
|
|
05069253a1 | ||
|
|
f10ac058b6 | ||
|
|
6aa2ac1f70 | ||
|
|
79e6dbaab5 | ||
|
|
fc86c876e0 | ||
|
|
1f25ef82ae | ||
|
|
5b60dcf0ff | ||
|
|
1cf152e8a0 | ||
|
|
d034a0c6ec | ||
|
|
33d1cdf193 | ||
|
|
483a76cb5c | ||
|
|
65055028c6 | ||
|
|
93f425e414 | ||
|
|
ce4cc8a946 | ||
|
|
77fd867421 | ||
|
|
22999d7e5b | ||
|
|
3c400a662b | ||
|
|
e78ce713cb | ||
|
|
70282cb4ca | ||
|
|
7abdb33a1d | ||
|
|
a9c7b38df9 | ||
|
|
d57fb6407a | ||
|
|
bcdeda5dab | ||
|
|
edef737249 | ||
|
|
9163b974db | ||
|
|
a388ef3e26 | ||
|
|
a84ecefa5d | ||
|
|
54af7e9da2 | ||
|
|
be3c78f317 | ||
|
|
6b85f5a22a | ||
|
|
875a2c98b3 | ||
|
|
6525d9d84a | ||
|
|
61356a786b | ||
|
|
864250f34d | ||
|
|
7ee570f0ed | ||
|
|
118b64a932 | ||
|
|
3cc1e480a7 | ||
|
|
124de7562d | ||
|
|
7aef8f04c1 | ||
|
|
08a353fbef | ||
|
|
51557d306b | ||
|
|
6b910166b2 | ||
|
|
668d389035 | ||
|
|
79a6a5a486 | ||
|
|
1c37a42566 | ||
|
|
c38369ac0f | ||
|
|
03769f7d45 | ||
|
|
eb43a8f3e7 | ||
|
|
cdf13b880d | ||
|
|
47fad1ef58 | ||
|
|
5f69c8e8b8 | ||
|
|
21550db5f2 | ||
|
|
1cae5d55a1 | ||
|
|
11d23f850a | ||
|
|
d994a25017 | ||
|
|
756df70154 | ||
|
|
f0b06419f9 | ||
|
|
60ff75705e | ||
|
|
5e8ad67296 | ||
|
|
ba24a882be | ||
|
|
c7686209cd | ||
|
|
f1b281ae32 | ||
|
|
b676fbd5e0 | ||
|
|
51aef6c791 | ||
|
|
ae9a9cde5f | ||
|
|
beb907b180 | ||
|
|
a3362e1f38 | ||
|
|
64d2d34367 | ||
|
|
d83cd45b6f | ||
|
|
227971d22c | ||
|
|
4822cf4620 | ||
|
|
91e4528eac | ||
|
|
1ee64d8285 | ||
|
|
66a4c13bf3 | ||
|
|
9012e98ba7 | ||
|
|
046e01dfa9 | ||
|
|
6e71e1e058 | ||
|
|
d5a9f3e779 | ||
|
|
f16f5f21ce | ||
|
|
0bb7e67e63 | ||
|
|
c6b54930b6 | ||
|
|
599f1fcaf5 | ||
|
|
3f7a4e455f | ||
|
|
1f3e9cc6ee | ||
|
|
17ed63129f | ||
|
|
769334d04e | ||
|
|
dad80d5718 | ||
|
|
d8e93058da | ||
|
|
4ae894313f | ||
|
|
438f398022 | ||
|
|
630ee24fdd | ||
|
|
ae13873c2c | ||
|
|
7705f521c8 | ||
|
|
cbd98a8bd7 | ||
|
|
1f80e4c105 | ||
|
|
dbc8f31be6 | ||
|
|
ed994dc670 | ||
|
|
a57e58607a | ||
|
|
03ea259d70 | ||
|
|
e39b58fd04 | ||
|
|
ab45211401 | ||
|
|
32183bb92a | ||
|
|
761c42301d | ||
|
|
0863894f40 | ||
|
|
8f736ddefe | ||
|
|
ab686e2a07 | ||
|
|
2c14bdab2e | ||
|
|
0b8b064757 | ||
|
|
93eadb734b | ||
|
|
8d561d7309 | ||
|
|
cdca9e1e2b | ||
|
|
170bab659d | ||
|
|
941c628445 | ||
|
|
547c375bd6 | ||
|
|
aa85876eb2 | ||
|
|
50b168cc41 | ||
|
|
25a11e673d | ||
|
|
e846054f4e | ||
|
|
d6854abd7d | ||
|
|
1b172319b9 | ||
|
|
e81394c26e | ||
|
|
d33aa3eee2 | ||
|
|
5d78c73e8c | ||
|
|
7735d304ef | ||
|
|
b7c72308ea | ||
|
|
5fee075774 | ||
|
|
25204eeff0 | ||
|
|
9c61fe58d3 | ||
|
|
782125d169 | ||
|
|
af805d67e6 | ||
|
|
68068f8cb6 | ||
|
|
e05e2122d7 | ||
|
|
47dac44120 | ||
|
|
22af6894c2 | ||
|
|
458c534200 | ||
|
|
ec2db3e2a3 | ||
|
|
e6f00f7592 | ||
|
|
976320916e | ||
|
|
808790505e | ||
|
|
89ad493ce5 | ||
|
|
c4f2869ad8 | ||
|
|
3ae1e5b786 | ||
|
|
e929fb572d | ||
|
|
5af9d537c2 | ||
|
|
b7d3f0d099 | ||
|
|
f1ca00a13a | ||
|
|
9451c0f85e | ||
|
|
b408bc5537 | ||
|
|
a2f25a46c4 | ||
|
|
29d52f5b31 | ||
|
|
ba6be1f64a | ||
|
|
43f78828a5 | ||
|
|
26bc8173f0 | ||
|
|
ecae52ccc5 | ||
|
|
997653863f | ||
|
|
5dc692719f | ||
|
|
0361845537 | ||
|
|
f391031fb1 | ||
|
|
77799abf9b | ||
|
|
2cfd09ecdb | ||
|
|
89eea2b366 | ||
|
|
18440158b3 | ||
|
|
24eda7c4ad | ||
|
|
44ebd8f5a3 | ||
|
|
a3ed8f05e6 | ||
|
|
b653fe0bd3 | ||
|
|
30da226e4d | ||
|
|
f1f39bfabd | ||
|
|
267c5aab7e | ||
|
|
fe71c7efe5 | ||
|
|
eb59875bed | ||
|
|
e1fc253277 | ||
|
|
40b03aca52 | ||
|
|
418a1a9516 | ||
|
|
8762f8e9c8 | ||
|
|
10ef2bfe20 | ||
|
|
6d2ae4597e | ||
|
|
a34b8b149f | ||
|
|
6166024d15 | ||
|
|
701ecb7c52 | ||
|
|
75042093c2 | ||
|
|
bb417900a9 | ||
|
|
89e0a15d1b | ||
|
|
b2e0a3702f | ||
|
|
a4644c204d | ||
|
|
1e9536b611 | ||
|
|
d15dafc108 | ||
|
|
c9c1aab97e | ||
|
|
a2903f9d30 | ||
|
|
c7771ac64f | ||
|
|
c8129c2a99 | ||
|
|
b793737393 | ||
|
|
2a4d1c81d8 | ||
|
|
a8a25d66ea | ||
|
|
e06d7a9650 | ||
|
|
c2501f2bc2 | ||
|
|
c51e7f1965 | ||
|
|
dfff6b3780 | ||
|
|
5f6b4a746a | ||
|
|
f077907dd4 | ||
|
|
a94367bdcb | ||
|
|
65febbbc52 | ||
|
|
b14d2e5bd8 | ||
|
|
7c83ba162d | ||
|
|
3ffc005a7b | ||
|
|
50a70ad02f | ||
|
|
768e895701 | ||
|
|
af5fde994d | ||
|
|
c3d0763944 | ||
|
|
0aba506725 | ||
|
|
91032d8565 | ||
|
|
0696721488 | ||
|
|
fe18830ce6 | ||
|
|
1b86d02300 | ||
|
|
c56b472258 | ||
|
|
b00132f007 | ||
|
|
8b8f8f0374 | ||
|
|
8e94af34e1 | ||
|
|
7ffaf592d5 | ||
|
|
a1017fa785 | ||
|
|
5db300df35 | ||
|
|
214d862e61 | ||
|
|
4ef5453b33 | ||
|
|
a27671d7cf | ||
|
|
8d2a650670 | ||
|
|
4cc1c983e8 | ||
|
|
37413fd55f | ||
|
|
07ff0ea104 | ||
|
|
1a87657500 | ||
|
|
5e4c8e520a | ||
|
|
236121e32c | ||
|
|
39c76ca50c | ||
|
|
e65f91d849 | ||
|
|
a535c558d8 | ||
|
|
a74be91b7c | ||
|
|
8eaaf28a32 | ||
|
|
16d448dc5b | ||
|
|
ea75cced13 | ||
|
|
cfc56a1354 | ||
|
|
b1cd92a65d | ||
|
|
d87f71ebc1 | ||
|
|
a2cafe56fc | ||
|
|
351c0ac3d4 | ||
|
|
877c0d2818 | ||
|
|
3afc5d5de6 | ||
|
|
edf3b9b7ff | ||
|
|
518a85bf1b | ||
|
|
ba75acb66a | ||
|
|
bc68b0b6f4 | ||
|
|
44ecbc072e | ||
|
|
4cf4905fc2 | ||
|
|
509920be4b | ||
|
|
04e3c23e67 | ||
|
|
32f454de66 | ||
|
|
1273bfc7cf | ||
|
|
dc7f526f6b | ||
|
|
7cda89c5ce | ||
|
|
695eba45af | ||
|
|
92a2cb274a | ||
|
|
b1ca04d11e | ||
|
|
22ab70ea6f | ||
|
|
49f36d8eb1 | ||
|
|
80ba119b83 | ||
|
|
c30b67f6db | ||
|
|
4580f93199 | ||
|
|
4c46126ffe | ||
|
|
f8d5c15405 | ||
|
|
dc54e03625 | ||
|
|
4d5819ae28 | ||
|
|
5c482ef3be | ||
|
|
f51592f96e | ||
|
|
6bffe1775f | ||
|
|
7e75d037f2 | ||
|
|
7536508fe3 | ||
|
|
945fff0015 | ||
|
|
5032ed6fe1 | ||
|
|
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 |
@@ -1,9 +0,0 @@
|
||||
# EditorConfig: http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
coverage
|
||||
dist
|
||||
node_modules
|
||||
packages/**/vendor/*
|
||||
88
.eslintrc
88
.eslintrc
@@ -9,8 +9,11 @@
|
||||
},
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": [
|
||||
"prettier",
|
||||
"prettier/react"
|
||||
],
|
||||
"plugins": [
|
||||
"jsx-a11y",
|
||||
"promise",
|
||||
"react"
|
||||
],
|
||||
@@ -21,34 +24,24 @@
|
||||
"globals": {
|
||||
"document": false,
|
||||
"navigator": false,
|
||||
"window": false
|
||||
"window": false,
|
||||
// Flow global types
|
||||
"HTMLInputElement": false,
|
||||
"ReactClass": false,
|
||||
"ReactComponent": false,
|
||||
"ReactElement": false,
|
||||
"ReactPropsChainableTypeChecker": false,
|
||||
"ReactPropsCheckType": false,
|
||||
"ReactPropTypes": false,
|
||||
"SyntheticEvent": false
|
||||
},
|
||||
"rules": {
|
||||
"accessor-pairs": 2,
|
||||
"array-bracket-spacing": ["error", "always"],
|
||||
"arrow-parens": [2, "always"],
|
||||
"arrow-spacing": [2, { "before": true, "after": true }],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"camelcase": 0,
|
||||
"comma-dangle": [2, "never"],
|
||||
"comma-spacing": [2, { "before": false, "after": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"computed-property-spacing": ["error", "never"],
|
||||
"constructor-super": 2,
|
||||
"curly": [2, "all"],
|
||||
"default-case": [2, { commentPattern: '^no default$' }],
|
||||
"dot-location": [2, "property"],
|
||||
"eol-last": 2,
|
||||
"eqeqeq": [2, "allow-null"],
|
||||
"generator-star-spacing": [2, { "before": true, "after": true }],
|
||||
"handle-callback-err": [2, "^(err|error)$" ],
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, { "before": true, "after": true }],
|
||||
"max-len": [2, 120, 4],
|
||||
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
|
||||
"new-parens": 2,
|
||||
"no-alert": 1,
|
||||
"no-array-constructor": 2,
|
||||
"no-caller": 2,
|
||||
@@ -71,8 +64,6 @@
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-extra-boolean-cast": 2,
|
||||
"no-extra-parens": [2, "functions"],
|
||||
"no-extra-semi": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-floating-decimal": 2,
|
||||
"no-func-assign": 2,
|
||||
@@ -85,10 +76,7 @@
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-multiple-empty-lines": [2, { "max": 1 }],
|
||||
"no-native-reassign": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-new": 2,
|
||||
@@ -110,11 +98,9 @@
|
||||
"no-self-compare": 2,
|
||||
"no-sequences": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-sparse-arrays": 2,
|
||||
"no-this-before-super": 2,
|
||||
"no-throw-literal": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-undef": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
@@ -129,66 +115,20 @@
|
||||
"no-useless-constructor": 2,
|
||||
"no-useless-escape": 2,
|
||||
"no-var": 2,
|
||||
"no-whitespace-before-property": 2,
|
||||
"no-with": 2,
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"operator-linebreak": [2, "after"],
|
||||
"padded-blocks": [2, "never"],
|
||||
"prefer-const": 2,
|
||||
"prefer-rest-params": 2,
|
||||
"prefer-template": 2,
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"radix": 2,
|
||||
"rest-spread-spacing": ["error"],
|
||||
"semi": [2, "always"],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": 2,
|
||||
"valid-typeof": 2,
|
||||
"wrap-iife": [2, "outside"],
|
||||
"yield-star-spacing": [2, "both"],
|
||||
"yoda": [2, "never"],
|
||||
|
||||
// promise
|
||||
"promise/param-names": 2,
|
||||
|
||||
// jsx accessibility
|
||||
"jsx-a11y/aria-props": 2,
|
||||
"jsx-a11y/aria-proptypes": 2,
|
||||
"jsx-a11y/aria-role": 2,
|
||||
"jsx-a11y/aria-unsupported-elements": 2,
|
||||
"jsx-a11y/heading-has-content": 2,
|
||||
"jsx-a11y/href-no-hash": 2,
|
||||
"jsx-a11y/html-has-lang": 2,
|
||||
"jsx-a11y/img-has-alt": 2,
|
||||
"jsx-a11y/img-redundant-alt": 2,
|
||||
"jsx-a11y/label-has-for": 2,
|
||||
"jsx-a11y/mouse-events-have-key-events": 2,
|
||||
"jsx-a11y/no-access-key": 2,
|
||||
"jsx-a11y/no-marquee": 2,
|
||||
"jsx-a11y/no-onchange": 0,
|
||||
"jsx-a11y/onclick-has-focus": 2,
|
||||
"jsx-a11y/onclick-has-role": 2,
|
||||
"jsx-a11y/role-has-required-aria-props": 2,
|
||||
"jsx-a11y/role-supports-aria-props": 2,
|
||||
"jsx-a11y/scope": 2,
|
||||
"jsx-a11y/tabindex-no-positive": 2,
|
||||
|
||||
// react
|
||||
"jsx-quotes": [2, "prefer-single"],
|
||||
"react/display-name": 0,
|
||||
"react/jsx-boolean-value": 2,
|
||||
"react/jsx-handler-names": [2, {
|
||||
"eventHandlerPrefix": "_handle"
|
||||
}],
|
||||
"react/jsx-indent": [2, 2],
|
||||
"react/jsx-indent-props": [2, 2],
|
||||
"react/jsx-no-bind": 2,
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/jsx-no-undef": 2,
|
||||
|
||||
16
.flowconfig
Normal file
16
.flowconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
[version]
|
||||
^0.63.0
|
||||
|
||||
[ignore]
|
||||
<PROJECT_ROOT>/.*/__tests__/.*
|
||||
<PROJECT_ROOT>/packages/.*/dist/.*
|
||||
<PROJECT_ROOT>/website/.*
|
||||
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
<PROJECT_ROOT>/types
|
||||
|
||||
[options]
|
||||
|
||||
140
.github/CONTRIBUTING.md
vendored
Normal file
140
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
# Contributing
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
|
||||
issue hasn't already been reported.
|
||||
|
||||
## Getting started
|
||||
|
||||
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
|
||||
to find a list of open issues that need attention.
|
||||
|
||||
Fork, then clone the repo:
|
||||
|
||||
```
|
||||
git clone https://github.com/your-username/react-native-web.git
|
||||
```
|
||||
|
||||
Install dependencies (requires [yarn](https://yarnpkg.com/en/docs/install)):
|
||||
|
||||
```
|
||||
yarn
|
||||
```
|
||||
|
||||
## Automated tests
|
||||
|
||||
To run the linter:
|
||||
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
To run flow:
|
||||
|
||||
```
|
||||
yarn flow
|
||||
```
|
||||
|
||||
To run the unit tests:
|
||||
|
||||
```
|
||||
yarn jest
|
||||
```
|
||||
|
||||
…in watch mode:
|
||||
|
||||
```
|
||||
yarn jest --watch
|
||||
```
|
||||
|
||||
To run all these automated tests:
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
## Compile and build
|
||||
|
||||
To compile the `react-native-web` source code:
|
||||
|
||||
```
|
||||
yarn compile
|
||||
```
|
||||
|
||||
…in watch mode:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
```
|
||||
|
||||
## Website and visual tests
|
||||
|
||||
To run the interactive storybook:
|
||||
|
||||
```
|
||||
yarn website
|
||||
```
|
||||
|
||||
When you're also making changes to the 'react-native-web' source files, run this command in another process:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
To run the benchmarks locally:
|
||||
|
||||
```
|
||||
yarn benchmarks
|
||||
open ./packages/benchmarks/dist/index.html
|
||||
```
|
||||
|
||||
To develop against these benchmarks:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
yarn benchmarks --watch
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
Please open an issue with a proposal for a new feature or refactoring before
|
||||
starting on the work. We don't want you to waste your efforts on a pull request
|
||||
that we won't want to accept.
|
||||
|
||||
## Pull requests
|
||||
|
||||
**Before submitting a pull request**, please make sure the following is done:
|
||||
|
||||
1. Fork the repository and create your branch from `master`.
|
||||
2. If you've added code that should be tested, add tests!
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Ensure the tests pass (`yarn test`).
|
||||
|
||||
You can now submit a pull request, referencing any issues it addresses.
|
||||
|
||||
Please try to keep your pull request focused in scope and avoid including
|
||||
unrelated commits.
|
||||
|
||||
After you have submitted your pull request, we'll try to get back to you as
|
||||
soon as possible. We may suggest some changes or improvements.
|
||||
|
||||
Thank you for contributing!
|
||||
|
||||
## Releases
|
||||
|
||||
To commit, publish, and push a final version:
|
||||
|
||||
```
|
||||
yarn release <version>
|
||||
```
|
||||
|
||||
Release candidates or versions that you'd like to publish to npm, but do not
|
||||
want to produce a commit and push it to GitHub:
|
||||
|
||||
```
|
||||
yarn release <version> --skip-git
|
||||
```
|
||||
34
.github/ISSUE_TEMPLATE.md
vendored
34
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,30 +1,20 @@
|
||||
<!--
|
||||
React Native for Web is an implementation of React Native. If you have feature
|
||||
requests, you should post them to https://productpains.com/product/react-native/.
|
||||
|
||||
GitHub issues should only be used for bugs or Web-specific features you believe
|
||||
React Native requires.
|
||||
|
||||
Make sure to add ALL the information needed to understand the bug so that
|
||||
someone can help. If the info is missing we'll add the 'needs more information'
|
||||
label and close the issue until there is enough information.
|
||||
-->
|
||||
**Do you want to request a *feature* or report a *bug*?**
|
||||
|
||||
**What is the current behavior?**
|
||||
|
||||
Link to minimal test case: (template: [codepen](https://codepen.io/necolas/pen/PZzwBR?editors=0010))
|
||||
|
||||
**What is the expected behaviour?**
|
||||
|
||||
**Steps to reproduce**
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and
|
||||
if a minimal demo of the problem via Glitch or similar (template:
|
||||
https://glitch.com/edit/#!/react-native-web-playground).**
|
||||
|
||||
1.
|
||||
2.
|
||||
|
||||
**Environment (include versions)**
|
||||
**What is the expected behavior?**
|
||||
|
||||
OS:
|
||||
Device:
|
||||
Browser:
|
||||
React Native for Web (version):
|
||||
React (version):
|
||||
**Environment (include versions). Did this work in previous versions?**
|
||||
|
||||
* OS:
|
||||
* Device:
|
||||
* Browser:
|
||||
* React Native for Web (version):
|
||||
* React (version):
|
||||
|
||||
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
18
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,17 +1 @@
|
||||
<!--
|
||||
Thanks for submitting a pull request! Make sure the PR does only one thing.
|
||||
Please provide enough information so that others can review your pull
|
||||
request. Make sure you have read the Contributing Guidelines -
|
||||
https://github.com/necolas/react-native-web/CONTRIBUTING.md
|
||||
-->
|
||||
|
||||
**This patch solves the following problem**
|
||||
|
||||
**Test plan**
|
||||
|
||||
**This pull request**
|
||||
|
||||
- [ ] includes documentation
|
||||
- [ ] includes tests
|
||||
- [ ] includes an interactive example
|
||||
- [ ] includes screenshots/videos
|
||||
**Before submitting a pull request,** please make sure you have followed the steps the CONTRIBUTING guide.
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
/dist
|
||||
/dist-examples
|
||||
/node_modules
|
||||
coverage
|
||||
node_modules
|
||||
dist
|
||||
|
||||
20
.travis.yml
20
.travis.yml
@@ -1,9 +1,17 @@
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "6"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- "8"
|
||||
|
||||
before_install:
|
||||
# Install Yarn
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
- yarn test
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
We are open to, and grateful for, any contributions made by the community.
|
||||
|
||||
## Reporting Issues and Asking Questions
|
||||
|
||||
Before opening an issue, please search the [issue
|
||||
tracker](https://github.com/necolas/react-native-web/issues) to make sure your
|
||||
issue hasn't already been reported.
|
||||
|
||||
## Development
|
||||
|
||||
Visit the [Issue tracker](https://github.com/necolas/react-native-web/issues)
|
||||
to find a list of open issues that need attention.
|
||||
|
||||
Fork, then clone the repo:
|
||||
|
||||
```
|
||||
git clone https://github.com/your-username/react-native-web.git
|
||||
```
|
||||
|
||||
Run the examples:
|
||||
|
||||
```
|
||||
npm run examples
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
To create a UMD build:
|
||||
|
||||
```
|
||||
npm run build:umd
|
||||
```
|
||||
|
||||
### Testing and Linting
|
||||
|
||||
To run the tests:
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
To continuously watch and run tests, run the following:
|
||||
|
||||
```
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
To perform linting, run the following:
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### New Features
|
||||
|
||||
Please open an issue with a proposal for a new feature or refactoring before
|
||||
starting on the work. We don't want you to waste your efforts on a pull request
|
||||
that we won't want to accept.
|
||||
|
||||
## Submitting Changes
|
||||
|
||||
* Open a new issue in the [Issue tracker](https://github.com/necolas/react-native-web/issues).
|
||||
* Fork the repo.
|
||||
* Create a new feature branch based off the `master` branch.
|
||||
* Make sure all tests pass and there are no linting errors.
|
||||
* Submit a pull request, referencing any issues it addresses.
|
||||
|
||||
Please try to keep your pull request focused in scope and avoid including unrelated commits.
|
||||
|
||||
After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or improvements.
|
||||
|
||||
Thank you for contributing!
|
||||
45
LICENSE
45
LICENSE
@@ -1,31 +1,22 @@
|
||||
BSD License
|
||||
MIT License
|
||||
|
||||
For React Native software
|
||||
Copyright (c) 2015-present, Nicolas Gallagher.
|
||||
Copyright (c) 2015-present, Facebook, Inc.
|
||||
|
||||
Copyright (c) 2015-present, Nicolas Gallagher. All rights reserved.
|
||||
Copyright (c) 2015-present, Facebook, Inc. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
* 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.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
250
README.md
250
README.md
@@ -1,147 +1,149 @@
|
||||
# React Native for Web
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![npm version][npm-image]][npm-url]
|
||||
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
[React Native][react-native-url] components and APIs for the Web.
|
||||
"React Native for Web" brings the platform-agnostic Components and APIs of
|
||||
[React Native][react-native-url] to the Web.
|
||||
|
||||
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
|
||||
* **High-quality user interfaces**: React Native for Web makes it easy to
|
||||
create [fast](https://github.com/necolas/react-native-web/blob/master/packages/benchmarks/README.md),
|
||||
adaptive web UIs in JavaScript. It provides native-like interactions, support
|
||||
for multiple input modes (touch, mouse, keyboard), optimized vendor-prefixed
|
||||
styles, built-in support for RTL layout, built-in accessibility, and integrates
|
||||
with React Dev Tools.
|
||||
|
||||
[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
|
||||
* **Write once, render anywhere**: React Native for Web interoperates with
|
||||
existing React DOM components and is compatible with the majority of the
|
||||
React Native API. You can develop new components for native and web without
|
||||
rewriting existing code. React Native for Web can also render to HTML and
|
||||
critical CSS on the server using Node.js.
|
||||
|
||||
## Overview
|
||||
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
|
||||
[Major League Soccer](https://matchcenter.mlssoccer.com),
|
||||
[Flipkart](https://www.flipkart.com/), Playstation, Uber, [The
|
||||
Times](https://github.com/newsuk/times-components), [React Native's
|
||||
documentation](http://facebook.github.io/react-native/).
|
||||
|
||||
"React Native for Web" is a project to bring React Native's building blocks and
|
||||
touch handling to the Web. [Read more](#why).
|
||||
|
||||
Browse the UI Explorer to see React Native [examples running on
|
||||
Web](https://necolas.github.io/react-native-web/storybook/). Or try it out
|
||||
online with [React Native for Web: Playground](http://codepen.io/necolas/pen/PZzwBR).
|
||||
Browser support: Chrome, Firefox, Edge, Safari 7+, IE 10+.
|
||||
|
||||
## Quick start
|
||||
|
||||
To install in your app:
|
||||
|
||||
```
|
||||
npm install --save react react-native-web
|
||||
```
|
||||
|
||||
Read the [Client and Server rendering](docs/guides/rendering.md) guide.
|
||||
|
||||
Alternatively, you can quickly setup a local project
|
||||
using [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
(which supports `react-native-web` out-of-the-box once installed) and
|
||||
[react-native-web-starter](https://github.com/grabcode/react-native-web-starter).
|
||||
The easiest way to get started with React Native for Web is to use this
|
||||
[ready-to-go project on Glitch](https://glitch.com/edit/#!/react-native).
|
||||
You don’t need to install anything to try it out.
|
||||
|
||||
## Documentation
|
||||
|
||||
Guides:
|
||||
You can find the React Native for Web API documentation [on the
|
||||
website][website-url].
|
||||
|
||||
* [Accessibility](docs/guides/accessibility.md)
|
||||
* [Client and server rendering](docs/guides/rendering.md)
|
||||
* [Direct manipulation](docs/guides/direct-manipulation.md)
|
||||
* [Internationalization](docs/guides/internationalization.md)
|
||||
* [Known issues](docs/guides/known-issues.md)
|
||||
* [React Native](docs/guides/react-native.md)
|
||||
* [Style](docs/guides/style.md)
|
||||
Please refer to the [React Native documentation][react-native-url] for more
|
||||
design details, and for information about the [Gesture Responder
|
||||
system](https://facebook.github.io/react-native/docs/gesture-responder-system.html)
|
||||
and [animations](https://facebook.github.io/react-native/docs/animations.html).
|
||||
|
||||
Exported modules:
|
||||
### Installation
|
||||
|
||||
* Components
|
||||
* [`ActivityIndicator`](docs/components/ActivityIndicator.md)
|
||||
* [`Image`](docs/components/Image.md)
|
||||
* [`ListView`](docs/components/ListView.md)
|
||||
* [`ScrollView`](docs/components/ScrollView.md)
|
||||
* [`Switch`](docs/components/Switch.md)
|
||||
* [`Text`](docs/components/Text.md)
|
||||
* [`TextInput`](docs/components/TextInput.md)
|
||||
* [`TouchableHighlight`](http://facebook.github.io/react-native/releases/0.22/docs/touchablehighlight.html) (mirrors React Native)
|
||||
* [`TouchableOpacity`](http://facebook.github.io/react-native/releases/0.22/docs/touchableopacity.html) (mirrors React Native)
|
||||
* [`TouchableWithoutFeedback`](docs/components/TouchableWithoutFeedback.md)
|
||||
* [`View`](docs/components/View.md)
|
||||
* APIs
|
||||
* [`Animated`](http://facebook.github.io/react-native/releases/0.20/docs/animated.html) (mirrors React Native)
|
||||
* [`AppRegistry`](docs/apis/AppRegistry.md)
|
||||
* [`AppState`](docs/apis/AppState.md)
|
||||
* [`AsyncStorage`](docs/apis/AsyncStorage.md)
|
||||
* [`Dimensions`](docs/apis/Dimensions.md)
|
||||
* [`I18nManager`](docs/apis/I18nManager.md)
|
||||
* [`NativeMethods`](docs/apis/NativeMethods.md)
|
||||
* [`NetInfo`](docs/apis/NetInfo.md)
|
||||
* [`PanResponder`](http://facebook.github.io/react-native/releases/0.20/docs/panresponder.html#content) (mirrors React Native)
|
||||
* [`PixelRatio`](docs/apis/PixelRatio.md)
|
||||
* [`Platform`](docs/apis/Platform.md)
|
||||
* [`StyleSheet`](docs/apis/StyleSheet.md)
|
||||
* [`Vibration`](docs/apis/Vibration.md)
|
||||
Install using `yarn` or `npm`:
|
||||
|
||||
<span id="#why"></span>
|
||||
## Why?
|
||||
|
||||
There are many different teams at Twitter building web applications with React.
|
||||
We want to share React components, libraries, and APIs between teams…much like
|
||||
the OSS community tries to do. At our scale, this involves dealing with
|
||||
multiple, inter-related problems including: a common way to handle style,
|
||||
animation, touch, viewport adaptation, accessibility, themes, RTL layout, and
|
||||
server-rendering.
|
||||
|
||||
This is hard to do with React DOM, as the components are essentially the same
|
||||
low-level building blocks that the browser provides. However, React Native
|
||||
avoids, solves, or can solve almost all these problems facing Web teams.
|
||||
Central to this is React Native's JavaScript style API (not strictly
|
||||
"CSS-in-JS") which avoids the key [problems with
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js) by giving up some of the
|
||||
complexity of CSS.
|
||||
|
||||
## Example code
|
||||
|
||||
```js
|
||||
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'
|
||||
},
|
||||
title: {
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
image: {
|
||||
height: 40,
|
||||
marginVertical: 10,
|
||||
width: 40
|
||||
}
|
||||
})
|
||||
|
||||
// App registration and rendering
|
||||
AppRegistry.registerComponent('MyApp', () => App)
|
||||
AppRegistry.runApplication('MyApp', { rootTag: document.getElementById('react-root') })
|
||||
```
|
||||
yarn add react react-dom react-native-web
|
||||
yarn add --dev babel-plugin-react-native-web
|
||||
```
|
||||
|
||||
## Related projects
|
||||
### Guides
|
||||
|
||||
* [react-native-web-starter](https://github.com/grabcode/react-native-web-starter)
|
||||
* [react-native-web-player](https://github.com/dabbott/react-native-web-player)
|
||||
* [react-web](https://github.com/taobaofed/react-web)
|
||||
* [react-native-for-web](https://github.com/KodersLab/react-native-for-web)
|
||||
* [Getting started](https://github.com/necolas/react-native-web/blob/master/website/guides/getting-started.md)
|
||||
* [Style](https://github.com/necolas/react-native-web/blob/master/website/guides/style.md)
|
||||
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/website/guides/accessibility.md)
|
||||
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/website/guides/internationalization.md)
|
||||
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/website/guides/direct-manipulation.md)
|
||||
* [Advanced use](https://github.com/necolas/react-native-web/blob/master/website/guides/advanced.md)
|
||||
|
||||
## Examples
|
||||
|
||||
There are several examples [on the website][website-url] and in the [website's
|
||||
source code](https://github.com/necolas/react-native-web/blob/master/website).
|
||||
Here is an example to get you started:
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.box}>
|
||||
<Text style={styles.text}>Hello, world!</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: { padding: 10 },
|
||||
text: { fontWeight: 'bold' }
|
||||
});
|
||||
|
||||
AppRegistry.registerComponent('App', () => App);
|
||||
AppRegistry.runApplication('App', { rootTag: document.getElementById('react-root') });
|
||||
```
|
||||
|
||||
This example will render the `App` into a container on the page.
|
||||
|
||||
You'll notice that there is no reference to `react-dom`; the `App` component is
|
||||
defined using the platform-agnostic APIs and Components introduced by React
|
||||
Native. This allows the app to be rendered to web and native platforms.
|
||||
|
||||
## Integrations
|
||||
|
||||
Examples of using React Native for Web with other web tools:
|
||||
|
||||
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
|
||||
* [Storybook](https://github.com/necolas/react-native-web/tree/0.5.1/website/storybook/.storybook)
|
||||
* [Razzle](https://github.com/jaredpalmer/razzle/tree/master/examples/with-react-native-web)
|
||||
* [Phenomic](https://github.com/phenomic/phenomic/tree/v1.0.0-alpha.20/docs)
|
||||
* [Styleguidist](https://github.com/styleguidist/react-styleguidist/tree/v6.2.6/examples/react-native)
|
||||
|
||||
## Contributing
|
||||
|
||||
The main purpose of this repository is to help evolve React web and native
|
||||
development towards the platform-agnostic design of React Native, and in the
|
||||
process make it faster and easier to build high-quality experiences for the web
|
||||
with React. Development happens in the open on GitHub, and we are grateful for
|
||||
contributing bugfixes and improvements. Read below to learn how you can take
|
||||
part in improving React Native for Web.
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Facebook has adopted a [Code of Conduct][code-of-conduct] that this project
|
||||
expects all participants to adhere to. Please read the full text so that you
|
||||
can understand what actions will and will not be tolerated.
|
||||
|
||||
### Contributing guide
|
||||
|
||||
Read the [contributing guide][contributing-url] to learn about the
|
||||
development process, how to propose bugfixes and improvements, and how to build
|
||||
and test your changes to React Native for Web.
|
||||
|
||||
### Good first issues
|
||||
|
||||
To help you get you familiar with the contribution process, there is a list of
|
||||
[good first issues][good-first-issue-url] that contain bugs which have a
|
||||
relatively limited scope. This is a great place to get started.
|
||||
|
||||
## License
|
||||
|
||||
React Native for Web is [BSD licensed](LICENSE).
|
||||
React Native for Web is [MIT licensed](./LICENSE). By contributing to React
|
||||
Native for Web, you agree that your contributions will be licensed under its
|
||||
MIT license.
|
||||
|
||||
[package-badge]: https://img.shields.io/npm/v/react-native-web.svg?style=flat
|
||||
[package-url]: https://yarnpkg.com/en/package/react-native-web
|
||||
[ci-badge]: https://travis-ci.org/necolas/react-native-web.svg?branch=master
|
||||
[ci-url]: https://travis-ci.org/necolas/react-native-web
|
||||
[website-url]: https://necolas.github.io/react-native-web/storybook/
|
||||
[react-native-url]: https://facebook.github.io/react-native/
|
||||
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/.github/CONTRIBUTING.md
|
||||
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue
|
||||
[code-of-conduct]: https://code.facebook.com/codeofconduct
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
# 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`, and prerendered by invoking
|
||||
`AppRegistry.prerenderApplication` (see the [client and server rendering
|
||||
guide](../guides/rendering.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 **prerenderApplication**(appKey:string, appParameters: object)
|
||||
|
||||
Renders the given application to an HTML string. Use this for server-side
|
||||
rendering. Return object is of type `{ html: string; style: string;
|
||||
styleElement: ReactComponent }`. `html` is the prerendered HTML, `style` is the
|
||||
prerendered style sheet, and `styleElement` is a React Component. It's
|
||||
recommended that you use `styleElement` to render the style sheet in an app
|
||||
shell.
|
||||
|
||||
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')
|
||||
})
|
||||
```
|
||||
@@ -1,60 +0,0 @@
|
||||
## 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,71 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,13 +0,0 @@
|
||||
# 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')`
|
||||
@@ -1,25 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,42 +0,0 @@
|
||||
# NativeMethods
|
||||
|
||||
React Native for Web provides several methods to directly access the underlying
|
||||
DOM node. This can be useful in cases when you want to focus a view or measure
|
||||
its on-screen dimensions, for example.
|
||||
|
||||
The methods described are available on most of the default components provided
|
||||
by React Native for Web. Note, however, that they are *not* available on the
|
||||
composite components that you define in your own app. For more information, see
|
||||
[Direct Manipulation](../guides/direct-manipulation.md).
|
||||
|
||||
## Methods
|
||||
|
||||
**blur**()
|
||||
|
||||
Removes focus from an input or view. This is the opposite of `focus()`.
|
||||
|
||||
**focus**()
|
||||
|
||||
Requests focus for the given input or view. The exact behavior triggered will
|
||||
depend the type of view.
|
||||
|
||||
**measure**(callback: (x, y, width, height, pageX, pageY) => void)
|
||||
|
||||
For a given view, `measure` determines the offset relative to the parent view,
|
||||
width, height, and the offset relative to the viewport. Returns the values via
|
||||
an async callback.
|
||||
|
||||
Note that these measurements are not available until after the rendering has
|
||||
been completed.
|
||||
|
||||
**measureLayout**(relativeToNativeNode: DOMNode, onSuccess: (x, y, width, height) => void)
|
||||
|
||||
Like `measure`, but measures the view relative to another view, specified as
|
||||
`relativeToNativeNode`. This means that the returned `x`, `y` are relative to
|
||||
the origin `x`, `y` of the ancestor view.
|
||||
|
||||
**setNativeProps**(nativeProps: Object)
|
||||
|
||||
This function sends props straight to the underlying DOM node. See the [direct
|
||||
manipulation](../guides/direct-manipulation.md) guide for cases where
|
||||
`setNativeProps` should be used.
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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);
|
||||
```
|
||||
@@ -1,51 +0,0 @@
|
||||
# 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 }} />
|
||||
```
|
||||
@@ -1,45 +0,0 @@
|
||||
# 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'
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
@@ -1,78 +0,0 @@
|
||||
# StyleSheet
|
||||
|
||||
The `StyleSheet` abstraction converts predefined styles to (vendor-prefixed)
|
||||
CSS without requiring a compile-time step. Some styles cannot be resolved
|
||||
outside of the render loop and are applied as inline styles. Read more about
|
||||
[how to style your application](../guides/style.md).
|
||||
|
||||
## 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.
|
||||
|
||||
**render**: function
|
||||
|
||||
Returns a React `<style>` element 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',
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,35 +0,0 @@
|
||||
# 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]);
|
||||
```
|
||||
@@ -1,70 +0,0 @@
|
||||
# ActivityIndicator
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**animating**: bool = true
|
||||
|
||||
Whether to show the indicator or hide it.
|
||||
|
||||
**color**: string = '#1976D2'
|
||||
|
||||
The foreground color of the spinner.
|
||||
|
||||
**hidesWhenStopped**: bool = true
|
||||
|
||||
Whether the indicator should hide when not animating.
|
||||
|
||||
**size**: oneOf('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'
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,149 +0,0 @@
|
||||
# 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**: bool
|
||||
|
||||
When `false`, the view is hidden from screenreaders. Default: `true`.
|
||||
|
||||
**children**: any
|
||||
|
||||
Content to display over the image.
|
||||
|
||||
**defaultSource**: { uri: string }
|
||||
|
||||
An image to display as a placeholder while downloading the final image off the network.
|
||||
|
||||
**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**: oneOf('center', 'contain', 'cover', 'none', 'repeat', 'stretch') = 'stretch'
|
||||
|
||||
Determines how to resize the image when the frame doesn't match the raw image
|
||||
dimensions.
|
||||
|
||||
**source**: { uri: string }
|
||||
|
||||
`uri` is a string representing the resource identifier for the image, which
|
||||
could be an http address or a base64 encoded image.
|
||||
|
||||
**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} />
|
||||
```
|
||||
|
||||
## 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
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,34 +0,0 @@
|
||||
# ListView
|
||||
|
||||
TODO
|
||||
|
||||
## Props
|
||||
|
||||
[...ScrollView props](./ScrollView.md)
|
||||
|
||||
**children**: any
|
||||
|
||||
Content to display over the image.
|
||||
|
||||
**style**: style
|
||||
|
||||
+ ...[View#style](View.md)
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import React, { Component, PropTypes } from 'react'
|
||||
import { ListView } from 'react-native'
|
||||
|
||||
export default class ListViewExample extends Component {
|
||||
static propTypes = {}
|
||||
|
||||
static defaultProps = {}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ListView />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
# ProgressBar
|
||||
|
||||
Display an activity progress bar.
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
**color**: string = '#1976D2'
|
||||
|
||||
Color of the progress bar.
|
||||
|
||||
**indeterminate**: bool = true
|
||||
|
||||
Whether the progress bar will show indeterminate progress.
|
||||
|
||||
**progress**: number
|
||||
|
||||
The progress value (between 0 and 1).
|
||||
|
||||
(web) **trackColor**: string = 'transparent'
|
||||
|
||||
Color of the track bar.
|
||||
@@ -1,122 +0,0 @@
|
||||
# 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**: bool = false
|
||||
|
||||
When true, the scroll view's children are arranged horizontally in a row
|
||||
instead of vertically in a column.
|
||||
|
||||
**keyboardDismissMode**: oneOf('none', 'on-drag') = 'none'
|
||||
|
||||
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.
|
||||
|
||||
**refreshControl**: element
|
||||
|
||||
TODO
|
||||
|
||||
A [RefreshControl](../RefreshControl) component, used to provide
|
||||
pull-to-refresh functionality for the `ScrollView`.
|
||||
|
||||
**scrollEnabled**: bool = true
|
||||
|
||||
When false, the content does not scroll.
|
||||
|
||||
**scrollEventThrottle**: number = 0
|
||||
|
||||
This controls how often the scroll event will be fired while scrolling (in
|
||||
events per seconds). A higher number yields better accuracy for code that is
|
||||
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={60}
|
||||
style={styles.root}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
borderWidth: 1
|
||||
},
|
||||
container: {
|
||||
padding: 10
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,76 +0,0 @@
|
||||
# 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**: bool = false
|
||||
|
||||
If `true` the user won't be able to interact with the switch.
|
||||
|
||||
**onValueChange**: func
|
||||
|
||||
Invoked with the new value when the value changes.
|
||||
|
||||
**value**: bool = 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,147 +0,0 @@
|
||||
# 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
|
||||
|
||||
Defines the text available to assistive technologies upon interaction with the
|
||||
element. (This is implemented using `aria-label`.)
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles)
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
in a manner that is consistent with user expectations for similar views of that
|
||||
type. For example, marking a touchable view with an `accessibilityRole` of
|
||||
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
|
||||
**accessible**: bool = true
|
||||
|
||||
When `false`, the text is hidden from assistive technologies. (This is
|
||||
implemented using `aria-hidden`.)
|
||||
|
||||
**children**: any
|
||||
|
||||
Child content.
|
||||
|
||||
**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**: bool = true
|
||||
|
||||
Lets the user select the text.
|
||||
|
||||
**style**: style
|
||||
|
||||
+ ...[View#style](View.md)
|
||||
+ `color`
|
||||
+ `fontFamily`
|
||||
+ `fontSize`
|
||||
+ `fontStyle`
|
||||
+ `fontWeight`
|
||||
+ `letterSpacing`
|
||||
+ `lineHeight`
|
||||
+ `textAlign`‡
|
||||
+ `textAlignVertical`
|
||||
+ `textDecorationLine`
|
||||
+ `textOverflow`
|
||||
+ `textRendering`
|
||||
+ `textShadowColor`
|
||||
+ `textShadowOffset`‡
|
||||
+ `textShadowRadius`
|
||||
+ `textTransform`
|
||||
+ `unicodeBidi`
|
||||
+ `whiteSpace`
|
||||
+ `wordWrap`
|
||||
+ `writingDirection`‡
|
||||
|
||||
‡ This property can be suffixed with `$noI18n` to prevent automatic
|
||||
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
|
||||
|
||||
**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' }
|
||||
})
|
||||
```
|
||||
@@ -1,203 +0,0 @@
|
||||
# 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:
|
||||
`autoCapitalize`,
|
||||
`autoCorrect`,
|
||||
`onEndEditing`,
|
||||
`onSubmitEditing`,
|
||||
`clearButtonMode` (ios),
|
||||
`enablesReturnKeyAutomatically` (ios),
|
||||
`returnKeyType` (ios),
|
||||
`selectionState` (ios),
|
||||
`underlineColorAndroid` (android)
|
||||
|
||||
## Props
|
||||
|
||||
[...View props](./View.md)
|
||||
|
||||
(web) **autoComplete**: bool = false
|
||||
|
||||
Indicates whether the value of the control can be automatically completed by the browser.
|
||||
|
||||
**autoFocus**: bool = false
|
||||
|
||||
If true, focuses the input on `componentDidMount`. Only the first form element
|
||||
in a document with `autofocus` is focused.
|
||||
|
||||
**clearTextOnFocus**: bool = 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**: bool = true
|
||||
|
||||
If `false`, text is not editable (i.e., read-only).
|
||||
|
||||
**keyboardType**: oneOf('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.
|
||||
|
||||
(web) **maxNumberOfLines**: number = numberOfLines
|
||||
|
||||
Limits the maximum number of lines for a multiline `TextInput`.
|
||||
|
||||
(Requires `multiline` to be `true`.)
|
||||
|
||||
**multiline**: bool = false
|
||||
|
||||
If true, the text input can be multiple lines.
|
||||
|
||||
**numberOfLines**: number = 2
|
||||
|
||||
Sets the initial 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.
|
||||
|
||||
(web) **onSelectionChange**: function
|
||||
|
||||
Callback that is called when the text input's selection changes. The following
|
||||
object is passed as an argument to the callback handler.
|
||||
|
||||
```js
|
||||
{
|
||||
selectionDirection,
|
||||
selectionEnd,
|
||||
selectionStart,
|
||||
nativeEvent
|
||||
}
|
||||
```
|
||||
|
||||
**placeholder**: string
|
||||
|
||||
The string that will be rendered in an empty `TextInput` before text has been
|
||||
entered.
|
||||
|
||||
**placeholderTextColor**: string
|
||||
|
||||
The text color of the placeholder string.
|
||||
|
||||
**secureTextEntry**: bool = false
|
||||
|
||||
If true, the text input obscures the text entered so that sensitive text like
|
||||
passwords stay secure.
|
||||
|
||||
(Not available when `multiline` is `true`.)
|
||||
|
||||
**selectTextOnFocus**: bool = false
|
||||
|
||||
If `true`, all text will automatically be selected on focus.
|
||||
|
||||
**style**: style
|
||||
|
||||
+ ...[Text#style](./Text.md)
|
||||
+ `outline`
|
||||
|
||||
**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.
|
||||
|
||||
## 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'
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,73 +0,0 @@
|
||||
# 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)
|
||||
|
||||
**accessibilityLabel**: string
|
||||
|
||||
Overrides the text that's read by the screen reader when the user interacts
|
||||
with the element.
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles) = 'button'
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
|
||||
**accessible**: bool = true
|
||||
|
||||
When `false`, the view is hidden from screenreaders.
|
||||
|
||||
**children**: View
|
||||
|
||||
**delayLongPress**: number
|
||||
|
||||
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**: bool
|
||||
|
||||
If true, disable all interactions for this component.
|
||||
|
||||
**hitSlop**: `{top: number, left: number, bottom: number, right: number}`
|
||||
|
||||
This defines how far your touch can start away from the button. This is added
|
||||
to `pressRetentionOffset` when moving off of the button. **NOTE**: The touch
|
||||
area never extends past the parent view bounds and the z-index of sibling views
|
||||
always takes precedence if a touch hits two overlapping views.
|
||||
|
||||
**onLayout**: function
|
||||
|
||||
Invoked on mount and layout changes with `{ nativeEvent: { layout: { x, y, width,
|
||||
height } } }`, where `x` and `y` are the offsets from the parent node.
|
||||
|
||||
**onLongPress**: function
|
||||
|
||||
**onPress**: function
|
||||
|
||||
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.
|
||||
@@ -1,231 +0,0 @@
|
||||
# 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:
|
||||
`onAccessibilityTap`,
|
||||
`hitSlop`,
|
||||
`onMagicTap`
|
||||
|
||||
## Props
|
||||
|
||||
NOTE: `View` will transfer all other props to the rendered HTML element.
|
||||
|
||||
**accessibilityLabel**: string
|
||||
|
||||
Defines the text available to assistive technologies upon interaction with the
|
||||
element. (This is implemented using `aria-label`.)
|
||||
|
||||
**accessibilityLiveRegion**: oneOf('assertive', 'off', 'polite') = 'off'
|
||||
|
||||
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).)
|
||||
|
||||
(web) **accessibilityRole**: oneOf(roles)
|
||||
|
||||
Allows assistive technologies to present and support interaction with the view
|
||||
in a manner that is consistent with user expectations for similar views of that
|
||||
type. For example, marking a touchable view with an `accessibilityRole` of
|
||||
`button`. (This is implemented using [ARIA roles](http://www.w3.org/TR/wai-aria/roles#role_definitions)).
|
||||
|
||||
Note: Avoid changing `accessibilityRole` values over time or after user
|
||||
actions. Generally, accessibility APIs do not provide a means of notifying
|
||||
assistive technologies of a `role` value change.
|
||||
|
||||
**accessible**: bool = true
|
||||
|
||||
When `false`, the view is hidden from assistive technologies. (This is
|
||||
implemented using `aria-hidden`.)
|
||||
|
||||
**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
|
||||
|
||||
**onMoveShouldSetResponderCapture**: function
|
||||
|
||||
**onResponderGrant**: function
|
||||
|
||||
For most touch interactions, you'll simply want to wrap your component in
|
||||
`TouchableHighlight` or `TouchableOpacity`.
|
||||
|
||||
**onResponderMove**: function
|
||||
|
||||
**onResponderReject**: function
|
||||
|
||||
**onResponderRelease**: function
|
||||
|
||||
**onResponderTerminate**: function
|
||||
|
||||
**onResponderTerminationRequest**: function
|
||||
|
||||
**onStartShouldSetResponder**: function
|
||||
|
||||
**onStartShouldSetResponderCapture**: function
|
||||
|
||||
**pointerEvents**: oneOf('auto', 'box-only', 'box-none', 'none') = 'auto'
|
||||
|
||||
Configure the `pointerEvents` of the view. 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`
|
||||
+ `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`
|
||||
+ `cursor`
|
||||
+ `flex` (number)
|
||||
+ `flexBasis`
|
||||
+ `flexDirection`
|
||||
+ `flexGrow`
|
||||
+ `flexShrink`
|
||||
+ `flexWrap`
|
||||
+ `height`
|
||||
+ `justifyContent`
|
||||
+ `left`‡
|
||||
+ `margin` (single value)
|
||||
+ `marginBottom`
|
||||
+ `marginHorizontal`
|
||||
+ `marginLeft`‡
|
||||
+ `marginRight`‡
|
||||
+ `marginTop`
|
||||
+ `marginVertical`
|
||||
+ `maxHeight`
|
||||
+ `maxWidth`
|
||||
+ `minHeight`
|
||||
+ `minWidth`
|
||||
+ `opacity`
|
||||
+ `order`
|
||||
+ `overflow`
|
||||
+ `overflowX`
|
||||
+ `overflowY`
|
||||
+ `padding` (single value)
|
||||
+ `paddingBottom`
|
||||
+ `paddingHorizontal`
|
||||
+ `paddingLeft`‡
|
||||
+ `paddingRight`‡
|
||||
+ `paddingTop`
|
||||
+ `paddingVertical`
|
||||
+ `position`
|
||||
+ `right`‡
|
||||
+ `top`
|
||||
+ `transform`
|
||||
+ `userSelect`
|
||||
+ `visibility`
|
||||
+ `width`
|
||||
+ `zIndex`
|
||||
|
||||
‡ This property can be suffixed with `$noI18n` to prevent automatic
|
||||
bidi-flipping in RTL mode. This is only supported if `Platform.OS === 'web'`.
|
||||
|
||||
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
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
# Accessibility
|
||||
|
||||
On the Web, assistive technologies derive useful information about the
|
||||
structure, purpose, and interactivity of apps from their [HTML
|
||||
elements][html-accessibility-url], attributes, and [ARIA in
|
||||
HTML][aria-in-html-url].
|
||||
|
||||
The most common and best supported accessibility features of the Web are
|
||||
exposed as the props: `accessible`, `accessibilityLabel`,
|
||||
`accessibilityLiveRegion`, and `accessibilityRole`.
|
||||
|
||||
React Native for Web does not provide a way to directly control the type of the
|
||||
rendered HTML element. The `accessibilityRole` prop is used to infer an
|
||||
[analogous HTML element][html-aria-url] to use in addition to the resulting
|
||||
ARIA `role`, where possible. While this may contradict some ARIA
|
||||
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' />`.
|
||||
|
||||
Other ARIA properties should be set via [direct
|
||||
manipulation](./direct-manipulation.md).
|
||||
|
||||
[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/
|
||||
@@ -1,23 +0,0 @@
|
||||
# Known issues
|
||||
|
||||
## Missing modules and Views
|
||||
|
||||
This is an initial release of React Native for Web, therefore, not all of the
|
||||
views present on iOS/Android are released on Web. We are very much interested in
|
||||
the community's feedback on the next set of modules and views.
|
||||
|
||||
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.
|
||||
@@ -1,73 +0,0 @@
|
||||
# React Native
|
||||
|
||||
Use a module loader that supports package aliases (e.g., webpack), and alias
|
||||
`react-native` to `react-native-web`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Image assets
|
||||
|
||||
In order to require image assets (e.g. `require('assets/myimage.png')`), add
|
||||
the `url-loader` to the webpack config:
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
module: {
|
||||
loaders: {
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: { name: '[name].[hash:16].[ext]' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Web-specific code
|
||||
|
||||
Minor platform differences can use the `Platform` module.
|
||||
|
||||
```js
|
||||
import { AppRegistry, Platform } from 'react-native'
|
||||
|
||||
AppRegistry.registerComponent('MyApp', () => MyApp)
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
AppRegistry.runApplication('MyApp', {
|
||||
rootTag: document.getElementById('react-root')
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
More substantial Web-specific implementation code should be written in files
|
||||
with the extension `.web.js`, which webpack will automatically resolve.
|
||||
|
||||
## Optimizations
|
||||
|
||||
Production builds can benefit from dead-code elimination by defining the
|
||||
following variables:
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,74 +0,0 @@
|
||||
# Client and Server rendering
|
||||
|
||||
It's recommended that you use a module loader that supports package aliases
|
||||
(e.g., webpack), and alias `react-native` to `react-native-web`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...other configuration
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `react-native-web` package also includes a `core` module that exports only
|
||||
`ReactNative`, `Image`, `StyleSheet`, `Text`, `TextInput`, and `View`.
|
||||
|
||||
```js
|
||||
// webpack.config.js
|
||||
|
||||
module.exports = {
|
||||
// ...other configuration
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': 'react-native-web/core'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Client-side rendering
|
||||
|
||||
Rendering without using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import ReactNative from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppHeaderContainer = (props) => { /* ... */ }
|
||||
|
||||
// DOM render
|
||||
ReactNative.render(<AppHeaderContainer />, document.getElementById('react-app-header'))
|
||||
|
||||
// Server render
|
||||
ReactNative.renderToString(<AppHeaderContainer />)
|
||||
ReactNative.renderToStaticMarkup(<AppHeaderContainer />)
|
||||
```
|
||||
|
||||
Rendering using the `AppRegistry`:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import ReactNative, { AppRegistry } from 'react-native'
|
||||
|
||||
// component that renders the app
|
||||
const AppContainer = (props) => { /* ... */ }
|
||||
|
||||
// register the app
|
||||
AppRegistry.registerComponent('App', () => AppContainer)
|
||||
|
||||
// DOM render
|
||||
AppRegistry.runApplication('App', {
|
||||
initialProps: {},
|
||||
rootTag: document.getElementById('react-app')
|
||||
})
|
||||
|
||||
// prerender the app
|
||||
const { html, styleElement } = AppRegistry.prerenderApplication('App', { initialProps })
|
||||
```
|
||||
@@ -1,152 +0,0 @@
|
||||
# Style
|
||||
|
||||
React Native for Web relies on JavaScript to define styles for your
|
||||
application. This allows you to avoid issues arising from the [7 deadly sins of
|
||||
CSS](https://speakerdeck.com/vjeux/react-css-in-js):
|
||||
|
||||
1. Global namespace
|
||||
2. Dependency hell
|
||||
3. No dead code elimination
|
||||
4. No code minification
|
||||
5. No sharing of constants
|
||||
6. Non-deterministic resolution
|
||||
7. Lack of isolation
|
||||
|
||||
## Defining styles
|
||||
|
||||
Styles should be defined outside of the component:
|
||||
|
||||
```js
|
||||
class Example extends React.Component {}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
heading: {
|
||||
color: 'gray',
|
||||
fontSize: '2rem'
|
||||
},
|
||||
text: {
|
||||
color: 'gray',
|
||||
fontSize: '1.25rem'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Using `StyleSheet.create` is optional but provides some key advantages: styles
|
||||
are immutable in development, certain declarations are automatically converted
|
||||
to CSS rather than applied as inline styles, and styles are only created once
|
||||
for the application and not on every render.
|
||||
|
||||
The attribute names and values are a subset of CSS. See the `style`
|
||||
documentation of individual components.
|
||||
|
||||
## Using styles
|
||||
|
||||
All the React Native components accept a `style` attribute.
|
||||
|
||||
```js
|
||||
<Text style={styles.text} />
|
||||
<View style={styles.view} />
|
||||
```
|
||||
|
||||
A common pattern is to conditionally add style based on a condition:
|
||||
|
||||
```js
|
||||
// either
|
||||
<View style={[
|
||||
styles.base,
|
||||
this.state.active && styles.active
|
||||
]} />
|
||||
|
||||
// or
|
||||
<View style={{
|
||||
...styles.base,
|
||||
...(this.state.active && styles.active)
|
||||
}} />
|
||||
```
|
||||
|
||||
## Composing styles
|
||||
|
||||
In order to let a call site customize the style of your component children, you
|
||||
can pass styles around. Use `View.propTypes.style` and `Text.propTypes.style` in
|
||||
order to make sure only valid styles are being passed.
|
||||
|
||||
```js
|
||||
class List extends React.Component {
|
||||
static propTypes = {
|
||||
style: View.propTypes.style,
|
||||
elementStyle: View.propTypes.style,
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={this.props.style}>
|
||||
{elements.map((element) =>
|
||||
<View style={[ styles.element, this.props.elementStyle ]} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In another file:
|
||||
|
||||
```js
|
||||
<List style={styles.list} elementStyle={styles.listElement} />
|
||||
```
|
||||
|
||||
You also have much greater control over how styles are composed when compared
|
||||
to using class names. For example, you may choose to accept a limited subset
|
||||
of style props in the component's API, and control when they are applied:
|
||||
|
||||
```js
|
||||
class List extends React.Component {
|
||||
static propTypes = {
|
||||
children: React.PropTypes.any,
|
||||
// limit which styles are accepted
|
||||
style: React.PropTypes.shape({
|
||||
borderColor: View.propTypes.borderColor,
|
||||
borderWidth: View.propTypes.borderWidth
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
children={children}
|
||||
style={[
|
||||
this.props.style,
|
||||
// override border-color when scrolling
|
||||
isScrolling && { borderColor: 'transparent' }
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Media Queries
|
||||
|
||||
`StyleSheet.create` is a way of defining the styles your application requires;
|
||||
it does not concern itself with _where_ or _when_ those styles are applied to
|
||||
elements.
|
||||
|
||||
There are various React libraries wrapping JavaScript Media Query API's, e.g.,
|
||||
[react-media-queries](https://github.com/bloodyowl/react-media-queries),
|
||||
[media-query-fascade](https://github.com/tanem/media-query-facade), or
|
||||
[react-responsive](https://github.com/contra/react-responsive). This has the
|
||||
benefit of co-locating breakpoint-specific DOM and style changes.
|
||||
|
||||
## Pseudo-classes and pseudo-elements
|
||||
|
||||
Pseudo-classes like `:hover` and `:focus` can be implemented with events (e.g.
|
||||
`onFocus`). Pseudo-elements are not supported; elements should be used instead.
|
||||
|
||||
### Reset
|
||||
|
||||
You **do not** need to include a CSS reset or
|
||||
[normalize.css](https://necolas.github.io/normalize.css/).
|
||||
|
||||
React Native for Web includes a very small CSS reset taken from normalize.css.
|
||||
It removes unwanted User Agent styles from (pseudo-)elements beyond the reach
|
||||
of React (e.g., `html`, `body`) or inline styles (e.g., `::-moz-focus-inner`).
|
||||
BIN
docs/static/components.png
vendored
BIN
docs/static/components.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
BIN
docs/static/styling-strategy.png
vendored
BIN
docs/static/styling-strategy.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
@@ -1,12 +0,0 @@
|
||||
import { configure, addDecorator } from '@kadira/storybook'
|
||||
import centered from './decorator-centered'
|
||||
|
||||
const context = require.context('../', true, /Example\.js$/)
|
||||
|
||||
addDecorator(centered)
|
||||
|
||||
function loadStories() {
|
||||
context.keys().forEach(context)
|
||||
}
|
||||
|
||||
configure(loadStories, module)
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'center',
|
||||
height: '100vh',
|
||||
justifyContent: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
export default function (renderStory) {
|
||||
return (
|
||||
<View style={[ StyleSheet.absoluteFill, styles.root ]}>
|
||||
{renderStory()}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
query: { cacheDirectory: true }
|
||||
},
|
||||
{
|
||||
test: /\.(gif|jpe?g|png|svg)$/,
|
||||
loader: 'url-loader',
|
||||
query: { name: '[name].[ext]' }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
|
||||
}),
|
||||
// https://github.com/animatedjs/animated/issues/40
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
/es6-set/,
|
||||
path.join(__dirname, '../../src/modules/polyfills/Set.js')
|
||||
),
|
||||
new webpack.optimize.OccurenceOrderPlugin()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-native': path.join(__dirname, '../../src')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import { I18nManager, StyleSheet, TouchableHighlight, Text, View } from 'react-native'
|
||||
import React, { Component } from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
class RTLExample extends Component {
|
||||
componentWillUnmount() {
|
||||
I18nManager.setPreferredLanguageRTL(false)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text accessibilityRole='heading' style={styles.welcome}>
|
||||
LTR/RTL layout example!
|
||||
</Text>
|
||||
<Text style={styles.text}>
|
||||
This is sample text. The writing direction can be changed by pressing the button below.
|
||||
</Text>
|
||||
<Text style={[ styles.text, styles.ltrText ]}>
|
||||
This is text that will always display LTR.
|
||||
</Text>
|
||||
<Text style={[ styles.text, styles.rtlText ]}>
|
||||
This is text that will always display RTL.
|
||||
</Text>
|
||||
<TouchableHighlight
|
||||
onPress={this._handleToggle}
|
||||
style={styles.toggle}
|
||||
underlayColor='rgba(0,0,0,0.25)'
|
||||
>
|
||||
<Text>Toggle LTR/RTL</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
_handleToggle = () => {
|
||||
this._isRTL = !this._isRTL
|
||||
I18nManager.setPreferredLanguageRTL(this._isRTL)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#F5FCFF',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
padding: 10
|
||||
},
|
||||
welcome: {
|
||||
fontSize: 28,
|
||||
marginVertical: 10
|
||||
},
|
||||
text: {
|
||||
color: '#333333',
|
||||
fontSize: 18,
|
||||
marginBottom: 5
|
||||
},
|
||||
ltrText: {
|
||||
textAlign$noI18n: 'left',
|
||||
writingDirection$noI18n: 'ltr'
|
||||
},
|
||||
rtlText: {
|
||||
textAlign$noI18n: 'right',
|
||||
writingDirection$noI18n: 'rtl'
|
||||
},
|
||||
toggle: {
|
||||
alignSelf: 'center',
|
||||
borderColor: 'black',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
marginTop: 10,
|
||||
padding: 10
|
||||
}
|
||||
})
|
||||
|
||||
storiesOf('api: I18nManager', module)
|
||||
.add('RTL layout', () => (
|
||||
<RTLExample />
|
||||
))
|
||||
@@ -1,117 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
var React = require('react');
|
||||
var ReactNative = require('react-native');
|
||||
var {
|
||||
PanResponder,
|
||||
StyleSheet,
|
||||
View
|
||||
} = ReactNative;
|
||||
|
||||
var CIRCLE_SIZE = 80;
|
||||
|
||||
var PanResponderExample = React.createClass({
|
||||
_panResponder: {},
|
||||
_previousLeft: 0,
|
||||
_previousTop: 0,
|
||||
_circleStyles: {},
|
||||
circle: (null : ?{ setNativeProps(props: Object): void }),
|
||||
|
||||
componentWillMount: function() {
|
||||
this._panResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder,
|
||||
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
|
||||
onPanResponderGrant: this._handlePanResponderGrant,
|
||||
onPanResponderMove: this._handlePanResponderMove,
|
||||
onPanResponderRelease: this._handlePanResponderEnd,
|
||||
onPanResponderTerminate: this._handlePanResponderEnd,
|
||||
});
|
||||
this._previousLeft = 20;
|
||||
this._previousTop = 84;
|
||||
this._circleStyles = {
|
||||
style: {
|
||||
left: this._previousLeft,
|
||||
top: this._previousTop,
|
||||
backgroundColor: 'green',
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<View
|
||||
style={styles.container}>
|
||||
<View
|
||||
ref={(circle) => {
|
||||
this.circle = circle;
|
||||
}}
|
||||
style={styles.circle}
|
||||
{...this._panResponder.panHandlers}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_highlight: function() {
|
||||
this._circleStyles.style.backgroundColor = 'blue';
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
_unHighlight: function() {
|
||||
this._circleStyles.style.backgroundColor = 'green';
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
|
||||
_updateNativeStyles: function() {
|
||||
this.circle && this.circle.setNativeProps(this._circleStyles);
|
||||
},
|
||||
|
||||
_handleStartShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||
// Should we become active when the user presses down on the circle?
|
||||
return true;
|
||||
},
|
||||
|
||||
_handleMoveShouldSetPanResponder: function(e: Object, gestureState: Object): boolean {
|
||||
// Should we become active when the user moves a touch over the circle?
|
||||
return false;
|
||||
},
|
||||
|
||||
_handlePanResponderGrant: function(e: Object, gestureState: Object) {
|
||||
this._highlight();
|
||||
},
|
||||
_handlePanResponderMove: function(e: Object, gestureState: Object) {
|
||||
this._circleStyles.style.left = this._previousLeft + gestureState.dx;
|
||||
this._circleStyles.style.top = this._previousTop + gestureState.dy;
|
||||
this._updateNativeStyles();
|
||||
},
|
||||
_handlePanResponderEnd: function(e: Object, gestureState: Object) {
|
||||
this._unHighlight();
|
||||
this._previousLeft += gestureState.dx;
|
||||
this._previousTop += gestureState.dy;
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
circle: {
|
||||
width: CIRCLE_SIZE,
|
||||
height: CIRCLE_SIZE,
|
||||
borderRadius: CIRCLE_SIZE / 2,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingTop: 64,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
storiesOf('api: PanResponder', module)
|
||||
.add('example', () => <PanResponderExample />)
|
||||
@@ -1,162 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ActivityIndicator, StyleSheet, View } from 'react-native'
|
||||
import TimerMixin from 'react-timer-mixin';
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
const ToggleAnimatingActivityIndicator = React.createClass({
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
animating: true,
|
||||
};
|
||||
},
|
||||
|
||||
setToggleTimeout() {
|
||||
this.setTimeout(() => {
|
||||
this.setState({animating: !this.state.animating});
|
||||
this.setToggleTimeout();
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this.setToggleTimeout();
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
animating={this.state.animating}
|
||||
style={[styles.centering, {height: 80}]}
|
||||
size="large"
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Default',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom colors',
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<ActivityIndicator color="#0000ff" />
|
||||
<ActivityIndicator color="#aa00aa" />
|
||||
<ActivityIndicator color="#aa3300" />
|
||||
<ActivityIndicator color="#00aa00" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Large',
|
||||
render() {
|
||||
return (
|
||||
<ActivityIndicator
|
||||
style={[styles.centering, styles.gray]}
|
||||
color="white"
|
||||
size="large"
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Large, custom colors',
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#0000ff"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#aa00aa"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#aa3300"
|
||||
/>
|
||||
<ActivityIndicator
|
||||
size="large"
|
||||
color="#00aa00"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Start/stop',
|
||||
render() {
|
||||
return <ToggleAnimatingActivityIndicator />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Custom size',
|
||||
render() {
|
||||
return (
|
||||
<View style={[styles.horizontal, styles.centering]}>
|
||||
<ActivityIndicator size="40" />
|
||||
<ActivityIndicator
|
||||
style={{ marginLeft: 20, transform: [ {scale: 1.5} ] }}
|
||||
size="large"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
centering: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 8,
|
||||
},
|
||||
gray: {
|
||||
backgroundColor: '#cccccc',
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
padding: 8,
|
||||
},
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: ActivityIndicator', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,657 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action, addDecorator } from '@kadira/storybook';
|
||||
import { ActivityIndicator, Image, Platform, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
|
||||
|
||||
//var ImageCapInsetsExample = require('./ImageCapInsetsExample');
|
||||
//const IMAGE_PREFETCH_URL = 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now();
|
||||
//var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL);
|
||||
|
||||
/*
|
||||
var NetworkImageCallbackExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
events: [],
|
||||
startLoadPrefetched: false,
|
||||
mountTime: new Date(),
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({mountTime: new Date()});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var { mountTime } = this.state;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={() => this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)}
|
||||
onLoad={() => this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms)`)}
|
||||
onLoadEnd={() => {
|
||||
this._loadEventFired(`✔ onLoadEnd (+${new Date() - mountTime}ms)`);
|
||||
this.setState({startLoadPrefetched: true}, () => {
|
||||
prefetchTask.then(() => {
|
||||
this._loadEventFired(`✔ Prefetch OK (+${new Date() - mountTime}ms)`);
|
||||
}, error => {
|
||||
this._loadEventFired(`✘ Prefetch failed (+${new Date() - mountTime}ms)`);
|
||||
});
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{this.state.startLoadPrefetched ?
|
||||
<Image
|
||||
source={this.props.prefetchedSource}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={() => this._loadEventFired(`✔ (prefetched) onLoadStart (+${new Date() - mountTime}ms)`)}
|
||||
onLoad={() => this._loadEventFired(`✔ (prefetched) onLoad (+${new Date() - mountTime}ms)`)}
|
||||
onLoadEnd={() => this._loadEventFired(`✔ (prefetched) onLoadEnd (+${new Date() - mountTime}ms)`)}
|
||||
/>
|
||||
: null}
|
||||
<Text style={{marginTop: 20}}>
|
||||
{this.state.events.join('\n')}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
|
||||
_loadEventFired(event) {
|
||||
this.setState((state) => {
|
||||
return state.events = [...state.events, event];
|
||||
});
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
var NetworkImageExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
error: false,
|
||||
loading: false,
|
||||
progress: 0
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
var loader = this.state.loading ?
|
||||
<View style={styles.progress}>
|
||||
<Text>{this.state.progress}%</Text>
|
||||
<ActivityIndicator style={{marginLeft:5}} />
|
||||
</View> : null;
|
||||
return this.state.error ?
|
||||
<Text>{this.state.error}</Text> :
|
||||
<Image
|
||||
source={this.props.source}
|
||||
style={[styles.base, {overflow: 'visible'}]}
|
||||
onLoadStart={(e) => this.setState({loading: true})}
|
||||
onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})}
|
||||
onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})}
|
||||
onLoad={() => this.setState({loading: false, error: false})}>
|
||||
{loader}
|
||||
</Image>;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
var ImageSizeExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
Image.getSize(this.props.source.uri, (width, height) => {
|
||||
this.setState({width, height});
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<Image
|
||||
style={{
|
||||
width: 60,
|
||||
height: 60,
|
||||
backgroundColor: 'transparent',
|
||||
marginRight: 10,
|
||||
}}
|
||||
source={this.props.source} />
|
||||
<Text>
|
||||
Actual dimensions:{'\n'}
|
||||
Width: {this.state.width}, Height: {this.state.height}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
*/
|
||||
/*
|
||||
var MultipleSourcesExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
width: 30,
|
||||
height: 30,
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={{flexDirection: 'row', justifyContent: 'space-between'}}>
|
||||
<Text
|
||||
style={styles.touchableText}
|
||||
onPress={this.decreaseImageSize} >
|
||||
Decrease image size
|
||||
</Text>
|
||||
<Text
|
||||
style={styles.touchableText}
|
||||
onPress={this.increaseImageSize} >
|
||||
Increase image size
|
||||
</Text>
|
||||
</View>
|
||||
<Text>Container image size: {this.state.width}x{this.state.height} </Text>
|
||||
<View
|
||||
style={[styles.imageContainer, {height: this.state.height, width: this.state.width}]} >
|
||||
<Image
|
||||
style={{flex: 1}}
|
||||
source={[
|
||||
{uri: 'http://facebook.github.io/react/img/logo_small.png', width: 38, height: 38},
|
||||
{uri: 'http://facebook.github.io/react/img/logo_small_2x.png', width: 76, height: 76},
|
||||
{uri: 'http://facebook.github.io/react/img/logo_og.png', width: 400, height: 400}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
increaseImageSize: function() {
|
||||
if (this.state.width >= 100) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
width: this.state.width + 10,
|
||||
height: this.state.height + 10,
|
||||
});
|
||||
},
|
||||
decreaseImageSize: function() {
|
||||
if (this.state.width <= 10) {
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
width: this.state.width - 10,
|
||||
height: this.state.height - 10,
|
||||
});
|
||||
},
|
||||
});
|
||||
*/
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Plain Network Image',
|
||||
description: 'If the `source` prop `uri` property is prefixed with ' +
|
||||
'"http", then it will be downloaded from the network.',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
source={{uri: 'http://facebook.github.io/react/img/logo_og.png'}}
|
||||
style={styles.base}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Plain Static Image',
|
||||
description: 'Static assets should be placed in the source code tree, and ' +
|
||||
'required in the same way as JavaScript modules.',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image source={require('./uie_thumb_normal@2x.png')} style={styles.icon} />
|
||||
<Image source={require('./uie_thumb_selected@2x.png')} style={styles.icon} />
|
||||
{/*<Image source={require('./uie_comment_normal.png')} style={styles.icon} />*/}
|
||||
{/*<Image source={require('./uie_comment_highlighted.png')} style={styles.icon} />*/}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
/*
|
||||
{
|
||||
title: 'Image Loading Events',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageCallbackExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now()}}
|
||||
prefetchedSource={{uri: IMAGE_PREFETCH_URL}}/>
|
||||
);
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
title: 'Error Handler',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://TYPO_ERROR_facebook.github.io/react/img/logo_og.png'}} />
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Image Download Progress',
|
||||
render: function() {
|
||||
return (
|
||||
<NetworkImageExample source={{uri: 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1'}}/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'defaultSource',
|
||||
description: 'Show a placeholder image when a network image is loading',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
defaultSource={require('./bunny.png')}
|
||||
source={{uri: 'http://facebook.github.io/origami/public/images/birds.jpg'}}
|
||||
style={styles.base}
|
||||
/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Border Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[
|
||||
styles.base,
|
||||
styles.background,
|
||||
{borderWidth: 3, borderColor: '#f099f0'}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Border Width',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[
|
||||
styles.base,
|
||||
styles.background,
|
||||
{borderWidth: 5, borderColor: '#f099f0'}
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Border Radius',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
style={[styles.base, {borderRadius: 5}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 19}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Background Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image source={smallImage} style={styles.base} />
|
||||
<Image
|
||||
style={[
|
||||
styles.base,
|
||||
styles.leftMargin,
|
||||
{backgroundColor: 'rgba(0, 0, 100, 0.25)'}
|
||||
]}
|
||||
source={smallImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {backgroundColor: 'red'}]}
|
||||
source={smallImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {backgroundColor: 'black'}]}
|
||||
source={smallImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Opacity',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
style={[styles.base, {opacity: 1}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.8}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.6}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.4}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0.2}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
<Image
|
||||
style={[styles.base, styles.leftMargin, {opacity: 0}]}
|
||||
source={fullImage}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Nesting',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={{width: 60, height: 60, backgroundColor: 'transparent'}}
|
||||
source={fullImage}>
|
||||
<Text style={styles.nestedText}>
|
||||
React
|
||||
</Text>
|
||||
</Image>
|
||||
);
|
||||
},
|
||||
},
|
||||
/*
|
||||
{
|
||||
title: 'Tint Color',
|
||||
description: 'The `tintColor` style prop changes all the non-alpha ' +
|
||||
'pixels to the tint color.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal@2x.png')}
|
||||
style={[styles.icon, {borderRadius: 5, tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal@2x.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal@2x.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={require('./uie_thumb_normal@2x.png')}
|
||||
style={[styles.icon, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.sectionText}>
|
||||
It also works with downloaded images:
|
||||
</Text>
|
||||
<View style={styles.horizontal}>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, {borderRadius: 5, tintColor: '#5ac8fa' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#4cd964' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#ff2d55' }]}
|
||||
/>
|
||||
<Image
|
||||
source={smallImage}
|
||||
style={[styles.base, styles.leftMargin, {borderRadius: 5, tintColor: '#8e8e93' }]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
*/
|
||||
{
|
||||
title: 'Resize Mode',
|
||||
description: 'The `resizeMode` style prop controls how the image is ' +
|
||||
'rendered within the frame.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
{[smallImage, fullImage].map((image, index) => {
|
||||
return (
|
||||
<View key={index}>
|
||||
<View style={styles.horizontal}>
|
||||
<View>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Contain
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.contain}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Cover
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.cover}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.horizontal}>
|
||||
<View>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Stretch
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={Image.resizeMode.stretch}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Repeat
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={'repeat'}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.leftMargin}>
|
||||
<Text style={[styles.resizeModeText]}>
|
||||
Center
|
||||
</Text>
|
||||
<Image
|
||||
style={styles.resizeMode}
|
||||
resizeMode={'center'}
|
||||
source={image}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Animated GIF',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={styles.gif}
|
||||
source={{uri: 'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
{
|
||||
title: 'Base64 image',
|
||||
render: function() {
|
||||
return (
|
||||
<Image
|
||||
style={styles.base64}
|
||||
source={{uri: base64Icon, scale: 3}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
/*
|
||||
{
|
||||
title: 'Cap Insets',
|
||||
description:
|
||||
'When the image is resized, the corners of the size specified ' +
|
||||
'by capInsets will stay a fixed size, but the center content and ' +
|
||||
'borders of the image will be stretched. This is useful for creating ' +
|
||||
'resizable rounded buttons, shadows, and other resizable assets.',
|
||||
render: function() {
|
||||
return <ImageCapInsetsExample />;
|
||||
},
|
||||
platform: 'ios',
|
||||
},
|
||||
*/
|
||||
/*
|
||||
{
|
||||
title: 'Image Size',
|
||||
render: function() {
|
||||
return <ImageSizeExample source={fullImage} />;
|
||||
},
|
||||
},
|
||||
*/
|
||||
/*
|
||||
{
|
||||
title: 'MultipleSourcesExample',
|
||||
description:
|
||||
'The `source` prop allows passing in an array of uris, so that native to choose which image ' +
|
||||
'to diplay based on the size of the of the target image',
|
||||
render: function() {
|
||||
return <MultipleSourcesExample />;
|
||||
},
|
||||
platform: 'android',
|
||||
},
|
||||
*/
|
||||
];
|
||||
|
||||
var fullImage = {uri: 'http://facebook.github.io/react/img/logo_og.png'};
|
||||
var smallImage = {uri: 'http://facebook.github.io/react/img/logo_small_2x.png'};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
base: {
|
||||
width: 38,
|
||||
height: 38,
|
||||
},
|
||||
progress: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
width: 100
|
||||
},
|
||||
leftMargin: {
|
||||
marginLeft: 10,
|
||||
},
|
||||
background: {
|
||||
backgroundColor: '#222222'
|
||||
},
|
||||
sectionText: {
|
||||
marginVertical: 6,
|
||||
},
|
||||
nestedText: {
|
||||
marginLeft: 12,
|
||||
marginTop: 20,
|
||||
backgroundColor: 'transparent',
|
||||
color: 'white'
|
||||
},
|
||||
resizeMode: {
|
||||
width: 90,
|
||||
height: 60,
|
||||
borderWidth: 0.5,
|
||||
borderColor: 'black'
|
||||
},
|
||||
resizeModeText: {
|
||||
fontSize: 11,
|
||||
marginBottom: 3,
|
||||
},
|
||||
icon: {
|
||||
width: 15,
|
||||
height: 15,
|
||||
},
|
||||
horizontal: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
gif: {
|
||||
flex: 1,
|
||||
height: 200,
|
||||
},
|
||||
base64: {
|
||||
flex: 1,
|
||||
height: 50,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
touchableText: {
|
||||
fontWeight: '500',
|
||||
color: 'blue',
|
||||
},
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Image', module)
|
||||
.addDecorator((renderStory) => <View>{renderStory()}</View>)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 850 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ListView } from 'react-native'
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { ProgressBar, StyleSheet, View } from 'react-native'
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import TimerMixin from 'react-timer-mixin';
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var ProgressBarExample = React.createClass({
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
progress: 0,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this.updateProgress();
|
||||
},
|
||||
|
||||
updateProgress() {
|
||||
var progress = this.state.progress + 0.01;
|
||||
this.setState({ progress });
|
||||
this.requestAnimationFrame(() => this.updateProgress());
|
||||
},
|
||||
|
||||
getProgress(offset) {
|
||||
var progress = this.state.progress + offset;
|
||||
return Math.sin(progress % Math.PI) % 1;
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ProgressBar style={styles.progressView} color="purple" progress={this.getProgress(0.2)} />
|
||||
<ProgressBar style={styles.progressView} color="red" progress={this.getProgress(0.4)} />
|
||||
<ProgressBar style={styles.progressView} color="orange" progress={this.getProgress(0.6)} />
|
||||
<ProgressBar style={styles.progressView} color="yellow" progress={this.getProgress(0.8)} />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const examples = [{
|
||||
title: 'progress',
|
||||
render() {
|
||||
return (
|
||||
<ProgressBarExample />
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'indeterminate',
|
||||
render() {
|
||||
return (
|
||||
<ProgressBar indeterminate style={styles.progressView} trackColor='#D1E3F6' />
|
||||
);
|
||||
}
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
minWidth: 200,
|
||||
marginTop: -20,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
progressView: {
|
||||
marginTop: 20,
|
||||
minWidth: 200
|
||||
}
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: ProgressBar', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,58 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
storiesOf('component: ScrollView', module)
|
||||
.add('vertical', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
onScroll={e => console.log('ScrollView.onScroll', e)}
|
||||
scrollEventThrottle={1} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
>
|
||||
{Array.from({ length: 50 }).map((item, i) => (
|
||||
<View key={i} style={styles.box}>
|
||||
<Text>{i}</Text>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
))
|
||||
.add('horizontal', () => (
|
||||
<View style={styles.scrollViewContainer}>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollViewContentContainerStyle}
|
||||
horizontal
|
||||
onScroll={e => console.log('ScrollView.onScroll', e)}
|
||||
scrollEventThrottle={1} // 1 event per second
|
||||
style={styles.scrollViewStyle}
|
||||
>
|
||||
{Array.from({ length: 50 }).map((item, i) => (
|
||||
<View key={i} style={[ styles.box, styles.horizontalBox ]}>
|
||||
<Text>{i}</Text>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
))
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
box: {
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
justifyContent: 'center',
|
||||
borderWidth: 1
|
||||
},
|
||||
scrollViewContainer: {
|
||||
height: '200px',
|
||||
width: 300
|
||||
},
|
||||
scrollViewStyle: {
|
||||
borderWidth: '1px'
|
||||
},
|
||||
scrollViewContentContainerStyle: {
|
||||
backgroundColor: '#eee',
|
||||
padding: '10px'
|
||||
}
|
||||
})
|
||||
@@ -1,190 +0,0 @@
|
||||
import { Platform, Switch, Text, View } from 'react-native'
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var BasicSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
trueSwitchIsOn: true,
|
||||
falseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({falseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.falseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({trueSwitchIsOn: value})}
|
||||
value={this.state.trueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DisabledSwitchExample = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
disabled={true}
|
||||
style={{marginBottom: 10}}
|
||||
value={true} />
|
||||
<Switch
|
||||
disabled={true}
|
||||
value={false} />
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var ColorSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
colorTrueSwitchIsOn: true,
|
||||
colorFalseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
activeThumbColor="#428bca"
|
||||
activeTrackColor="#A0C4E3"
|
||||
onValueChange={(value) => this.setState({colorFalseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.colorFalseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
activeThumbColor="#5CB85C"
|
||||
activeTrackColor="#ADDAAD"
|
||||
onValueChange={(value) => this.setState({colorTrueSwitchIsOn: value})}
|
||||
thumbColor="#EBA9A7"
|
||||
trackColor="#D9534F"
|
||||
value={this.state.colorTrueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var EventSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
eventSwitchIsOn: false,
|
||||
eventSwitchRegressionIsOn: true,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}>
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchIsOn} />
|
||||
<Text>{this.state.eventSwitchIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchRegressionIsOn} />
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({eventSwitchRegressionIsOn: value})}
|
||||
style={{marginBottom: 10}}
|
||||
value={this.state.eventSwitchRegressionIsOn} />
|
||||
<Text>{this.state.eventSwitchRegressionIsOn ? 'On' : 'Off'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var SizeSwitchExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
trueSwitchIsOn: true,
|
||||
falseSwitchIsOn: false,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({falseSwitchIsOn: value})}
|
||||
style={{marginBottom: 10, height: '3rem' }}
|
||||
value={this.state.falseSwitchIsOn}
|
||||
/>
|
||||
<Switch
|
||||
onValueChange={(value) => this.setState({trueSwitchIsOn: value})}
|
||||
style={{marginBottom: 10, width: 150 }}
|
||||
value={this.state.trueSwitchIsOn}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var examples = [
|
||||
{
|
||||
title: 'set to true or false',
|
||||
render(): ReactElement<any> { return <BasicSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'disabled',
|
||||
render(): ReactElement<any> { return <DisabledSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'change events',
|
||||
render(): ReactElement<any> { return <EventSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'custom colors',
|
||||
render(): ReactElement<any> { return <ColorSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'custom size',
|
||||
render(): ReactElement<any> { return <SizeSwitchExample />; }
|
||||
},
|
||||
{
|
||||
title: 'controlled component',
|
||||
render(): ReactElement<any> { return <Switch />; }
|
||||
}
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Switch', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,472 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { Image, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var Entity = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<Text style={{fontWeight: '500', color: '#527fe4'}}>
|
||||
{this.props.children}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var AttributeToggler = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {fontWeight: 'bold', fontSize: 15};
|
||||
},
|
||||
toggleWeight: function() {
|
||||
this.setState({
|
||||
fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold'
|
||||
});
|
||||
},
|
||||
increaseSize: function() {
|
||||
this.setState({
|
||||
fontSize: this.state.fontSize + 1
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize};
|
||||
return (
|
||||
<View>
|
||||
<Text style={curStyle}>
|
||||
Tap the controls below to change attributes.
|
||||
</Text>
|
||||
<Text>
|
||||
<Text>See how it will even work on <Text style={curStyle}>this nested text</Text></Text>
|
||||
</Text>
|
||||
<Text
|
||||
style={{backgroundColor: '#ffaaaa', marginTop: 5}}
|
||||
onPress={this.toggleWeight}>
|
||||
Toggle Weight
|
||||
</Text>
|
||||
<Text
|
||||
style={{backgroundColor: '#aaaaff', marginTop: 5}}
|
||||
onPress={this.increaseSize}>
|
||||
Increase Size
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Wrap',
|
||||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
The text should wrap if it goes on multiple lines. See, this is going to
|
||||
the next line.
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Padding',
|
||||
render: function() {
|
||||
return (
|
||||
<Text style={{padding: 10}}>
|
||||
This text is indented by 10px padding on all sides.
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font Family',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontFamily: 'Cochin'}}>
|
||||
Cochin
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Cochin', fontWeight: 'bold'}}>
|
||||
Cochin bold
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Helvetica'}}>
|
||||
Helvetica
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Helvetica', fontWeight: 'bold'}}>
|
||||
Helvetica bold
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Verdana'}}>
|
||||
Verdana
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Verdana', fontWeight: 'bold'}}>
|
||||
Verdana bold
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font Size',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontSize: 23}}>
|
||||
Size 23
|
||||
</Text>
|
||||
<Text style={{fontSize: 8}}>
|
||||
Size 8
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{color: 'red'}}>
|
||||
Red color
|
||||
</Text>
|
||||
<Text style={{color: 'blue'}}>
|
||||
Blue color
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font Weight',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontSize: 20, fontWeight: '100'}}>
|
||||
Move fast and be ultralight
|
||||
</Text>
|
||||
<Text style={{fontSize: 20, fontWeight: '200'}}>
|
||||
Move fast and be light
|
||||
</Text>
|
||||
<Text style={{fontSize: 20, fontWeight: 'normal'}}>
|
||||
Move fast and be normal
|
||||
</Text>
|
||||
<Text style={{fontSize: 20, fontWeight: 'bold'}}>
|
||||
Move fast and be bold
|
||||
</Text>
|
||||
<Text style={{fontSize: 20, fontWeight: '900'}}>
|
||||
Move fast and be ultrabold
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font Style',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontStyle: 'normal'}}>
|
||||
Normal text
|
||||
</Text>
|
||||
<Text style={{fontStyle: 'italic'}}>
|
||||
Italic text
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Text Decoration',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{textDecorationLine: 'underline', textDecorationStyle: 'solid'}}>
|
||||
Solid underline
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'underline', textDecorationStyle: 'double', textDecorationColor: '#ff0000'}}>
|
||||
Double underline with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'underline', textDecorationStyle: 'dashed', textDecorationColor: '#9CDC40'}}>
|
||||
Dashed underline with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'underline', textDecorationStyle: 'dotted', textDecorationColor: 'blue'}}>
|
||||
Dotted underline with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'none'}}>
|
||||
None textDecoration
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'solid'}}>
|
||||
Solid line-through
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'double', textDecorationColor: '#ff0000'}}>
|
||||
Double line-through with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'dashed', textDecorationColor: '#9CDC40'}}>
|
||||
Dashed line-through with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'line-through', textDecorationStyle: 'dotted', textDecorationColor: 'blue'}}>
|
||||
Dotted line-through with custom color
|
||||
</Text>
|
||||
<Text style={{textDecorationLine: 'underline line-through'}}>
|
||||
Both underline and line-through
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Nested',
|
||||
description: 'Nested text components will inherit the styles of their ' +
|
||||
'parents (only backgroundColor is inherited from non-Text parents). ' +
|
||||
'<Text> only supports other <Text> and raw text (strings) as children.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
(Normal text,
|
||||
<Text style={{fontWeight: 'bold'}}>
|
||||
(and bold
|
||||
<Text style={{fontSize: 11, color: '#527fe4'}}>
|
||||
(and tiny inherited bold blue)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
<Text style={{opacity:0.7}}>
|
||||
(opacity
|
||||
<Text>
|
||||
(is inherited
|
||||
<Text style={{opacity:0.7}}>
|
||||
(and accumulated
|
||||
<Text style={{backgroundColor:'#ffaaaa'}}>
|
||||
(and also applies to the background)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
)
|
||||
</Text>
|
||||
<Text style={{fontSize: 12}}>
|
||||
<Entity>Entity Name</Entity>
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Text Align',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
auto (default) - english LTR
|
||||
</Text>
|
||||
<Text style={{ writingDirection$noI18n: 'rtl' }}>
|
||||
أحب اللغة العربية auto (default) - arabic RTL
|
||||
</Text>
|
||||
<Text style={{textAlign: 'left'}}>
|
||||
left left left left left left left left left left left left left left left
|
||||
</Text>
|
||||
<Text style={{textAlign: 'center'}}>
|
||||
center center center center center center center center center center center
|
||||
</Text>
|
||||
<Text style={{textAlign: 'right'}}>
|
||||
right right right right right right right right right right right right right
|
||||
</Text>
|
||||
<Text style={{textAlign: 'justify'}}>
|
||||
justify: this text component{"'"}s contents are laid out with "textAlign: justify"
|
||||
and as you can see all of the lines except the last one span the
|
||||
available width of the parent container.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Letter Spacing',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{letterSpacing: 0}}>
|
||||
letterSpacing = 0
|
||||
</Text>
|
||||
<Text style={{letterSpacing: 2, marginTop: 5}}>
|
||||
letterSpacing = 2
|
||||
</Text>
|
||||
<Text style={{letterSpacing: 9, marginTop: 5}}>
|
||||
letterSpacing = 9
|
||||
</Text>
|
||||
<Text style={{letterSpacing: -1, marginTop: 5}}>
|
||||
letterSpacing = -1
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Spaces',
|
||||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
A {'generated'} {' '} {'string'} and some spaces
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Line Height',
|
||||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
<Text style={{lineHeight: 35}}>
|
||||
A lot of space between the lines of this long passage that should
|
||||
wrap once.
|
||||
</Text>
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Empty Text',
|
||||
description: 'It\'s ok to have Text with zero or null children.',
|
||||
render: function() {
|
||||
return (
|
||||
<Text />
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Toggling Attributes',
|
||||
render: function(): ReactElement<any> {
|
||||
return <AttributeToggler />;
|
||||
},
|
||||
}, {
|
||||
title: 'backgroundColor attribute',
|
||||
description: 'backgroundColor is inherited from all types of views.',
|
||||
render: function() {
|
||||
return (
|
||||
<Text style={{backgroundColor: 'yellow'}}>
|
||||
Yellow container background,
|
||||
<Text style={{backgroundColor: '#ffaaaa'}}>
|
||||
{' '}red background,
|
||||
<Text style={{backgroundColor: '#aaaaff'}}>
|
||||
{' '}blue background,
|
||||
<Text>
|
||||
{' '}inherited blue background,
|
||||
<Text style={{backgroundColor: '#aaffaa'}}>
|
||||
{' '}nested green background.
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'numberOfLines attribute',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text numberOfLines={1}>
|
||||
Maximum of one line, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after one line.
|
||||
</Text>
|
||||
<Text numberOfLines={2} style={{marginTop: 20}}>
|
||||
Maximum of two lines, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after two lines.
|
||||
</Text>
|
||||
<Text style={{marginTop: 20}}>
|
||||
No maximum lines specified, no matter how much I write here. If I keep writing, it{"'"}ll just keep going and going.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Text highlighting (tap the link to see highlight)',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Lorem ipsum dolor sit amet, <Text suppressHighlighting={false} style={{backgroundColor: 'white', textDecorationLine: 'underline', color: 'blue'}} onPress={() => null}>consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud</Text> exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'allowFontScaling attribute',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
By default, text will respect Text Size accessibility setting on iOS.
|
||||
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
|
||||
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
|
||||
</Text>
|
||||
<Text style={{marginTop: 10}}>
|
||||
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
|
||||
</Text>
|
||||
<Text allowFontScaling={false} style={{marginTop: 20}}>
|
||||
This text will not scale.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Inline views',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
This text contains an inline blue view <View style={{width: 25, height: 25, backgroundColor: 'steelblue'}} /> and
|
||||
an inline image <Image source={{ uri: 'http://lorempixel.com/30/11' }} style={{width: 30, height: 11, resizeMode: 'cover'}}/>. Neat, huh?
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Text shadow',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontSize: 20, textShadowOffset: {width: 2, height: 2}, textShadowRadius: 1, textShadowColor: '#00cccc'}}>
|
||||
Demo text shadow
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Line break mode',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text numberOfLines={1}>
|
||||
This very long text should be truncated with dots in the end.
|
||||
</Text>
|
||||
<Text lineBreakMode="middle" numberOfLines={1}>
|
||||
This very long text should be truncated with dots in the middle.
|
||||
</Text>
|
||||
<Text lineBreakMode="head" numberOfLines={1}>
|
||||
This very long text should be truncated with dots in the beginning.
|
||||
</Text>
|
||||
<Text lineBreakMode="clip" numberOfLines={1}>
|
||||
This very looooooooooooooooooooooooooooong text should be clipped.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
backgroundColorText: {
|
||||
margin: 5,
|
||||
marginBottom: 0,
|
||||
backgroundColor: 'rgba(100, 100, 100, 0.3)'
|
||||
},
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Text', module)
|
||||
.addDecorator((renderStory) => <View style={{ width: 320 }}>{renderStory()}</View>)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { StyleSheet, TextInput, View } from 'react-native'
|
||||
|
||||
storiesOf('component: TextInput', module)
|
||||
.add('tbd', () => (
|
||||
<View>
|
||||
<TextInput
|
||||
defaultValue='Default textInput'
|
||||
keyboardType='default'
|
||||
onBlur={(e) => { console.log('TextInput.onBlur', e) }}
|
||||
onChange={(e) => { console.log('TextInput.onChange', e) }}
|
||||
onChangeText={(e) => { console.log('TextInput.onChangeText', e) }}
|
||||
onFocus={(e) => { console.log('TextInput.onFocus', e) }}
|
||||
onSelectionChange={(e) => { console.log('TextInput.onSelectionChange', e) }}
|
||||
/>
|
||||
<TextInput keyboardType='search' style={styles.textInput} />
|
||||
<TextInput secureTextEntry style={styles.textInput} />
|
||||
<TextInput defaultValue='read only' editable={false} style={styles.textInput} />
|
||||
<TextInput
|
||||
style={[ styles.textInput, { flex:1, height: 60, padding: 20, fontSize: 20, textAlign: 'center' } ]}
|
||||
keyboardType='email-address' placeholder='you@domain.com' placeholderTextColor='red'
|
||||
/>
|
||||
<TextInput keyboardType='numeric' style={styles.textInput} />
|
||||
<TextInput keyboardType='phone-pad' style={styles.textInput} />
|
||||
<TextInput defaultValue='https://delete-me' keyboardType='url' placeholder='https://www.some-website.com' selectTextOnFocus style={styles.textInput} />
|
||||
<TextInput
|
||||
defaultValue='default value'
|
||||
maxNumberOfLines={10}
|
||||
multiline
|
||||
numberOfLines={5}
|
||||
style={styles.textInput}
|
||||
/>
|
||||
</View>
|
||||
))
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
textInput: {
|
||||
borderWidth: 1
|
||||
}
|
||||
})
|
||||
@@ -1,450 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import {
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
TouchableOpacity,
|
||||
Platform,
|
||||
TouchableNativeFeedback,
|
||||
View,
|
||||
} from 'react-native'
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: '<TouchableHighlight>',
|
||||
description: 'TouchableHighlight works by adding an extra view with a ' +
|
||||
'black background under the single child view. This works best when the ' +
|
||||
'child view is fully opaque, although it can be made to work as a simple ' +
|
||||
'background color change as well with the activeOpacity and ' +
|
||||
'underlayColor props.',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.row}>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
onPress={() => console.log('stock THW image - highlight')}>
|
||||
<Image
|
||||
source={heartImage}
|
||||
style={styles.image}
|
||||
/>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
style={styles.wrapper}
|
||||
activeOpacity={1}
|
||||
animationVelocity={0}
|
||||
underlayColor="rgb(210, 230, 255)"
|
||||
onPress={() => console.log('custom THW text - highlight')}>
|
||||
<View style={styles.wrapperCustom}>
|
||||
<Text style={styles.text}>
|
||||
Tap Here For Custom Highlight!
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: '<Text onPress={fn}> with highlight',
|
||||
render: function(): ReactElement<any> {
|
||||
return <TextOnPressBox />;
|
||||
},
|
||||
}, {
|
||||
title: 'Touchable feedback events',
|
||||
description: '<Touchable*> components accept onPress, onPressIn, ' +
|
||||
'onPressOut, and onLongPress as props.',
|
||||
render: function(): ReactElement<any> {
|
||||
return <TouchableFeedbackEvents />;
|
||||
},
|
||||
}, {
|
||||
title: 'Touchable delay for events',
|
||||
description: '<Touchable*> components also accept delayPressIn, ' +
|
||||
'delayPressOut, and delayLongPress as props. These props impact the ' +
|
||||
'timing of feedback events.',
|
||||
render: function(): ReactElement<any> {
|
||||
return <TouchableDelayEvents />;
|
||||
},
|
||||
}, {
|
||||
title: '3D Touch / Force Touch',
|
||||
description: 'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches',
|
||||
render: function(): ReactElement<any> {
|
||||
return <ForceTouchExample />;
|
||||
},
|
||||
platform: 'ios',
|
||||
}, {
|
||||
title: 'Touchable Hit Slop',
|
||||
description: '<Touchable*> components accept hitSlop prop which extends the touch area ' +
|
||||
'without changing the view bounds.',
|
||||
render: function(): ReactElement<any> {
|
||||
return <TouchableHitSlop />;
|
||||
},
|
||||
}, {
|
||||
title: 'Disabled Touchable*',
|
||||
description: '<Touchable*> components accept disabled prop which prevents ' +
|
||||
'any interaction with component',
|
||||
render: function(): ReactElement<any> {
|
||||
return <TouchableDisabled />;
|
||||
},
|
||||
}];
|
||||
|
||||
var TextOnPressBox = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
timesPressed: 0,
|
||||
};
|
||||
},
|
||||
textOnPress: function() {
|
||||
this.setState({
|
||||
timesPressed: this.state.timesPressed + 1,
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var textLog = '';
|
||||
if (this.state.timesPressed > 1) {
|
||||
textLog = this.state.timesPressed + 'x text onPress';
|
||||
} else if (this.state.timesPressed > 0) {
|
||||
textLog = 'text onPress';
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text
|
||||
style={styles.textBlock}
|
||||
onPress={this.textOnPress}>
|
||||
Text has built-in onPress handling
|
||||
</Text>
|
||||
<View style={styles.logBox}>
|
||||
<Text>
|
||||
{textLog}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var TouchableFeedbackEvents = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
eventLog: [],
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View testID="touchable_feedback_events">
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<TouchableOpacity
|
||||
style={styles.wrapper}
|
||||
testID="touchable_feedback_events_button"
|
||||
accessibilityLabel="touchable feedback events"
|
||||
accessibilityTraits="button"
|
||||
accessibilityComponentType="button"
|
||||
onPress={() => this._appendEvent('press')}
|
||||
onPressIn={() => this._appendEvent('pressIn')}
|
||||
onPressOut={() => this._appendEvent('pressOut')}
|
||||
onLongPress={() => this._appendEvent('longPress')}>
|
||||
<Text style={styles.button}>
|
||||
Press Me
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View testID="touchable_feedback_events_console" style={styles.eventLogBox}>
|
||||
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
_appendEvent: function(eventName) {
|
||||
var limit = 6;
|
||||
var eventLog = this.state.eventLog.slice(0, limit - 1);
|
||||
eventLog.unshift(eventName);
|
||||
this.setState({eventLog});
|
||||
},
|
||||
});
|
||||
|
||||
var TouchableDelayEvents = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
eventLog: [],
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View testID="touchable_delay_events">
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<TouchableOpacity
|
||||
style={styles.wrapper}
|
||||
testID="touchable_delay_events_button"
|
||||
onPress={() => this._appendEvent('press')}
|
||||
delayPressIn={400}
|
||||
onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
|
||||
delayPressOut={1000}
|
||||
onPressOut={() => this._appendEvent('pressOut - 1000ms delay')}
|
||||
delayLongPress={800}
|
||||
onLongPress={() => this._appendEvent('longPress - 800ms delay')}>
|
||||
<Text style={styles.button}>
|
||||
Press Me
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={styles.eventLogBox} testID="touchable_delay_events_console">
|
||||
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
_appendEvent: function(eventName) {
|
||||
var limit = 6;
|
||||
var eventLog = this.state.eventLog.slice(0, limit - 1);
|
||||
eventLog.unshift(eventName);
|
||||
this.setState({eventLog});
|
||||
},
|
||||
});
|
||||
|
||||
var ForceTouchExample = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
force: 0,
|
||||
};
|
||||
},
|
||||
_renderConsoleText: function() {
|
||||
return View.forceTouchAvailable ?
|
||||
'Force: ' + this.state.force.toFixed(3) :
|
||||
'3D Touch is not available on this device';
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View testID="touchable_3dtouch_event">
|
||||
<View style={styles.forceTouchBox} testID="touchable_3dtouch_output">
|
||||
<Text>{this._renderConsoleText()}</Text>
|
||||
</View>
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<View
|
||||
style={styles.wrapper}
|
||||
testID="touchable_3dtouch_button"
|
||||
onStartShouldSetResponder={() => true}
|
||||
onResponderMove={(event) => this.setState({force: event.nativeEvent.force})}
|
||||
onResponderRelease={(event) => this.setState({force: 0})}>
|
||||
<Text style={styles.button}>
|
||||
Press Me
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var TouchableHitSlop = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
timesPressed: 0,
|
||||
};
|
||||
},
|
||||
onPress: function() {
|
||||
this.setState({
|
||||
timesPressed: this.state.timesPressed + 1,
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var log = '';
|
||||
if (this.state.timesPressed > 1) {
|
||||
log = this.state.timesPressed + 'x onPress';
|
||||
} else if (this.state.timesPressed > 0) {
|
||||
log = 'onPress';
|
||||
}
|
||||
|
||||
return (
|
||||
<View testID="touchable_hit_slop">
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<TouchableOpacity
|
||||
onPress={this.onPress}
|
||||
style={styles.hitSlopWrapper}
|
||||
hitSlop={{top: 30, bottom: 30, left: 60, right: 60}}
|
||||
testID="touchable_hit_slop_button">
|
||||
<Text style={styles.hitSlopButton}>
|
||||
Press Outside This View
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={styles.logBox}>
|
||||
<Text>
|
||||
{log}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var TouchableDisabled = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity disabled={true} style={[styles.row, styles.block]}>
|
||||
<Text style={styles.disabledButton}>Disabled TouchableOpacity</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity disabled={false} style={[styles.row, styles.block]}>
|
||||
<Text style={styles.button}>Enabled TouchableOpacity</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableHighlight
|
||||
activeOpacity={1}
|
||||
disabled={true}
|
||||
animationVelocity={0}
|
||||
underlayColor="rgb(210, 230, 255)"
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom THW text - highlight')}>
|
||||
<Text style={styles.disabledButton}>
|
||||
Disabled TouchableHighlight
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
|
||||
<TouchableHighlight
|
||||
activeOpacity={1}
|
||||
animationVelocity={0}
|
||||
underlayColor="rgb(210, 230, 255)"
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom THW text - highlight')}>
|
||||
<Text style={styles.button}>
|
||||
Enabled TouchableHighlight
|
||||
</Text>
|
||||
</TouchableHighlight>
|
||||
|
||||
{Platform.OS === 'android' &&
|
||||
<TouchableNativeFeedback
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom TNF has been clicked')}
|
||||
background={TouchableNativeFeedback.SelectableBackground()}>
|
||||
<View>
|
||||
<Text style={[styles.button, styles.nativeFeedbackButton]}>
|
||||
Enabled TouchableNativeFeedback
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableNativeFeedback>
|
||||
}
|
||||
|
||||
{Platform.OS === 'android' &&
|
||||
<TouchableNativeFeedback
|
||||
disabled={true}
|
||||
style={[styles.row, styles.block]}
|
||||
onPress={() => console.log('custom TNF has been clicked')}
|
||||
background={TouchableNativeFeedback.SelectableBackground()}>
|
||||
<View>
|
||||
<Text style={[styles.disabledButton, styles.nativeFeedbackButton]}>
|
||||
Disabled TouchableNativeFeedback
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableNativeFeedback>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'};
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
row: {
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
icon: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
image: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
},
|
||||
block: {
|
||||
padding: 10,
|
||||
},
|
||||
button: {
|
||||
color: '#007AFF',
|
||||
},
|
||||
disabledButton: {
|
||||
color: '#007AFF',
|
||||
opacity: 0.5,
|
||||
},
|
||||
nativeFeedbackButton: {
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
hitSlopButton: {
|
||||
color: 'white',
|
||||
},
|
||||
wrapper: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
wrapperCustom: {
|
||||
borderRadius: 8,
|
||||
padding: 6,
|
||||
},
|
||||
hitSlopWrapper: {
|
||||
backgroundColor: 'red',
|
||||
marginVertical: 30,
|
||||
},
|
||||
logBox: {
|
||||
padding: 20,
|
||||
margin: 10,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#f0f0f0',
|
||||
backgroundColor: '#f9f9f9',
|
||||
},
|
||||
eventLogBox: {
|
||||
padding: 10,
|
||||
margin: 10,
|
||||
height: 120,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#f0f0f0',
|
||||
backgroundColor: '#f9f9f9',
|
||||
},
|
||||
forceTouchBox: {
|
||||
padding: 10,
|
||||
margin: 10,
|
||||
borderWidth: StyleSheet.hairlineWidth,
|
||||
borderColor: '#f0f0f0',
|
||||
backgroundColor: '#f9f9f9',
|
||||
alignItems: 'center',
|
||||
},
|
||||
textBlock: {
|
||||
fontWeight: '500',
|
||||
color: 'blue',
|
||||
},
|
||||
});
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: Touchable*', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,250 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
box: {
|
||||
backgroundColor: '#527FE4',
|
||||
borderColor: '#000033',
|
||||
borderWidth: 1,
|
||||
},
|
||||
zIndex: {
|
||||
justifyContent: 'space-around',
|
||||
width: 100,
|
||||
height: 50,
|
||||
marginTop: -10,
|
||||
},
|
||||
});
|
||||
|
||||
var ViewBorderStyleExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
showBorder: true
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._handlePress}>
|
||||
<View>
|
||||
<View style={{
|
||||
borderWidth: 1,
|
||||
borderStyle: this.state.showBorder ? 'dashed' : null,
|
||||
padding: 5
|
||||
}}>
|
||||
<Text style={{fontSize: 11}}>
|
||||
Dashed border style
|
||||
</Text>
|
||||
</View>
|
||||
<View style={{
|
||||
marginTop: 5,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderStyle: this.state.showBorder ? 'dotted' : null,
|
||||
padding: 5
|
||||
}}>
|
||||
<Text style={{fontSize: 11}}>
|
||||
Dotted border style
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
},
|
||||
|
||||
_handlePress() {
|
||||
this.setState({showBorder: !this.state.showBorder});
|
||||
}
|
||||
});
|
||||
|
||||
var ZIndexExample = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
flipped: false
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
const indices = this.state.flipped ? [-1, 0, 1, 2] : [2, 1, 0, -1];
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._handlePress}>
|
||||
<View>
|
||||
<Text style={{paddingBottom: 10}}>Tap to flip sorting order</Text>
|
||||
<View style={[
|
||||
styles.zIndex,
|
||||
{marginTop: 0, backgroundColor: '#E57373', zIndex: indices[0]}
|
||||
]}>
|
||||
<Text>ZIndex {indices[0]}</Text>
|
||||
</View>
|
||||
<View style={[
|
||||
styles.zIndex,
|
||||
{marginLeft: 50, backgroundColor: '#FFF176', zIndex: indices[1]}
|
||||
]}>
|
||||
<Text>ZIndex {indices[1]}</Text>
|
||||
</View>
|
||||
<View style={[
|
||||
styles.zIndex,
|
||||
{marginLeft: 100, backgroundColor: '#81C784', zIndex: indices[2]}
|
||||
]}>
|
||||
<Text>ZIndex {indices[2]}</Text>
|
||||
</View>
|
||||
<View style={[
|
||||
styles.zIndex,
|
||||
{marginLeft: 150, backgroundColor: '#64B5F6', zIndex: indices[3]}
|
||||
]}>
|
||||
<Text>ZIndex {indices[3]}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
},
|
||||
|
||||
_handlePress() {
|
||||
this.setState({flipped: !this.state.flipped});
|
||||
}
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Background Color',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{backgroundColor: '#527FE4', padding: 5}}>
|
||||
<Text style={{fontSize: 11}}>
|
||||
Blue background
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Border',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{borderColor: '#527FE4', borderWidth: 5, padding: 10}}>
|
||||
<Text style={{fontSize: 11}}>5px blue border</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Padding/Margin',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{borderColor: '#bb0000', borderWidth: 0.5}}>
|
||||
<View style={[styles.box, {padding: 5}]}>
|
||||
<Text style={{fontSize: 11}}>5px padding</Text>
|
||||
</View>
|
||||
<View style={[styles.box, {margin: 5}]}>
|
||||
<Text style={{fontSize: 11}}>5px margin</Text>
|
||||
</View>
|
||||
<View style={[styles.box, {margin: 5, padding: 5, alignSelf: 'flex-start'}]}>
|
||||
<Text style={{fontSize: 11}}>
|
||||
5px margin and padding,
|
||||
</Text>
|
||||
<Text style={{fontSize: 11}}>
|
||||
widthAutonomous=true
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Border Radius',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{borderWidth: 0.5, borderRadius: 5, padding: 5}}>
|
||||
<Text style={{fontSize: 11}}>
|
||||
Too much use of `borderRadius` (especially large radii) on
|
||||
anything which is scrolling may result in dropped frames.
|
||||
Use sparingly.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Border Style',
|
||||
render: function() {
|
||||
return <ViewBorderStyleExample />;
|
||||
},
|
||||
}, {
|
||||
title: 'Circle with Border Radius',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{borderRadius: 10, borderWidth: 1, width: 20, height: 20}} />
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Overflow',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{flexDirection: 'row'}}>
|
||||
<View
|
||||
style={{
|
||||
width: 95,
|
||||
height: 10,
|
||||
marginRight: 10,
|
||||
marginBottom: 5,
|
||||
overflow: 'hidden',
|
||||
borderWidth: 0.5,
|
||||
}}>
|
||||
<View style={{width: 200, height: 20}}>
|
||||
<Text>Overflow hidden</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{width: 95, height: 10, marginBottom: 5, borderWidth: 0.5}}>
|
||||
<View style={{width: 200, height: 20}}>
|
||||
<Text>Overflow visible</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Opacity',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View style={{opacity: 0}}><Text>Opacity 0</Text></View>
|
||||
<View style={{opacity: 0.1}}><Text>Opacity 0.1</Text></View>
|
||||
<View style={{opacity: 0.3}}><Text>Opacity 0.3</Text></View>
|
||||
<View style={{opacity: 0.5}}><Text>Opacity 0.5</Text></View>
|
||||
<View style={{opacity: 0.7}}><Text>Opacity 0.7</Text></View>
|
||||
<View style={{opacity: 0.9}}><Text>Opacity 0.9</Text></View>
|
||||
<View style={{opacity: 1}}><Text>Opacity 1</Text></View>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'ZIndex',
|
||||
render: function() {
|
||||
return <ZIndexExample />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: View', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,286 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import { Animated, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* @flow
|
||||
*/
|
||||
|
||||
var Flip = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
theta: new Animated.Value(45),
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this._animate();
|
||||
},
|
||||
|
||||
_animate() {
|
||||
this.state.theta.setValue(0);
|
||||
Animated.timing(this.state.theta, {
|
||||
toValue: 360,
|
||||
duration: 5000,
|
||||
}).start(this._animate);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.flipCardContainer}>
|
||||
<Animated.View style={[
|
||||
styles.flipCard,
|
||||
{transform: [
|
||||
{perspective: 850},
|
||||
{rotateX: this.state.theta.interpolate({
|
||||
inputRange: [0, 180],
|
||||
outputRange: ['0deg', '180deg']
|
||||
})},
|
||||
]}]}>
|
||||
<Text style={styles.flipText}>
|
||||
This text is flipping great.
|
||||
</Text>
|
||||
</Animated.View>
|
||||
<Animated.View style={[styles.flipCard, {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
backgroundColor: 'red',
|
||||
transform: [
|
||||
{perspective: 850},
|
||||
{rotateX: this.state.theta.interpolate({
|
||||
inputRange: [0, 180],
|
||||
outputRange: ['180deg', '360deg']
|
||||
})},
|
||||
]}]}>
|
||||
<Text style={styles.flipText}>
|
||||
On the flip side...
|
||||
</Text>
|
||||
</Animated.View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
box1: {
|
||||
left: 0,
|
||||
backgroundColor: 'green',
|
||||
height: 50,
|
||||
top: 0,
|
||||
transform: [
|
||||
{translateX: 100},
|
||||
{translateY: 50},
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box2: {
|
||||
left: 0,
|
||||
backgroundColor: 'purple',
|
||||
height: 50,
|
||||
top: 0,
|
||||
transform: [
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
{translateX: 100},
|
||||
{translateY: 50},
|
||||
{rotate: '30deg'},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step1: {
|
||||
left: 0,
|
||||
backgroundColor: 'lightpink',
|
||||
height: 50,
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step2: {
|
||||
left: 0,
|
||||
backgroundColor: 'hotpink',
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step3: {
|
||||
left: 0,
|
||||
backgroundColor: 'deeppink',
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
{translateX: 10},
|
||||
{translateY: 50},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box4: {
|
||||
left: 0,
|
||||
backgroundColor: 'darkorange',
|
||||
height: 50,
|
||||
top: 0,
|
||||
transform: [
|
||||
{translateX: 20},
|
||||
{translateY: 35},
|
||||
{scale: 2.5},
|
||||
{rotate: '-0.2rad'},
|
||||
],
|
||||
width: 100,
|
||||
},
|
||||
box5: {
|
||||
backgroundColor: 'maroon',
|
||||
height: 50,
|
||||
right: 0,
|
||||
top: 0,
|
||||
width: 50,
|
||||
},
|
||||
box5Transform: {
|
||||
transform: [
|
||||
{translateX: -50},
|
||||
{translateY: 35},
|
||||
{rotate: '50deg'},
|
||||
{scale: 2},
|
||||
],
|
||||
},
|
||||
flipCardContainer: {
|
||||
marginVertical: 40,
|
||||
flex: 1,
|
||||
alignSelf: 'center',
|
||||
},
|
||||
flipCard: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'blue',
|
||||
backfaceVisibility: 'hidden',
|
||||
},
|
||||
flipText: {
|
||||
width: 90,
|
||||
fontSize: 20,
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
}
|
||||
});
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Perspective',
|
||||
description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)',
|
||||
render(): ReactElement<any> { return <Flip />; }
|
||||
},
|
||||
{
|
||||
title: 'Translate, Rotate, Scale',
|
||||
description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box1} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Scale, Translate, Rotate, ',
|
||||
description: "scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box2} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate',
|
||||
description: "rotate: '30deg'",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box3step1} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate, Scale',
|
||||
description: "rotate: '30deg', scaleX: 2, scaleY: 2",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box3step2} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Rotate, Scale, Translate ',
|
||||
description: "rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box3step3} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Translate, Scale, Rotate',
|
||||
description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={styles.box4} />
|
||||
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Translate, Rotate, Scale',
|
||||
description: "translate: [-50, 35], rotate: '50deg', scale: 2",
|
||||
render() {
|
||||
return (
|
||||
|
||||
<View style={[styles.box5, styles.box5Transform]} />
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
examples.forEach((example) => {
|
||||
storiesOf('component: View (transforms)', module)
|
||||
.add(example.title, () => example.render())
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import Game2048 from './Game2048'
|
||||
|
||||
storiesOf('demo: Game2048', module)
|
||||
.add('the game', () => (
|
||||
<Game2048 />
|
||||
))
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from 'react';
|
||||
import { storiesOf, action } from '@kadira/storybook';
|
||||
import TicTacToe from './TicTacToe'
|
||||
|
||||
storiesOf('demo: TicTacToe', module)
|
||||
.add('the game', () => (
|
||||
<TicTacToe />
|
||||
))
|
||||
@@ -1,62 +0,0 @@
|
||||
const webpack = require('webpack')
|
||||
|
||||
const testEntry = 'src/tests.webpack.js'
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
browsers: process.env.TRAVIS ? [ 'Firefox' ] : [ 'Chrome' ],
|
||||
browserNoActivityTimeout: 60000,
|
||||
client: {
|
||||
captureConsole: true,
|
||||
mocha: { ui: 'tdd' },
|
||||
useIframe: true
|
||||
},
|
||||
files: [
|
||||
testEntry
|
||||
],
|
||||
frameworks: [ 'mocha' ],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-mocha',
|
||||
'karma-mocha-reporter',
|
||||
'karma-sourcemap-loader',
|
||||
'karma-webpack'
|
||||
],
|
||||
preprocessors: {
|
||||
[testEntry]: [ 'webpack', 'sourcemap' ]
|
||||
},
|
||||
reporters: process.env.TRAVIS ? [ 'dots' ] : [ 'mocha' ],
|
||||
singleRun: true,
|
||||
webpack: {
|
||||
devtool: 'inline-source-map',
|
||||
// required by 'enzyme'
|
||||
externals: {
|
||||
'cheerio': 'window',
|
||||
'react/addons': true,
|
||||
'react/lib/ExecutionEnvironment': true,
|
||||
'react/lib/ReactContext': true
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel',
|
||||
query: { cacheDirectory: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify('test')
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
webpackServer: {
|
||||
noInfo: true
|
||||
}
|
||||
})
|
||||
}
|
||||
137
package.json
137
package.json
@@ -1,77 +1,76 @@
|
||||
{
|
||||
"name": "react-native-web",
|
||||
"version": "0.0.45",
|
||||
"description": "React Native for Web",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"private": true,
|
||||
"version": "0.5.3",
|
||||
"name": "react-native-web-monorepo",
|
||||
"scripts": {
|
||||
"build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__",
|
||||
"build:examples": "build-storybook -o dist-examples -c ./examples/.storybook",
|
||||
"build:umd": "webpack --config webpack.config.js --sort-assets-by --progress",
|
||||
"deploy:examples": "git checkout gh-pages && rm -rf ./storybook && mv dist-examples storybook && git add -A && git commit -m \"Storybook deploy\" && git push origin gh-pages && git checkout -",
|
||||
"examples": "start-storybook -p 9001 -c ./examples/.storybook --dont-track",
|
||||
"lint": "eslint src",
|
||||
"prepublish": "npm run build && npm run build:umd",
|
||||
"test": "karma start karma.config.js",
|
||||
"test:watch": "npm run test -- --no-single-run"
|
||||
},
|
||||
"dependencies": {
|
||||
"animated": "^0.1.3",
|
||||
"babel-runtime": "^6.11.6",
|
||||
"fbjs": "^0.8.4",
|
||||
"inline-style-prefixer": "^2.0.1",
|
||||
"lodash": "^4.15.0",
|
||||
"react-dom": "^15.3.1",
|
||||
"react-textarea-autosize": "^4.0.4",
|
||||
"react-timer-mixin": "^0.13.3"
|
||||
"clean": "del ./packages/*/dist",
|
||||
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
|
||||
"benchmarks": "cd packages/benchmarks && yarn build",
|
||||
"benchmarks:release": "cd packages/benchmarks && yarn release",
|
||||
"website": "cd website && yarn start",
|
||||
"website:release": "cd website && yarn release",
|
||||
"flow": "flow",
|
||||
"fmt": "find packages scripts types website -name '*.js' | grep -v -E '(node_modules|dist|vendor)' | xargs yarn fmt:cmd",
|
||||
"fmt:cmd": "prettier --write",
|
||||
"jest": "jest --config ./scripts/jest/config.js",
|
||||
"lint": "yarn lint:check --fix",
|
||||
"lint:check": "eslint packages scripts website",
|
||||
"precommit": "lint-staged",
|
||||
"prerelease": "yarn test && yarn compile",
|
||||
"release": "node ./scripts/release/publish.js",
|
||||
"postrelease": "yarn website:release && yarn benchmarks:release",
|
||||
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^2.5.1",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-eslint": "^6.1.2",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-preset-react-native": "^1.9.0",
|
||||
"del-cli": "^0.2.0",
|
||||
"enzyme": "^2.4.1",
|
||||
"eslint": "^3.4.0",
|
||||
"eslint-plugin-jsx-a11y": "^2.2.0",
|
||||
"eslint-plugin-promise": "^2.0.1",
|
||||
"eslint-plugin-react": "^6.1.2",
|
||||
"file-loader": "^0.9.0",
|
||||
"karma": "^1.2.0",
|
||||
"karma-browserstack-launcher": "^1.0.1",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-firefox-launcher": "^1.0.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-mocha-reporter": "^2.1.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^1.8.0",
|
||||
"mocha": "^3.0.2",
|
||||
"node-libs-browser": "^0.5.3",
|
||||
"react": "^15.3.1",
|
||||
"react-addons-test-utils": "^15.3.1",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.13.2"
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-eslint": "^8.0.3",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-flow": "^6.23.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-react-native": "^4.0.0",
|
||||
"caniuse-api": "^2.0.0",
|
||||
"del-cli": "^1.1.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.0",
|
||||
"enzyme-to-json": "^3.2.2",
|
||||
"eslint": "^4.12.1",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-promise": "^3.6.0",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
"flow-bin": "^0.63.1",
|
||||
"glob": "^7.1.2",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^21.2.1",
|
||||
"lint-staged": "^6.0.0",
|
||||
"prettier": "^1.8.2",
|
||||
"raf": "^3.4.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-test-renderer": "^16.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.3.1"
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"website"
|
||||
],
|
||||
"lint-staged": {
|
||||
"**/*.js": [
|
||||
"fmt:cmd",
|
||||
"git update-index --again",
|
||||
"eslint"
|
||||
],
|
||||
"packages/react-native-web/src/index.js": [
|
||||
"node ./scripts/babel/createModuleMap.js"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": true
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/necolas/react-native-web.git"
|
||||
},
|
||||
"tags": [
|
||||
"react"
|
||||
],
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"react-native",
|
||||
"web"
|
||||
]
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
44
packages/babel-plugin-react-native-web/README.md
Normal file
44
packages/babel-plugin-react-native-web/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# babel-plugin-react-native-web
|
||||
|
||||
[![npm version][package-badge]][package-url] [](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
|
||||
|
||||
A Babel plugin that will alias `react-native` to `react-native-web` and exclude
|
||||
any modules not required by your app (keeping bundle size down).
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
yarn add --dev babel-plugin-react-native-web
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
**.babelrc**
|
||||
|
||||
```
|
||||
{
|
||||
"plugins": ["react-native-web"]
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
NOTE: `react-native-web` internal paths are _not stable_ and you must not rely
|
||||
on them. Always use the Babel plugin to optimize your build. What follows is an
|
||||
example of the rewrite performed by the plugin.
|
||||
|
||||
**Before**
|
||||
|
||||
```js
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
```
|
||||
|
||||
**After**
|
||||
|
||||
```js
|
||||
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
|
||||
import View from 'react-native-web/dist/exports/View';
|
||||
```
|
||||
|
||||
[package-badge]: https://img.shields.io/npm/v/babel-plugin-react-native-web.svg?style=flat
|
||||
[package-url]: https://yarnpkg.com/en/package/babel-plugin-react-native-web
|
||||
1
packages/babel-plugin-react-native-web/index.js
Normal file
1
packages/babel-plugin-react-native-web/index.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('./src');
|
||||
15
packages/babel-plugin-react-native-web/package.json
Normal file
15
packages/babel-plugin-react-native-web/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "babel-plugin-react-native-web",
|
||||
"version": "0.5.3",
|
||||
"description": "Babel plugin for React Native for Web",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"babel-plugin-tester": "^5.0.0"
|
||||
},
|
||||
"author": "Nicolas Gallagher",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/necolas/react-native-web.git"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`export from "react-native" 1`] = `
|
||||
"
|
||||
export { View } from 'react-native';
|
||||
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
export { default as View } from 'react-native-web/dist/exports/View';
|
||||
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
|
||||
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
|
||||
export { default as Text } from 'react-native-web/dist/exports/Text';
|
||||
export { default as createElement } from 'react-native-web/dist/exports/createElement';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`export from "react-native-web" 1`] = `
|
||||
"
|
||||
export { View } from 'react-native-web';
|
||||
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
export { default as View } from 'react-native-web/dist/exports/View';
|
||||
export { default as ColorPropType } from 'react-native-web/dist/exports/ColorPropType';
|
||||
export { default as StyleSheet } from 'react-native-web/dist/exports/StyleSheet';
|
||||
export { default as Text } from 'react-native-web/dist/exports/Text';
|
||||
export { default as createElement } from 'react-native-web/dist/exports/createElement';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`import from "native-native" 1`] = `
|
||||
"
|
||||
import ReactNative from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
|
||||
import * as ReactNativeModules from 'react-native';
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
import ReactNative from 'react-native-web/dist/index';
|
||||
import View from 'react-native-web/dist/exports/View';
|
||||
import { Invalid } from 'react-native-web/dist/index';
|
||||
import MyView from 'react-native-web/dist/exports/View';
|
||||
import ViewPropTypes from 'react-native-web/dist/exports/ViewPropTypes';
|
||||
import * as ReactNativeModules from 'react-native-web/dist/index';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`import from "react-native-web" 1`] = `
|
||||
"
|
||||
import { createElement } from 'react-native-web';
|
||||
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
|
||||
import * as ReactNativeModules from 'react-native-web';
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
import createElement from 'react-native-web/dist/exports/createElement';
|
||||
import ColorPropType from 'react-native-web/dist/exports/ColorPropType';
|
||||
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
|
||||
import View from 'react-native-web/dist/exports/View';
|
||||
import TouchableOpacity from 'react-native-web/dist/exports/TouchableOpacity';
|
||||
import processColor from 'react-native-web/dist/exports/processColor';
|
||||
import * as ReactNativeModules from 'react-native-web/dist/index';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`require "react-native" 1`] = `
|
||||
"
|
||||
const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
const ReactNative = require('react-native-web/dist/index').default;
|
||||
|
||||
const View = require('react-native-web/dist/exports/View').default;
|
||||
|
||||
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
|
||||
|
||||
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`require "react-native-web" 1`] = `
|
||||
"
|
||||
const ReactNative = require('react-native-web');
|
||||
const { createElement } = require('react-native-web');
|
||||
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');
|
||||
|
||||
↓ ↓ ↓ ↓ ↓ ↓
|
||||
|
||||
const ReactNative = require('react-native-web/dist/index').default;
|
||||
|
||||
const createElement = require('react-native-web/dist/exports/createElement').default;
|
||||
|
||||
const ColorPropType = require('react-native-web/dist/exports/ColorPropType').default;
|
||||
|
||||
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
|
||||
|
||||
const View = require('react-native-web/dist/exports/View').default;
|
||||
|
||||
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
|
||||
|
||||
const processColor = require('react-native-web/dist/exports/processColor').default;
|
||||
"
|
||||
`;
|
||||
@@ -0,0 +1,52 @@
|
||||
const plugin = require('..');
|
||||
const pluginTester = require('babel-plugin-tester');
|
||||
|
||||
const tests = [
|
||||
// import react-native
|
||||
{
|
||||
title: 'import from "native-native"',
|
||||
code: `import ReactNative from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
|
||||
import * as ReactNativeModules from 'react-native';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'import from "react-native-web"',
|
||||
code: `import { createElement } from 'react-native-web';
|
||||
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
|
||||
import * as ReactNativeModules from 'react-native-web';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'export from "react-native"',
|
||||
code: `export { View } from 'react-native';
|
||||
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'export from "react-native-web"',
|
||||
code: `export { View } from 'react-native-web';
|
||||
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'require "react-native"',
|
||||
code: `const ReactNative = require('react-native');
|
||||
const { View } = require('react-native');
|
||||
const { StyleSheet, TouchableOpacity } = require('react-native');`,
|
||||
snapshot: true
|
||||
},
|
||||
{
|
||||
title: 'require "react-native-web"',
|
||||
code: `const ReactNative = require('react-native-web');
|
||||
const { createElement } = require('react-native-web');
|
||||
const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = require('react-native-web');`,
|
||||
snapshot: true
|
||||
}
|
||||
];
|
||||
|
||||
pluginTester({
|
||||
plugin,
|
||||
tests
|
||||
});
|
||||
128
packages/babel-plugin-react-native-web/src/index.js
Normal file
128
packages/babel-plugin-react-native-web/src/index.js
Normal file
@@ -0,0 +1,128 @@
|
||||
const moduleMap = require('./moduleMap');
|
||||
|
||||
const getDistLocation = importName =>
|
||||
importName && moduleMap[importName] ? `react-native-web/dist/exports/${importName}` : undefined;
|
||||
|
||||
const isReactNativeRequire = (t, node) => {
|
||||
const { declarations } = node;
|
||||
if (declarations.length > 1) {
|
||||
return false;
|
||||
}
|
||||
const { id, init } = declarations[0];
|
||||
return (
|
||||
(t.isObjectPattern(id) || t.isIdentifier(id)) &&
|
||||
t.isCallExpression(init) &&
|
||||
t.isIdentifier(init.callee) &&
|
||||
init.callee.name === 'require' &&
|
||||
init.arguments.length === 1 &&
|
||||
(init.arguments[0].value === 'react-native' || init.arguments[0].value === 'react-native-web')
|
||||
);
|
||||
};
|
||||
|
||||
const isReactNativeModule = ({ source, specifiers }) =>
|
||||
source &&
|
||||
(source.value === 'react-native' || source.value === 'react-native-web') &&
|
||||
specifiers.length;
|
||||
|
||||
module.exports = function({ types: t }) {
|
||||
return {
|
||||
name: 'Rewrite react-native to react-native-web',
|
||||
visitor: {
|
||||
ImportDeclaration(path, state) {
|
||||
const { specifiers } = path.node;
|
||||
if (isReactNativeModule(path.node)) {
|
||||
const imports = specifiers
|
||||
.map(specifier => {
|
||||
if (t.isImportSpecifier(specifier)) {
|
||||
const importName = specifier.imported.name;
|
||||
const distLocation = getDistLocation(importName);
|
||||
|
||||
if (distLocation) {
|
||||
return t.importDeclaration(
|
||||
[t.importDefaultSpecifier(t.identifier(specifier.local.name))],
|
||||
t.stringLiteral(distLocation)
|
||||
);
|
||||
}
|
||||
}
|
||||
return t.importDeclaration(
|
||||
[specifier],
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
path.replaceWithMultiple(imports);
|
||||
}
|
||||
},
|
||||
ExportNamedDeclaration(path, state) {
|
||||
const { specifiers } = path.node;
|
||||
if (isReactNativeModule(path.node)) {
|
||||
const exports = specifiers
|
||||
.map(specifier => {
|
||||
if (t.isExportSpecifier(specifier)) {
|
||||
const exportName = specifier.exported.name;
|
||||
const localName = specifier.local.name;
|
||||
const distLocation = getDistLocation(localName);
|
||||
|
||||
if (distLocation) {
|
||||
return t.exportNamedDeclaration(
|
||||
null,
|
||||
[t.exportSpecifier(t.identifier('default'), t.identifier(exportName))],
|
||||
t.stringLiteral(distLocation)
|
||||
);
|
||||
}
|
||||
}
|
||||
return t.exportNamedDeclaration(
|
||||
null,
|
||||
[specifier],
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
path.replaceWithMultiple(exports);
|
||||
}
|
||||
},
|
||||
VariableDeclaration(path, state) {
|
||||
if (isReactNativeRequire(t, path.node)) {
|
||||
const { id } = path.node.declarations[0];
|
||||
if (t.isObjectPattern(id)) {
|
||||
const imports = id.properties
|
||||
.map(identifier => {
|
||||
const distLocation = getDistLocation(identifier.key.name);
|
||||
if (distLocation) {
|
||||
return t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
t.identifier(identifier.value.name),
|
||||
t.memberExpression(
|
||||
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)]),
|
||||
t.identifier('default')
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
path.replaceWithMultiple(imports);
|
||||
} else if (t.isIdentifier(id)) {
|
||||
const name = id.name;
|
||||
const importIndex = t.variableDeclaration(path.node.kind, [
|
||||
t.variableDeclarator(
|
||||
t.identifier(name),
|
||||
t.memberExpression(
|
||||
t.callExpression(t.identifier('require'), [
|
||||
t.stringLiteral('react-native-web/dist/index')
|
||||
]),
|
||||
t.identifier('default')
|
||||
)
|
||||
)
|
||||
]);
|
||||
|
||||
path.replaceWith(importIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
61
packages/babel-plugin-react-native-web/src/moduleMap.js
Normal file
61
packages/babel-plugin-react-native-web/src/moduleMap.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
module.exports = {
|
||||
ART: true,
|
||||
ActivityIndicator: true,
|
||||
Animated: true,
|
||||
AppRegistry: true,
|
||||
AppState: true,
|
||||
AsyncStorage: true,
|
||||
BackHandler: true,
|
||||
Button: true,
|
||||
CheckBox: true,
|
||||
Clipboard: true,
|
||||
ColorPropType: true,
|
||||
Dimensions: true,
|
||||
Easing: true,
|
||||
EdgeInsetsPropType: true,
|
||||
FlatList: true,
|
||||
I18nManager: true,
|
||||
Image: true,
|
||||
ImageBackground: true,
|
||||
InteractionManager: true,
|
||||
Keyboard: true,
|
||||
KeyboardAvoidingView: true,
|
||||
Linking: true,
|
||||
ListView: true,
|
||||
Modal: true,
|
||||
NativeModules: true,
|
||||
NetInfo: true,
|
||||
PanResponder: true,
|
||||
Picker: true,
|
||||
PixelRatio: true,
|
||||
Platform: true,
|
||||
PointPropType: true,
|
||||
ProgressBar: true,
|
||||
RefreshControl: true,
|
||||
SafeAreaView: true,
|
||||
ScrollView: true,
|
||||
SectionList: true,
|
||||
Slider: true,
|
||||
StatusBar: true,
|
||||
StyleSheet: true,
|
||||
Switch: true,
|
||||
Text: true,
|
||||
TextInput: true,
|
||||
TextPropTypes: true,
|
||||
Touchable: true,
|
||||
TouchableHighlight: true,
|
||||
TouchableNativeFeedback: true,
|
||||
TouchableOpacity: true,
|
||||
TouchableWithoutFeedback: true,
|
||||
UIManager: true,
|
||||
Vibration: true,
|
||||
View: true,
|
||||
ViewPropTypes: true,
|
||||
VirtualizedList: true,
|
||||
createElement: true,
|
||||
findNodeHandle: true,
|
||||
processColor: true,
|
||||
render: true,
|
||||
unmountComponentAtNode: true
|
||||
};
|
||||
70
packages/benchmarks/README.md
Normal file
70
packages/benchmarks/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# benchmarks
|
||||
|
||||
Try the [benchmarks app](https://necolas.github.io/react-native-web/benchmarks) online.
|
||||
|
||||
To run the benchmarks locally:
|
||||
|
||||
```
|
||||
yarn benchmarks
|
||||
open ./packages/benchmarks/dist/index.html
|
||||
```
|
||||
|
||||
Develop against these benchmarks:
|
||||
|
||||
```
|
||||
yarn compile --watch
|
||||
yarn benchmarks --watch
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
These benchmarks are approximations of extreme cases that libraries may
|
||||
encounter. Their purpose is to provide an early-warning signal for performance
|
||||
regressions. Each test report includes the mean and standard deviation of the
|
||||
timings, and approximations of the time spent in scripting (S) and layout (L).
|
||||
|
||||
The components used in the render benchmarks are simple enough to be
|
||||
implemented by multiple UI or style libraries. The benchmark implementations
|
||||
and the features of the style libraries are _only approximately equivalent in
|
||||
functionality_.
|
||||
|
||||
No benchmark will run for more than 20 seconds.
|
||||
|
||||
### Mount deep/wide tree
|
||||
|
||||
These cases look at the performance of mounting and rendering large trees of
|
||||
elements that use static styles.
|
||||
|
||||
### Update dynamic styles
|
||||
|
||||
This case looks at the performance of repeated style updates to a large mounted
|
||||
tree. Some libraries choose to inject new styles for each "dynamic style",
|
||||
whereas others choose to use inline styles. Libraries without built-in support
|
||||
for dynamic styles (i.e., they rely on user-authored inline styles) are not
|
||||
included.
|
||||
|
||||
## Example results
|
||||
|
||||
### MacBook Pro (2011)
|
||||
|
||||
MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3 RAM. Google Chrome 63.
|
||||
|
||||
Typical render timings: mean ± standard deviations.
|
||||
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `css-modules` | `30.19` `±04.84` | `38.25` `±04.85` | - |
|
||||
| `react-native-web@0.4.0` | `36.40` `±04.98` | `51.28` `±05.58` | `19.36` `±02.56` |
|
||||
| `inline-styles` | `64.12` `±07.69` | `94.49` `±11.34` | `09.84` `±02.36` |
|
||||
|
||||
### Moto G4
|
||||
|
||||
Moto G4 (Android 7); Octa-core (4x1.5 GHz & 4x1.2 Ghz); 2 GB RAM. Google Chrome 63.
|
||||
|
||||
Typical render timings: mean ± standard deviations.
|
||||
|
||||
| Implementation | Mount deep tree (ms) | Mount wide tree (ms) | Dynamic update (ms) |
|
||||
| :--- | ---: | ---: | ---: |
|
||||
| `css-modules` | `98.24` `±20.26` | `143.75` `±25.50` | - |
|
||||
| `react-native-web@0.4.0` | `131.46` `±18.96` | `174.70` `±14.88` | `60.87` `±06.32` |
|
||||
| `inline-styles` | `184.58` `±26.23` | `273.86` `±26.23` | `30.28` `±07.44` |
|
||||
16
packages/benchmarks/index.html
Normal file
16
packages/benchmarks/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Performance tests</title>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
html, body { height: 100%; width: 100%; overflow: hidden; }
|
||||
.root { height: 100%; overflow: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="root"></div>
|
||||
<script src="./bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
36
packages/benchmarks/package.json
Normal file
36
packages/benchmarks/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "benchmarks",
|
||||
"version": "0.5.3",
|
||||
"scripts": {
|
||||
"build": "mkdir -p dist && cp -f index.html dist/index.html && webpack --config ./webpack.config.js",
|
||||
"release": "yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
|
||||
},
|
||||
"dependencies": {
|
||||
"aphrodite": "1.2.5",
|
||||
"classnames": "^2.2.5",
|
||||
"d3-scale-chromatic": "^1.2.0",
|
||||
"emotion": "^9.0.2",
|
||||
"fela": "^6.1.4",
|
||||
"glamor": "2.20.40",
|
||||
"radium": "^0.22.1",
|
||||
"react": "^16.2.0",
|
||||
"react-component-benchmark": "^0.0.4",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-fela": "^7.0.1",
|
||||
"react-jss": "^8.3.3",
|
||||
"react-native-web": "0.5.3",
|
||||
"reactxp": "^1.0.0",
|
||||
"styled-components": "^3.2.0",
|
||||
"styled-jsx": "^2.2.5",
|
||||
"styletron-engine-atomic": "^1.0.0",
|
||||
"styletron-react": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-plugin-react-native-web": "0.5.3",
|
||||
"css-loader": "^0.28.10",
|
||||
"style-loader": "^0.20.2",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-bundle-analyzer": "^2.9.2"
|
||||
}
|
||||
}
|
||||
298
packages/benchmarks/src/app/App.js
Normal file
298
packages/benchmarks/src/app/App.js
Normal file
@@ -0,0 +1,298 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
import Benchmark from './Benchmark';
|
||||
import { Picker, StyleSheet, ScrollView, TouchableOpacity, View } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import Button from './Button';
|
||||
import { IconClear, IconEye } from './Icons';
|
||||
import ReportCard from './ReportCard';
|
||||
import Text from './Text';
|
||||
import Layout from './Layout';
|
||||
import { colors } from './theme';
|
||||
|
||||
const Overlay = () => <View style={[StyleSheet.absoluteFill, { zIndex: 2 }]} />;
|
||||
|
||||
export default class App extends Component {
|
||||
static displayName = '@app/App';
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
const currentBenchmarkName = Object.keys(props.tests)[0];
|
||||
this.state = {
|
||||
currentBenchmarkName,
|
||||
currentLibraryName: 'react-native-web',
|
||||
status: 'idle',
|
||||
results: []
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tests } = this.props;
|
||||
const { currentBenchmarkName, status, currentLibraryName, results } = this.state;
|
||||
const currentImplementation = tests[currentBenchmarkName][currentLibraryName];
|
||||
const { Component, Provider, getComponentProps, sampleCount } = currentImplementation;
|
||||
|
||||
return (
|
||||
<Layout
|
||||
actionPanel={
|
||||
<View>
|
||||
<View style={styles.pickers}>
|
||||
<View style={styles.pickerContainer}>
|
||||
<Text style={styles.pickerTitle}>Library</Text>
|
||||
<Text style={{ fontWeight: 'bold' }}>{currentLibraryName}</Text>
|
||||
|
||||
<Picker
|
||||
enabled={status !== 'running'}
|
||||
onValueChange={this._handleChangeLibrary}
|
||||
selectedValue={currentLibraryName}
|
||||
style={styles.picker}
|
||||
>
|
||||
{Object.keys(tests[currentBenchmarkName]).map(libraryName => (
|
||||
<Picker.Item key={libraryName} label={libraryName} value={libraryName} />
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
<View style={{ width: 1, backgroundColor: colors.fadedGray }} />
|
||||
<View style={styles.pickerContainer}>
|
||||
<Text style={styles.pickerTitle}>Benchmark</Text>
|
||||
<Text>{currentBenchmarkName}</Text>
|
||||
<Picker
|
||||
enabled={status !== 'running'}
|
||||
onValueChange={this._handleChangeBenchmark}
|
||||
selectedValue={currentBenchmarkName}
|
||||
style={styles.picker}
|
||||
>
|
||||
{Object.keys(tests).map(test => (
|
||||
<Picker.Item key={test} label={test} value={test} />
|
||||
))}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={{ flexDirection: 'row', height: 50 }}>
|
||||
<View style={styles.grow}>
|
||||
<Button
|
||||
onPress={this._handleStart}
|
||||
style={styles.button}
|
||||
title={status === 'running' ? 'Running…' : 'Run'}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
listPanel={
|
||||
<View style={styles.listPanel}>
|
||||
<View style={styles.grow}>
|
||||
<View style={styles.listBar}>
|
||||
<View style={styles.iconClearContainer}>
|
||||
<TouchableOpacity onPress={this._handleClear}>
|
||||
<IconClear />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<ScrollView ref={this._setScrollRef} style={styles.grow}>
|
||||
{results.map((r, i) => (
|
||||
<ReportCard
|
||||
benchmarkName={r.benchmarkName}
|
||||
key={i}
|
||||
libraryName={r.libraryName}
|
||||
libraryVersion={r.libraryVersion}
|
||||
mean={r.mean}
|
||||
meanLayout={r.meanLayout}
|
||||
meanScripting={r.meanScripting}
|
||||
runTime={r.runTime}
|
||||
sampleCount={r.sampleCount}
|
||||
stdDev={r.stdDev}
|
||||
/>
|
||||
))}
|
||||
{status === 'running' ? (
|
||||
<ReportCard
|
||||
benchmarkName={currentBenchmarkName}
|
||||
libraryName={currentLibraryName}
|
||||
/>
|
||||
) : null}
|
||||
</ScrollView>
|
||||
</View>
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
viewPanel={
|
||||
<View style={styles.viewPanel}>
|
||||
<View style={styles.iconEyeContainer}>
|
||||
<TouchableOpacity onPress={this._handleVisuallyHideBenchmark}>
|
||||
<IconEye style={styles.iconEye} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<Provider>
|
||||
{status === 'running' ? (
|
||||
<React.Fragment>
|
||||
<View ref={this._setBenchWrapperRef}>
|
||||
<Benchmark
|
||||
component={Component}
|
||||
forceLayout={true}
|
||||
getComponentProps={getComponentProps}
|
||||
onComplete={this._createHandleComplete({
|
||||
sampleCount,
|
||||
benchmarkName: currentBenchmarkName,
|
||||
libraryName: currentLibraryName
|
||||
})}
|
||||
ref={this._setBenchRef}
|
||||
sampleCount={sampleCount}
|
||||
timeout={20000}
|
||||
type={Component.benchmarkType}
|
||||
/>
|
||||
</View>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Component {...getComponentProps({ cycle: 10 })} />
|
||||
)}
|
||||
</Provider>
|
||||
|
||||
{status === 'running' ? <Overlay /> : null}
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_handleChangeBenchmark = value => {
|
||||
this.setState(() => ({ currentBenchmarkName: value }));
|
||||
};
|
||||
|
||||
_handleChangeLibrary = value => {
|
||||
this.setState(() => ({ currentLibraryName: value }));
|
||||
};
|
||||
|
||||
_handleStart = () => {
|
||||
this.setState(
|
||||
() => ({ status: 'running' }),
|
||||
() => {
|
||||
if (this._shouldHideBenchmark && this._benchWrapperRef) {
|
||||
this._benchWrapperRef.setNativeProps({ style: { opacity: 0 } });
|
||||
}
|
||||
this._benchmarkRef.start();
|
||||
this._scrollToEnd();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// hide the benchmark as it is performed (no flashing on screen)
|
||||
_handleVisuallyHideBenchmark = () => {
|
||||
this._shouldHideBenchmark = !this._shouldHideBenchmark;
|
||||
if (this._benchWrapperRef) {
|
||||
this._benchWrapperRef.setNativeProps({
|
||||
style: { opacity: this._shouldHideBenchmark ? 0 : 1 }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_createHandleComplete = ({ benchmarkName, libraryName, sampleCount }) => results => {
|
||||
this.setState(
|
||||
state => ({
|
||||
results: state.results.concat([
|
||||
{
|
||||
...results,
|
||||
benchmarkName,
|
||||
libraryName,
|
||||
libraryVersion: this.props.tests[benchmarkName][libraryName].version
|
||||
}
|
||||
]),
|
||||
status: 'complete'
|
||||
}),
|
||||
this._scrollToEnd
|
||||
);
|
||||
// console.log(results);
|
||||
// console.log(results.samples.map(sample => sample.elapsed.toFixed(1)).join('\n'));
|
||||
};
|
||||
|
||||
_handleClear = () => {
|
||||
this.setState(() => ({ results: [] }));
|
||||
};
|
||||
|
||||
_setBenchRef = ref => {
|
||||
this._benchmarkRef = ref;
|
||||
};
|
||||
|
||||
_setBenchWrapperRef = ref => {
|
||||
this._benchWrapperRef = ref;
|
||||
};
|
||||
|
||||
_setScrollRef = ref => {
|
||||
this._scrollRef = ref;
|
||||
};
|
||||
|
||||
// scroll the most recent result into view
|
||||
_scrollToEnd = () => {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (this._scrollRef) {
|
||||
this._scrollRef.scrollToEnd();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
viewPanel: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'black'
|
||||
},
|
||||
iconEye: {
|
||||
color: 'white',
|
||||
height: 32
|
||||
},
|
||||
iconEyeContainer: {
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
right: 10,
|
||||
zIndex: 1
|
||||
},
|
||||
iconClearContainer: {
|
||||
height: '100%',
|
||||
marginLeft: 5
|
||||
},
|
||||
grow: {
|
||||
flex: 1
|
||||
},
|
||||
listPanel: {
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
marginHorizontal: 'auto'
|
||||
},
|
||||
listBar: {
|
||||
padding: 5,
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
backgroundColor: colors.fadedGray,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.mediumGray,
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
pickers: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
pickerContainer: {
|
||||
flex: 1,
|
||||
padding: 5
|
||||
},
|
||||
pickerTitle: {
|
||||
fontSize: 12,
|
||||
color: colors.deepGray
|
||||
},
|
||||
picker: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
appearance: 'none',
|
||||
opacity: 0,
|
||||
width: '100%'
|
||||
},
|
||||
button: {
|
||||
borderRadius: 0,
|
||||
fontSize: 32,
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
247
packages/benchmarks/src/app/Benchmark/index.js
Normal file
247
packages/benchmarks/src/app/Benchmark/index.js
Normal file
@@ -0,0 +1,247 @@
|
||||
/* global $Values */
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
import * as Timing from './timing';
|
||||
import React, { Component } from 'react';
|
||||
import { getMean, getMedian, getStdDev } from './math';
|
||||
|
||||
import type { BenchResultsType, FullSampleTimingType, SampleTimingType } from './types';
|
||||
|
||||
export const BenchmarkType = {
|
||||
MOUNT: 'mount',
|
||||
UPDATE: 'update',
|
||||
UNMOUNT: 'unmount'
|
||||
};
|
||||
|
||||
const shouldRender = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
|
||||
switch (type) {
|
||||
// Render every odd iteration (first, third, etc)
|
||||
// Mounts and unmounts the component
|
||||
case BenchmarkType.MOUNT:
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return !((cycle + 1) % 2);
|
||||
// Render every iteration (updates previously rendered module)
|
||||
case BenchmarkType.UPDATE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const shouldRecord = (cycle: number, type: $Values<typeof BenchmarkType>): boolean => {
|
||||
switch (type) {
|
||||
// Record every odd iteration (when mounted: first, third, etc)
|
||||
case BenchmarkType.MOUNT:
|
||||
return !((cycle + 1) % 2);
|
||||
// Record every iteration
|
||||
case BenchmarkType.UPDATE:
|
||||
return true;
|
||||
// Record every even iteration (when unmounted)
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return !(cycle % 2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const isDone = (
|
||||
cycle: number,
|
||||
sampleCount: number,
|
||||
type: $Values<typeof BenchmarkType>
|
||||
): boolean => {
|
||||
switch (type) {
|
||||
case BenchmarkType.MOUNT:
|
||||
return cycle >= sampleCount * 2 - 1;
|
||||
case BenchmarkType.UPDATE:
|
||||
return cycle >= sampleCount - 1;
|
||||
case BenchmarkType.UNMOUNT:
|
||||
return cycle >= sampleCount * 2;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const sortNumbers = (a: number, b: number): number => a - b;
|
||||
|
||||
type BenchmarkPropsType = {
|
||||
component: typeof React.Component,
|
||||
forceLayout?: boolean,
|
||||
getComponentProps: Function,
|
||||
onComplete: (x: BenchResultsType) => void,
|
||||
sampleCount: number,
|
||||
timeout: number,
|
||||
type: $Values<typeof BenchmarkType>
|
||||
};
|
||||
|
||||
type BenchmarkStateType = {
|
||||
componentProps: Object,
|
||||
cycle: number,
|
||||
running: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Benchmark
|
||||
* TODO: documentation
|
||||
*/
|
||||
export default class Benchmark extends Component<BenchmarkPropsType, BenchmarkStateType> {
|
||||
_raf: ?Function;
|
||||
_startTime: number;
|
||||
_samples: Array<SampleTimingType>;
|
||||
|
||||
static displayName = 'Benchmark';
|
||||
|
||||
static defaultProps = {
|
||||
sampleCount: 50,
|
||||
timeout: 10000, // 10 seconds
|
||||
type: BenchmarkType.MOUNT
|
||||
};
|
||||
|
||||
static Type = BenchmarkType;
|
||||
|
||||
constructor(props: BenchmarkPropsType, context?: {}) {
|
||||
super(props, context);
|
||||
const cycle = 0;
|
||||
const componentProps = props.getComponentProps({ cycle });
|
||||
this.state = {
|
||||
componentProps,
|
||||
cycle,
|
||||
running: false
|
||||
};
|
||||
this._startTime = 0;
|
||||
this._samples = [];
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: BenchmarkPropsType) {
|
||||
if (nextProps) {
|
||||
this.setState(state => ({ componentProps: nextProps.getComponentProps(state.cycle) }));
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps: BenchmarkPropsType, nextState: BenchmarkStateType) {
|
||||
if (nextState.running && !this.state.running) {
|
||||
this._startTime = Timing.now();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { forceLayout, sampleCount, timeout, type } = this.props;
|
||||
const { cycle, running } = this.state;
|
||||
|
||||
if (running && shouldRecord(cycle, type)) {
|
||||
this._samples[cycle].scriptingEnd = Timing.now();
|
||||
|
||||
// force style recalc that would otherwise happen before the next frame
|
||||
if (forceLayout) {
|
||||
this._samples[cycle].layoutStart = Timing.now();
|
||||
if (document.body) {
|
||||
document.body.offsetWidth;
|
||||
}
|
||||
this._samples[cycle].layoutEnd = Timing.now();
|
||||
}
|
||||
}
|
||||
|
||||
if (running) {
|
||||
const now = Timing.now();
|
||||
if (!isDone(cycle, sampleCount, type) && now - this._startTime < timeout) {
|
||||
this._handleCycleComplete();
|
||||
} else {
|
||||
this._handleComplete(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this._raf) {
|
||||
window.cancelAnimationFrame(this._raf);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { component: Component, type } = this.props;
|
||||
const { componentProps, cycle, running } = this.state;
|
||||
if (running && shouldRecord(cycle, type)) {
|
||||
this._samples[cycle] = { scriptingStart: Timing.now() };
|
||||
}
|
||||
return running && shouldRender(cycle, type) ? <Component {...componentProps} /> : null;
|
||||
}
|
||||
|
||||
start() {
|
||||
this._samples = [];
|
||||
this.setState(() => ({ running: true, cycle: 0 }));
|
||||
}
|
||||
|
||||
_handleCycleComplete() {
|
||||
const { getComponentProps, type } = this.props;
|
||||
const { cycle } = this.state;
|
||||
|
||||
let componentProps;
|
||||
if (getComponentProps) {
|
||||
// Calculate the component props outside of the time recording (render)
|
||||
// so that it doesn't skew results
|
||||
componentProps = getComponentProps({ cycle });
|
||||
// make sure props always change for update tests
|
||||
if (type === BenchmarkType.UPDATE) {
|
||||
componentProps['data-test'] = cycle;
|
||||
}
|
||||
}
|
||||
|
||||
this._raf = window.requestAnimationFrame(() => {
|
||||
this.setState((state: BenchmarkStateType) => ({
|
||||
cycle: state.cycle + 1,
|
||||
componentProps
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
getSamples(): Array<FullSampleTimingType> {
|
||||
return this._samples.reduce(
|
||||
(
|
||||
memo: Array<FullSampleTimingType>,
|
||||
{ scriptingStart, scriptingEnd, layoutStart, layoutEnd }: SampleTimingType
|
||||
): Array<FullSampleTimingType> => {
|
||||
memo.push({
|
||||
start: scriptingStart,
|
||||
end: layoutEnd || scriptingEnd || 0,
|
||||
scriptingStart,
|
||||
scriptingEnd: scriptingEnd || 0,
|
||||
layoutStart,
|
||||
layoutEnd
|
||||
});
|
||||
return memo;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
_handleComplete(endTime: number) {
|
||||
const { onComplete } = this.props;
|
||||
const samples = this.getSamples();
|
||||
|
||||
this.setState(() => ({ running: false, cycle: 0 }));
|
||||
|
||||
const runTime = endTime - this._startTime;
|
||||
const sortedElapsedTimes = samples.map(({ start, end }) => end - start).sort(sortNumbers);
|
||||
const sortedScriptingElapsedTimes = samples
|
||||
.map(({ scriptingStart, scriptingEnd }) => scriptingEnd - scriptingStart)
|
||||
.sort(sortNumbers);
|
||||
const sortedLayoutElapsedTimes = samples
|
||||
.map(({ layoutStart, layoutEnd }) => (layoutEnd || 0) - (layoutStart || 0))
|
||||
.sort(sortNumbers);
|
||||
|
||||
onComplete({
|
||||
startTime: this._startTime,
|
||||
endTime,
|
||||
runTime,
|
||||
sampleCount: samples.length,
|
||||
samples: samples,
|
||||
max: sortedElapsedTimes[sortedElapsedTimes.length - 1],
|
||||
min: sortedElapsedTimes[0],
|
||||
median: getMedian(sortedElapsedTimes),
|
||||
mean: getMean(sortedElapsedTimes),
|
||||
stdDev: getStdDev(sortedElapsedTimes),
|
||||
meanLayout: getMean(sortedLayoutElapsedTimes),
|
||||
meanScripting: getMean(sortedScriptingElapsedTimes)
|
||||
});
|
||||
}
|
||||
}
|
||||
27
packages/benchmarks/src/app/Benchmark/math.js
Normal file
27
packages/benchmarks/src/app/Benchmark/math.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
type ValuesType = Array<number>;
|
||||
|
||||
export const getStdDev = (values: ValuesType): number => {
|
||||
const avg = getMean(values);
|
||||
|
||||
const squareDiffs = values.map((value: number) => {
|
||||
const diff = value - avg;
|
||||
return diff * diff;
|
||||
});
|
||||
|
||||
return Math.sqrt(getMean(squareDiffs));
|
||||
};
|
||||
|
||||
export const getMean = (values: ValuesType): number => {
|
||||
const sum = values.reduce((sum: number, value: number) => sum + value, 0);
|
||||
return sum / values.length;
|
||||
};
|
||||
|
||||
export const getMedian = (values: ValuesType): number => {
|
||||
if (values.length === 1) {
|
||||
return values[0];
|
||||
}
|
||||
|
||||
const numbers = values.sort((a: number, b: number) => a - b);
|
||||
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
|
||||
};
|
||||
18
packages/benchmarks/src/app/Benchmark/timing.js
Normal file
18
packages/benchmarks/src/app/Benchmark/timing.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
|
||||
const NS_PER_MS = 1e6;
|
||||
const MS_PER_S = 1e3;
|
||||
|
||||
// Returns a high resolution time (if possible) in milliseconds
|
||||
export function now(): number {
|
||||
if (window && window.performance) {
|
||||
return window.performance.now();
|
||||
} else if (process && process.hrtime) {
|
||||
const [seconds, nanoseconds] = process.hrtime();
|
||||
const secInMS = seconds * MS_PER_S;
|
||||
const nSecInMS = nanoseconds / NS_PER_MS;
|
||||
return secInMS + nSecInMS;
|
||||
} else {
|
||||
return Date.now();
|
||||
}
|
||||
}
|
||||
31
packages/benchmarks/src/app/Benchmark/types.js
Normal file
31
packages/benchmarks/src/app/Benchmark/types.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
export type BenchResultsType = {
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
runTime: number,
|
||||
sampleCount: number,
|
||||
samples: Array<FullSampleTimingType>,
|
||||
max: number,
|
||||
min: number,
|
||||
median: number,
|
||||
mean: number,
|
||||
stdDev: number
|
||||
};
|
||||
|
||||
export type SampleTimingType = {
|
||||
scriptingStart: number,
|
||||
scriptingEnd?: number,
|
||||
layoutStart?: number,
|
||||
layoutEnd?: number
|
||||
};
|
||||
|
||||
export type FullSampleTimingType = {
|
||||
start: number,
|
||||
end: number,
|
||||
scriptingStart: number,
|
||||
scriptingEnd: number,
|
||||
layoutStart?: number,
|
||||
layoutEnd?: number
|
||||
};
|
||||
70
packages/benchmarks/src/app/Button.js
Normal file
70
packages/benchmarks/src/app/Button.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { ColorPropType, StyleSheet, TouchableHighlight, Text } from 'react-native';
|
||||
import React, { Component } from 'react';
|
||||
import { bool, func, string } from 'prop-types';
|
||||
|
||||
export default class Button extends Component<*> {
|
||||
static displayName = '@app/Button';
|
||||
|
||||
static propTypes = {
|
||||
accessibilityLabel: string,
|
||||
color: ColorPropType,
|
||||
disabled: bool,
|
||||
onPress: func.isRequired,
|
||||
style: TouchableHighlight.propTypes.style,
|
||||
testID: string,
|
||||
textStyle: Text.propTypes.style,
|
||||
title: string.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
accessibilityLabel,
|
||||
color,
|
||||
disabled,
|
||||
onPress,
|
||||
style,
|
||||
textStyle,
|
||||
testID,
|
||||
title
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityRole="button"
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
styles.button,
|
||||
style,
|
||||
color && { backgroundColor: color },
|
||||
disabled && styles.buttonDisabled
|
||||
]}
|
||||
testID={testID}
|
||||
>
|
||||
<Text style={[styles.text, textStyle, disabled && styles.textDisabled]}>{title}</Text>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
backgroundColor: '#2196F3',
|
||||
borderRadius: 0,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
text: {
|
||||
color: '#fff',
|
||||
fontWeight: '500',
|
||||
padding: 8,
|
||||
textAlign: 'center',
|
||||
textTransform: 'uppercase'
|
||||
},
|
||||
buttonDisabled: {
|
||||
backgroundColor: '#dfdfdf'
|
||||
},
|
||||
textDisabled: {
|
||||
color: '#a1a1a1'
|
||||
}
|
||||
});
|
||||
55
packages/benchmarks/src/app/Icons.js
Normal file
55
packages/benchmarks/src/app/Icons.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { createElement, StyleSheet, Text } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
display: 'inline-block',
|
||||
fill: 'currentcolor',
|
||||
height: '1.25em',
|
||||
maxWidth: '100%',
|
||||
position: 'relative',
|
||||
userSelect: 'none',
|
||||
textAlignVertical: 'text-bottom'
|
||||
}
|
||||
});
|
||||
|
||||
const createIcon = children => {
|
||||
const Icon = props =>
|
||||
createElement(
|
||||
'svg',
|
||||
{
|
||||
style: StyleSheet.compose(styles.root, props.style),
|
||||
width: 24,
|
||||
height: 24,
|
||||
viewBox: '0 0 24 24'
|
||||
},
|
||||
children
|
||||
);
|
||||
Icon.propTypes = {
|
||||
style: Text.propTypes.style
|
||||
};
|
||||
return Icon;
|
||||
};
|
||||
|
||||
export const IconClear = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M12 1.25C6.072 1.25 1.25 6.072 1.25 12S6.072 22.75 12 22.75 22.75 17.928 22.75 12 17.928 1.25 12 1.25zm0 1.5c2.28 0 4.368.834 5.982 2.207L4.957 17.982C3.584 16.368 2.75 14.282 2.75 12c0-5.1 4.15-9.25 9.25-9.25zm0 18.5c-2.28 0-4.368-.834-5.982-2.207L19.043 6.018c1.373 1.614 2.207 3.7 2.207 5.982 0 5.1-4.15 9.25-9.25 9.25z" />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export const IconEye = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M14.548 11.634c-1.207 0-2.188-.98-2.188-2.188 0-.664.302-1.25.77-1.653-.363-.097-.736-.165-1.13-.165-2.416 0-4.375 1.96-4.375 4.376S9.585 16.38 12 16.38c2.418 0 4.377-1.96 4.377-4.376 0-.4-.07-.78-.17-1.146-.402.47-.992.776-1.66.776z" />
|
||||
<path d="M12 19.79c-7.228 0-10.12-6.724-10.24-7.01-.254-.466-.254-1.105.035-1.642C1.88 10.923 4.772 4.2 12 4.2s10.12 6.723 10.24 7.01c.254.465.254 1.104-.035 1.64-.085.216-2.977 6.94-10.205 6.94zm0-14c-6.154 0-8.668 5.787-8.772 6.033-.068.135-.068.208-.033.273.137.316 2.65 6.104 8.805 6.104 6.18 0 8.747-5.973 8.772-6.033.07-.136.07-.21.034-.274-.138-.316-2.652-6.103-8.806-6.103z" />
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
export const IconCopy = createIcon(
|
||||
<Fragment>
|
||||
<path d="M0 0h24v24H0z" id="bounds" opacity="0" />
|
||||
<path d="M11.47 14.53c.146.146.338.22.53.22s.384-.073.53-.22l5-5c.293-.293.293-.768 0-1.06s-.768-.294-1.06 0l-3.72 3.72V2c0-.414-.337-.75-.75-.75s-.75.336-.75.75v10.19L7.53 8.47c-.293-.293-.768-.293-1.06 0s-.294.768 0 1.06l5 5z" />
|
||||
<path d="M21.25 13.25c-.414 0-.75.336-.75.75v5.652c0 .437-.355.792-.792.792H4.292c-.437 0-.792-.355-.792-.792V14c0-.414-.336-.75-.75-.75S2 13.586 2 14v5.652c0 1.264 1.028 2.292 2.292 2.292h15.416c1.264 0 2.292-1.028 2.292-2.292V14c0-.414-.336-.75-.75-.75z" />
|
||||
</Fragment>
|
||||
);
|
||||
68
packages/benchmarks/src/app/Layout.js
Normal file
68
packages/benchmarks/src/app/Layout.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { colors } from './theme';
|
||||
import { element } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
export default class Layout extends Component {
|
||||
static propTypes = {
|
||||
actionPanel: element,
|
||||
listPanel: element,
|
||||
viewPanel: element
|
||||
};
|
||||
|
||||
state = {
|
||||
widescreen: false
|
||||
};
|
||||
|
||||
render() {
|
||||
const { viewPanel, actionPanel, listPanel } = this.props;
|
||||
const { widescreen } = this.state;
|
||||
return (
|
||||
<View onLayout={this._handleLayout} style={[styles.root, widescreen && styles.row]}>
|
||||
<View style={[widescreen ? styles.grow : styles.stackPanel, styles.layer]}>
|
||||
{viewPanel}
|
||||
</View>
|
||||
<View style={styles.grow}>
|
||||
<View style={[styles.grow, styles.layer]}>{listPanel}</View>
|
||||
<View style={styles.divider} />
|
||||
<View style={styles.layer}>{actionPanel}</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleLayout = ({ nativeEvent }) => {
|
||||
const { layout } = nativeEvent;
|
||||
const { width } = layout;
|
||||
if (width >= 740) {
|
||||
this.setState(() => ({ widescreen: true }));
|
||||
} else {
|
||||
this.setState(() => ({ widescreen: false }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
height: '100%'
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
divider: {
|
||||
height: 10,
|
||||
backgroundColor: colors.fadedGray,
|
||||
borderBottomWidth: 1,
|
||||
borderTopWidth: 1,
|
||||
borderColor: colors.mediumGray
|
||||
},
|
||||
grow: {
|
||||
flex: 1
|
||||
},
|
||||
stackPanel: {
|
||||
height: '33.33%'
|
||||
},
|
||||
layer: {
|
||||
transform: [{ translateZ: '0' }]
|
||||
}
|
||||
});
|
||||
83
packages/benchmarks/src/app/ReportCard.js
Normal file
83
packages/benchmarks/src/app/ReportCard.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import Text from './Text';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
const fmt = (time: number) => {
|
||||
const i = Number(Math.round(time + 'e2') + 'e-2').toFixed(2);
|
||||
return 10 / i > 1 ? `0${i}` : i;
|
||||
};
|
||||
|
||||
class ReportCard extends React.PureComponent {
|
||||
render() {
|
||||
const {
|
||||
benchmarkName,
|
||||
libraryName,
|
||||
sampleCount,
|
||||
mean,
|
||||
meanLayout,
|
||||
meanScripting,
|
||||
stdDev,
|
||||
libraryVersion
|
||||
} = this.props;
|
||||
|
||||
const sampleCountText = sampleCount != null ? `(${sampleCount})` : '';
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<View style={styles.left}>
|
||||
<Text numberOfLines={1} style={styles.bold}>
|
||||
{`${libraryName}${libraryVersion ? '@' + libraryVersion : ''}`}
|
||||
</Text>
|
||||
<Text numberOfLines={1}>
|
||||
{benchmarkName} {sampleCountText}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.right}>
|
||||
{mean ? (
|
||||
<Fragment>
|
||||
<Text style={[styles.bold, styles.monoFont]}>
|
||||
{fmt(mean)} ±{fmt(stdDev)} ms
|
||||
</Text>
|
||||
<Text style={[styles.smallText, styles.monoFont]}>
|
||||
(S/L) {fmt(meanScripting)}/{fmt(meanLayout)} ms
|
||||
</Text>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Text style={styles.bold}>In progress…</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
padding: 5,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#eee'
|
||||
},
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
smallText: { fontSize: 12 },
|
||||
monoFont: {
|
||||
fontFamily: 'monospace'
|
||||
},
|
||||
centerText: {
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
left: {
|
||||
width: '50%'
|
||||
},
|
||||
right: {
|
||||
flex: 1,
|
||||
alignItems: 'flex-end'
|
||||
}
|
||||
});
|
||||
|
||||
export default ReportCard;
|
||||
30
packages/benchmarks/src/app/Text.js
Normal file
30
packages/benchmarks/src/app/Text.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
import { bool } from 'prop-types';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { colors } from './theme';
|
||||
|
||||
class AppText extends React.Component {
|
||||
static displayName = '@app/Text';
|
||||
|
||||
static contextTypes = {
|
||||
isInAParentText: bool
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style, ...rest } = this.props;
|
||||
const { isInAParentText } = this.context;
|
||||
return <Text {...rest} style={[!isInAParentText && styles.baseText, style]} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
baseText: {
|
||||
color: colors.textBlack,
|
||||
fontSize: '1rem',
|
||||
lineHeight: '1.3125em'
|
||||
}
|
||||
});
|
||||
|
||||
export default AppText;
|
||||
101
packages/benchmarks/src/app/theme.js
Normal file
101
packages/benchmarks/src/app/theme.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
const baseFontSize = 14;
|
||||
const baseUnit = 1.3125;
|
||||
|
||||
const createPlatformLength = multiplier =>
|
||||
Platform.select({ web: `${multiplier}rem`, default: multiplier * baseFontSize });
|
||||
|
||||
/**
|
||||
* Exported variables
|
||||
*/
|
||||
|
||||
export const borderRadii = {
|
||||
normal: Platform.select({ web: '0.35rem', default: 5 }),
|
||||
infinite: '9999px'
|
||||
};
|
||||
|
||||
export const breakpoints = {
|
||||
small: 360,
|
||||
medium: 600,
|
||||
large: 800,
|
||||
xLarge: 1100
|
||||
};
|
||||
|
||||
/**
|
||||
* Color palette
|
||||
* DO NOT add new colors unless they are part of @design's color palette.
|
||||
* DO NOT use colors that are not specified here.
|
||||
* source: go/uicolors
|
||||
*/
|
||||
export const colors = {
|
||||
// Primary
|
||||
blue: '#1DA1F2',
|
||||
purple: '#794BC4',
|
||||
green: '#17BF63',
|
||||
yellow: '#FFAD1F',
|
||||
orange: '#F45D22',
|
||||
red: '#E0245E',
|
||||
// Text and interface grays
|
||||
textBlack: '#14171A',
|
||||
deepGray: '#657786',
|
||||
mediumGray: '#AAB8C2',
|
||||
lightGray: '#CCD6DD',
|
||||
fadedGray: '#E6ECF0',
|
||||
faintGray: '#F5F8FA',
|
||||
white: '#FFF',
|
||||
textBlue: '#1B95E0'
|
||||
};
|
||||
|
||||
export const fontFamilies = {
|
||||
normal: 'System',
|
||||
japan: Platform.select({
|
||||
web:
|
||||
'Arial, "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic", sans-serif',
|
||||
default: 'System'
|
||||
}),
|
||||
rtl: Platform.select({ web: 'Tahoma, Arial, sans-serif', default: 'System' })
|
||||
};
|
||||
|
||||
export const fontSizes = {
|
||||
// font scale
|
||||
small: createPlatformLength(0.85),
|
||||
normal: createPlatformLength(1),
|
||||
large: createPlatformLength(1.25),
|
||||
xLarge: createPlatformLength(1.5),
|
||||
jumbo: createPlatformLength(2)
|
||||
};
|
||||
|
||||
export const lineHeight = Platform.select({ web: `${baseUnit}` });
|
||||
|
||||
export const spaces = {
|
||||
// This set of space variables should be used for margin, padding
|
||||
micro: createPlatformLength(baseUnit * 0.125),
|
||||
xxSmall: createPlatformLength(baseUnit * 0.25),
|
||||
xSmall: createPlatformLength(baseUnit * 0.5),
|
||||
small: createPlatformLength(baseUnit * 0.75),
|
||||
medium: createPlatformLength(baseUnit),
|
||||
large: createPlatformLength(baseUnit * 1.5),
|
||||
xLarge: createPlatformLength(baseUnit * 2),
|
||||
xxLarge: createPlatformLength(baseUnit * 2.5),
|
||||
jumbo: createPlatformLength(baseUnit * 3)
|
||||
};
|
||||
|
||||
// On web, change the root font-size at specific breakpoints to scale the UI
|
||||
// for larger viewports.
|
||||
if (Platform.OS === 'web' && canUseDOM) {
|
||||
const { medium, large } = breakpoints;
|
||||
const htmlElement = document.documentElement;
|
||||
const setFontSize = width => {
|
||||
const fontSize = width > medium ? (width > large ? '18px' : '17px') : '16px';
|
||||
if (htmlElement) {
|
||||
htmlElement.style.fontSize = fontSize;
|
||||
}
|
||||
};
|
||||
|
||||
setFontSize(Dimensions.get('window').width);
|
||||
Dimensions.addEventListener('change', dimensions => {
|
||||
setFontSize(dimensions.window.width);
|
||||
});
|
||||
}
|
||||
90
packages/benchmarks/src/cases/SierpinskiTriangle.js
Normal file
90
packages/benchmarks/src/cases/SierpinskiTriangle.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { BenchmarkType } from 'react-component-benchmark';
|
||||
import { number, object } from 'prop-types';
|
||||
import React from 'react';
|
||||
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
|
||||
|
||||
const targetSize = 10;
|
||||
|
||||
class SierpinskiTriangle extends React.Component {
|
||||
static displayName = 'SierpinskiTriangle';
|
||||
|
||||
static benchmarkType = BenchmarkType.UPDATE;
|
||||
|
||||
static propTypes = {
|
||||
components: object,
|
||||
depth: number,
|
||||
renderCount: number,
|
||||
s: number,
|
||||
x: number,
|
||||
y: number
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
depth: 0,
|
||||
renderCount: 0
|
||||
};
|
||||
|
||||
render() {
|
||||
const { components, x, y, depth, renderCount } = this.props;
|
||||
let { s } = this.props;
|
||||
const { Dot } = components;
|
||||
|
||||
if (Dot) {
|
||||
if (s <= targetSize) {
|
||||
let fn;
|
||||
switch (depth) {
|
||||
case 1:
|
||||
fn = interpolatePurples;
|
||||
break;
|
||||
case 2:
|
||||
fn = interpolateBuPu;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
fn = interpolateRdPu;
|
||||
}
|
||||
|
||||
// introduce randomness to ensure that repeated runs don't produce the same colors
|
||||
const color = fn(renderCount * Math.random() / 20);
|
||||
return (
|
||||
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
|
||||
);
|
||||
}
|
||||
|
||||
s /= 2;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={1}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x}
|
||||
y={y - s / 2}
|
||||
/>
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={2}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x - s}
|
||||
y={y + s / 2}
|
||||
/>
|
||||
<SierpinskiTriangle
|
||||
components={components}
|
||||
depth={3}
|
||||
renderCount={renderCount}
|
||||
s={s}
|
||||
x={x + s}
|
||||
y={y + s / 2}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
} else {
|
||||
return <span style={{ color: 'white' }}>No implementation available</span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SierpinskiTriangle;
|
||||
45
packages/benchmarks/src/cases/Tree.js
Normal file
45
packages/benchmarks/src/cases/Tree.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { BenchmarkType } from 'react-component-benchmark';
|
||||
import { number, object } from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class Tree extends Component {
|
||||
static displayName = 'Tree';
|
||||
|
||||
static benchmarkType = BenchmarkType.MOUNT;
|
||||
|
||||
static propTypes = {
|
||||
breadth: number.isRequired,
|
||||
components: object,
|
||||
depth: number.isRequired,
|
||||
id: number.isRequired,
|
||||
wrap: number.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
const { breadth, components, depth, id, wrap } = this.props;
|
||||
const { Box } = components;
|
||||
|
||||
let result = (
|
||||
<Box color={id % 3} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
|
||||
{depth === 0 && <Box color={id % 3 + 3} fixed />}
|
||||
{depth !== 0 &&
|
||||
Array.from({ length: breadth }).map((el, i) => (
|
||||
<Tree
|
||||
breadth={breadth}
|
||||
components={components}
|
||||
depth={depth - 1}
|
||||
id={i}
|
||||
key={i}
|
||||
wrap={wrap}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
for (let i = 0; i < wrap; i++) {
|
||||
result = <Box>{result}</Box>;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default Tree;
|
||||
36
packages/benchmarks/src/impl.js
Normal file
36
packages/benchmarks/src/impl.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { type Component } from 'react';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const context = require.context('./implementations/', true, /index\.js$/);
|
||||
const { dependencies } = packageJson;
|
||||
|
||||
type ComponentsType = {
|
||||
Box: Component,
|
||||
Dot: Component,
|
||||
Provider: Component,
|
||||
View: Component
|
||||
};
|
||||
|
||||
type ImplementationType = {
|
||||
components: ComponentsType,
|
||||
name: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
const toImplementations = (context: Object): Array<ImplementationType> =>
|
||||
context.keys().map(path => {
|
||||
const components = context(path).default;
|
||||
const name = path.split('/')[1];
|
||||
const version = dependencies[name] || '';
|
||||
return { components, name, version };
|
||||
});
|
||||
|
||||
const toObject = (impls: Array<ImplementationType>): Object =>
|
||||
impls.reduce((acc, impl) => {
|
||||
acc[impl.name] = impl;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const map = toObject(toImplementations(context));
|
||||
|
||||
export default map;
|
||||
50
packages/benchmarks/src/implementations/aphrodite/Box.js
Normal file
50
packages/benchmarks/src/implementations/aphrodite/Box.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import View from './View';
|
||||
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: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
});
|
||||
|
||||
export default Box;
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
31
packages/benchmarks/src/implementations/aphrodite/View.js
Normal file
31
packages/benchmarks/src/implementations/aphrodite/View.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import { css, StyleSheet } from 'aphrodite';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const { style, ...other } = this.props;
|
||||
return <div {...other} className={css(styles.root, style)} />;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
alignItems: 'stretch',
|
||||
borderWidth: 0,
|
||||
borderStyle: 'solid',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexBasis: 'auto',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
// fix flexbox bugs
|
||||
minHeight: 0,
|
||||
minWidth: 0
|
||||
}
|
||||
});
|
||||
|
||||
export default View;
|
||||
@@ -0,0 +1,9 @@
|
||||
import Box from './Box';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
18
packages/benchmarks/src/implementations/css-modules/Box.js
Normal file
18
packages/benchmarks/src/implementations/css-modules/Box.js
Normal file
@@ -0,0 +1,18 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import View from './View';
|
||||
import styles from './box-styles.css';
|
||||
|
||||
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
|
||||
<View
|
||||
{...other}
|
||||
className={classnames(styles[`color${color}`], {
|
||||
[styles.fixed]: fixed,
|
||||
[styles.outer]: outer,
|
||||
[styles.row]: layout === 'row'
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Box;
|
||||
@@ -0,0 +1,2 @@
|
||||
import View from './View';
|
||||
export default View;
|
||||
13
packages/benchmarks/src/implementations/css-modules/View.js
Normal file
13
packages/benchmarks/src/implementations/css-modules/View.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import styles from './view-styles.css';
|
||||
|
||||
class View extends React.Component {
|
||||
render() {
|
||||
const props = this.props;
|
||||
return <div {...props} className={classnames(styles.initial, props.className)} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default View;
|
||||
@@ -0,0 +1,37 @@
|
||||
.outer {
|
||||
align-self: flex-start;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.color0 {
|
||||
background-color: #14171A;
|
||||
}
|
||||
|
||||
.color1 {
|
||||
background-color: #AAB8C2;
|
||||
}
|
||||
|
||||
.color2 {
|
||||
background-color: #E6ECF0;
|
||||
}
|
||||
|
||||
.color3 {
|
||||
background-color: #FFAD1F;
|
||||
}
|
||||
|
||||
.color4 {
|
||||
background-color: #F45D22;
|
||||
}
|
||||
|
||||
.color5 {
|
||||
background-color: #E0245E;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import Box from './Box';
|
||||
import Provider from './Provider';
|
||||
import View from './View';
|
||||
|
||||
export default {
|
||||
Box,
|
||||
Provider,
|
||||
View
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
49
packages/benchmarks/src/implementations/emotion/Box.js
Normal file
49
packages/benchmarks/src/implementations/emotion/Box.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import View from './View';
|
||||
|
||||
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
|
||||
<View
|
||||
{...other}
|
||||
style={[
|
||||
styles[`color${color}`],
|
||||
fixed && styles.fixed,
|
||||
layout === 'row' && styles.row,
|
||||
outer && styles.outer
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = {
|
||||
outer: {
|
||||
alignSelf: 'flex-start',
|
||||
padding: 4
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
color0: {
|
||||
backgroundColor: '#14171A'
|
||||
},
|
||||
color1: {
|
||||
backgroundColor: '#AAB8C2'
|
||||
},
|
||||
color2: {
|
||||
backgroundColor: '#E6ECF0'
|
||||
},
|
||||
color3: {
|
||||
backgroundColor: '#FFAD1F'
|
||||
},
|
||||
color4: {
|
||||
backgroundColor: '#F45D22'
|
||||
},
|
||||
color5: {
|
||||
backgroundColor: '#E0245E'
|
||||
},
|
||||
fixed: {
|
||||
width: 6,
|
||||
height: 6
|
||||
}
|
||||
};
|
||||
|
||||
export default Box;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user