Compare commits

..

241 Commits
0.3.0 ... 0.8.9

Author SHA1 Message Date
Nicolas Gallagher
9c8407162e 0.8.9 2018-07-19 12:32:40 -07:00
Jeremy
16b9ec2917 [fix] nested ScrollView scroll behaviour propagation
Allow ScrollView to be scrolled from gestures originating in a
descendent ScrollView.

Fix #1042
Close #1043
2018-07-19 12:17:42 -07:00
Nicolas Gallagher
d4af1eb981 0.8.8 2018-07-06 15:06:46 -07:00
Nicolas Gallagher
405a3b79e8 [fix] TextInput calls onSelectionChange upon user input
Fix #1018
2018-07-06 15:00:50 -07:00
Nicolas Gallagher
afd5293172 Update React packages 2018-07-06 13:46:21 -07:00
Nicolas Gallagher
e4831b7bd8 Update benchmarks dependencies 2018-07-06 13:45:56 -07:00
Nicolas Gallagher
baffc9a9e6 0.8.7 2018-07-05 11:50:17 -07:00
Nicolas Gallagher
4151b47005 [fix] backgroundClip prefixing when value is 'text'
Temporary work-around for a bug in inline-style-prefixer.

Fix #1014
2018-07-05 11:46:49 -07:00
Nicolas Gallagher
96ec805f59 [fix] TextInput multiline value with onSubmitEditing
Prevent a newline from being added when submitting a multiline text
input using the "Enter" key.

Fix #1013
2018-07-04 12:32:48 -07:00
Nicolas Gallagher
d2df2c296e 0.8.6 2018-06-28 09:47:56 -07:00
Nicolas Gallagher
af80b046fa Update Flow config documentation
Close #1007
2018-06-28 09:33:07 -07:00
Nicolas Gallagher
1d01af57d0 [fix] add mousedown and mouseup to supported View props
Fix #1008
2018-06-28 09:08:28 -07:00
Johannes
e7613ca4d1 [fix] onLayout TypeError if node already unmounted
Close #993
2018-06-28 09:02:20 -07:00
Nicolas Gallagher
0e81c6ef27 0.8.5 2018-06-26 07:55:35 -07:00
Nicolas Gallagher
7fa8940325 [fix] VirtualizedList calls to findNodeHandle
Fix #1004
2018-06-26 07:52:07 -07:00
Nicolas Gallagher
3e681bed3e GitHub issue template adjustment
Move help text into HTML comments and make it clear that issues will be
closed if the issue template is ignored.
2018-06-15 10:47:04 -07:00
ral51
ecd4b40c71 Fix source code links on website
Close #994
2018-06-14 13:45:38 -07:00
Nicolas Gallagher
e7cb364b63 0.8.4 2018-06-14 13:40:57 -07:00
Nicolas Gallagher
90bd23f783 [fix] onLayout event object
Make sure that `this` within the `target` getter references the React
component rather than the layout event object.

Fix #996
2018-06-14 13:21:25 -07:00
Nicolas Gallagher
47a281373a 0.8.3 2018-06-06 16:24:02 -07:00
Nicolas Gallagher
6a0302169c [fix] react-native-web package files
Fix #987
2018-06-06 16:21:33 -07:00
Nicolas Gallagher
b195f2b1f5 0.8.2 2018-06-05 13:22:24 -07:00
Nicolas Gallagher
7b81d2a7ec Fix AsyncStorageExample
Comment out React Native's use of non-standard `done()` method, which
isn't available on `async` functions even though it is polyfilled for
the global Promise.
2018-06-05 13:13:36 -07:00
Nicolas Gallagher
a1d8ea776e [fix] PickerIOS is alias for Picker
PickerIOS API is a subset of the cross-platform Picker. This patch
provides some compatibility for use of PickerIOS / PickerIOS.Item in
legacy code.
2018-06-05 13:12:59 -07:00
Nicolas Gallagher
15b960f097 0.8.1 2018-06-05 09:31:42 -07:00
Nicolas Gallagher
001be82178 [fix] remove AppRegistry logging in production
Fix #986
2018-06-05 09:08:36 -07:00
Nicolas Gallagher
5eeef9e3d2 [fix] only call preventDefault for clicks on links
The previous incarnation of this fix would cancel clicks that bubble up
to elements like ScrollViews, with undesired impact on child element
events. Instead, limit the hack to elements with accessibilityRole=link.

Fix #985
2018-06-05 09:02:35 -07:00
Nicolas Gallagher
6a310999d0 0.8.0 2018-06-04 11:55:16 -07:00
Nicolas Gallagher
a9ad313a79 Add examples app to release tasks 2018-06-04 11:54:11 -07:00
Nicolas Gallagher
d0ca7585ab Update docs
Close #960
2018-06-04 11:40:08 -07:00
Nicolas Gallagher
3f8624e25f [change] StyleSheet.hairlineWidth is whole pixel value
Even on high DPI screens, browsers can round sub-pixel values down to
`0`. This causes hairlineWidth borders/heights/etc not to be rendered.
Revert `hairlineWidth` value back to `1`.
2018-06-04 11:40:08 -07:00
Nicolas Gallagher
8f0c39c2fe [change] introduce Jest preset
A simple Jest preset that configures module mapping and produces
human-readable styles (i.e., not converted to numeric form).

Fix #928
Fix #963
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
4f9216853b Add more paths to jest ignore 2018-06-04 11:40:07 -07:00
Nicolas Gallagher
14f7dfd515 [fix] Clipboard preserves line breaks
Fix #979
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
c336995952 [change] prevent default click behavior when using responder system
Certain HTML elements have a default behaviour (e.g., link navigation)
that can only be prevented in the `click` event handler. The responder
event system doesn't make use of `click` and no callbacks have access to
the `click` event. To prevent unwanted default behaviour, and emulate
the behavior in React Native, the `click` callback will automatically
call `preventDefault()` when the responder system is being used.

The result is that components like `Touchable*` that are overloaded as
web links need to explicitly trigger the link navigation, e.g.,

```
<TouchableOpacity
  accessibilityTraits="link"
  href={href}
  onPress={(e) => {
    Linking.openUrl(href);
  }}
/>
```

Fix #970
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
2afa5a3cf7 [change] remove componentWillReceiveProps from Image 2018-06-04 11:40:07 -07:00
Nicolas Gallagher
9fb818cfd4 [fix] Image resizeMode for 'center' and 'repeat'
React Native will scale down an image to fit its container before
centering or repeating it.
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
3153cd8213 [add] Image support for blurRadius, tintColor, and shadows
Use CSS filters to implement React Native image styles.

Ref #362
Ref #548
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
026a92fd53 [fix] babel-plugin option to rewrite to commonjs paths
Allow the babel plugin to be configured to rewrite paths to either ES
modules (default) or CommonJS.

Ref #961
2018-06-04 11:40:07 -07:00
Nicolas Gallagher
e0f010da47 [change] whitelist props passed to React DOM
Avoiding unknown property warnings from React DOM. Many components
within and build upon React Native do not filter their own properties
and spread them into View. It is expected that React Native ignore these
properties.

Fix #898
2018-06-04 11:40:06 -07:00
Nicolas Gallagher
b299eb6c59 [add] SwipeableFlatList and SwipeableListView 2018-06-04 11:39:55 -07:00
Nicolas Gallagher
da38e87b50 [fix] minor inconsistency in textShadow style resolution
React Native doesn't apply textShadow styles when the 'height' or
'width' offset is 0.
2018-06-04 11:39:12 -07:00
Nicolas Gallagher
a40521f485 [fix] shadow style props resolution 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
f62ed22a14 [add] Text support for textDecoration{Color,Style} 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
bfaca0557b [fix] add UIManager to NativeModules 2018-06-04 10:24:35 -07:00
Nicolas Gallagher
c3eedabac4 Add React Native examples app
RNTester examples taken from React Native 0.55.4.

Minor changes to disable / enable some examples for the web.
2018-06-04 10:24:31 -07:00
Nicolas Gallagher
cf43ffd700 [change] Export stubs for iOS/Android modules
Fix #958
Fix #967
2018-06-04 10:21:45 -07:00
Nicolas Gallagher
a8e5d43db5 Move 'website' to 'packages/website'
Keep all workspaces in the 'packages' directory.
2018-06-03 11:32:55 -07:00
Nicolas Gallagher
b4e3427fea [fix] UIManager measurements don't block
A large number of layout measurements (and their corresponding tasks)
can block the main thread. Make the work async and try to keep the UI
responsive to user input while the layout work is taking place.
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
a82cfbe504 [fix] Image layout in Firefox
Layout is more reliable in Firefox if the root element's flexBasis is
'auto'. This patch also moves the background image to an internal tile.
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0eae7bed2e [change] Image should not be draggable by default 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
b8f54f61f0 [change] Image uses decode() not requestIdleCallback()
Chrome heavily throttles `requestIdleCallback` while scrolling, which
causes delays in image loading. Instead, use the relatively new
`decode()` API to decode the image off the main thread and prevent jank,
without delaying loading itself.

Fix #764
Ref #759
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
d5c6b98340 [fix] Image loading callbacks when mounting cached image 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
16b2fb9bd7 [fix] Image loading for source={{ uri: '' }}
Avoid an error being thrown from attempting to call `match` on an object
value.

Fix #962
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
48e62fcd64 Add PJPEG example to docs 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0816c40790 [fix] add 'target' to onLayout nativeEvent 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
2756ab49c3 [fix] TextInput add 'numbers-and-punctuation' keyboardType 2018-06-03 11:32:54 -07:00
Nicolas Gallagher
10407f3aa2 [add] Share API
Fix #958
2018-06-03 11:32:54 -07:00
Nicolas Gallagher
0ee3310290 [change] Linking API
Linking updates the application document's URL rather than opening a new
window. This change also makes deep-linking work.
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
19b356aaea [add] Alert API 2018-06-03 11:32:53 -07:00
Nicolas Gallagher
ea744fe780 [add] NativeEventEmitter export
Export NativeEventEmitter and provide React Native's implementation.
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
f254c8eae6 [change] update Animated implementation
Mirror contents of React Native 0.55.4
2018-06-03 11:32:53 -07:00
Nicolas Gallagher
5fcb36fc21 [change] ListView update 2018-06-03 11:32:53 -07:00
Nicolas Gallagher
377f23f725 [add] SectionList
Close #831
2018-06-03 11:32:51 -07:00
Nicolas Gallagher
fc0b81416a [add] FlatList 2018-06-03 10:16:51 -07:00
Nicolas Gallagher
cb545b0dac [add] LayoutAnimation export
Fix #803
2018-06-03 10:16:51 -07:00
Nicolas Gallagher
f684a3656e [fix] InteractionManager improvements 2018-06-03 10:16:51 -07:00
Nicolas Gallagher
45975d3a1e Update and reorganize React Native vendor code 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
2d83ffbd6b Add .prettierignore file 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
64307c066a Update eslint globals 2018-06-03 10:16:50 -07:00
Nicolas Gallagher
bb66639519 [fix] ScrollView stickyHeaderIndices basic support
Close #434
2018-06-03 10:16:47 -07:00
Nicolas Gallagher
c00360491b [fix] ScrollView methods and horizontal layout
Horizontal ScrollView was not scrollable when combined with
RefreshControl.

Add a stub for the flashScrollIndicators method.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
4bc16fa3eb [fix] AppState should not throw for 'memoryWarning' event 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
2237777341 [fix] Vibration has default pattern
Rather than throwing an error when a pattern is not provided, set a
default value as per React Native.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
6a2252891a [fix] ImageBackground with <Text> children
Prevent Text from displaying behind the Image.
2018-05-29 11:04:27 -07:00
Nicolas Gallagher
206a236df2 [add] AccessibilityInfo and DeviceInfo stubs 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
ff5a928a50 [fix] processColor compatibility with React Native 2018-05-29 11:04:27 -07:00
Nicolas Gallagher
61bf7e76b0 [fix] CommonJS exports can be imported without 'default'
Make sure all the CommonJS modules can be required as normal, rather
than needing to be suffixed with `.default` due to being compiled from
ES modules.
2018-05-29 11:04:24 -07:00
Nicolas Gallagher
e3170623f1 [fix] use ES modules everywhere
Webpack doesn't like mixing `import` with `module.exports`, and some of
the dynamic requires cannot be safely used in both an ES and CommonJS
module environment without looking for `.default` each time.
2018-05-25 15:45:38 -07:00
Nicolas Gallagher
392de22992 0.7.3 2018-05-25 15:39:03 -07:00
Sunny R Gupta
c84163d80e Fix typo in examples text 2018-05-25 15:06:48 -07:00
Philipp Spiess
50442c4e7c [fix] ResponderEventPlugin with React 16.4
React 16.4 includes the necessary ResponderEventPlugin dependencies and
makes some changes to the event internals that causes a breaking change.
This patch fixes rendering with react-dom@16.4 while preserving
backward-compatibility with earlier versions of react-dom.

Close #908
2018-05-25 15:02:28 -07:00
Nicolas Gallagher
eb0e0b8771 0.7.2 2018-05-19 08:46:55 -07:00
Nicolas Gallagher
15d5e57e92 [fix] convert VirtualizedList vendor code to ES modules
Webpack has trouble bundling a mix of CommonJS and ES modules in the
same package.

Fix #957
2018-05-19 08:45:21 -07:00
Nicolas Gallagher
def873e9e3 0.7.1 2018-05-18 18:16:51 -07:00
Nicolas Gallagher
9e9b40f155 [fix] ResponderEventPlugin injection for tree-shaking bundler
Previously this depended on a side-effecting `import` statement which
would be removed under tree-shaking.
2018-05-18 18:13:30 -07:00
Nicolas Gallagher
4a45595b7a 0.7.0 2018-05-18 17:29:47 -07:00
Nicolas Gallagher
45095fd300 Documentation updates 2018-05-18 17:27:39 -07:00
Nicolas Gallagher
004c7ce478 [change] export ES modules by default
ES modules are the default package export. Commonjs modules are exported
from 'dist/cjs'. Modern bundlers like webpack can consume ES modules.
The addition of the `sideEffects:false` to the `package.json` helps
webpack tree-shaking modules.
2018-05-18 17:27:36 -07:00
Nicolas Gallagher
96c3f09fac [fix] Firefox UI for numeric TextInput
Fix #900
2018-05-18 13:40:15 -07:00
Nicolas Gallagher
edc99e79eb [change] ResponderEventPlugin filters browser emulated mouse events
Browsers dispatch mouse events after touch events:
https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent

There have been several attempts to avoid this behaviour affecting the
ResponderEvent system. The previous approach of cancelling the event in
the `onResponderRelease` event handler can end up cancelling other
events that are expected, e.g., `focus`.

Instead, this patch changes the `ResponderEventPlugin.extractEvents`
function to filter the mouse events that occur a short time after a
touch event. (It's assumed that people will not be clicking a mouse
within a few hundred ms of performing a touch.) This allows the
ResponderEvent system to function as expected and leaves other callbacks
to fire as they would be expected to in React DOM, i.e., both
`onTouchStart` and `onMouseDown` will be called following a touch start.

Fix #835
Fix #888
Fix #932
Close #938
Ref #802
2018-05-18 13:39:25 -07:00
Nicolas Gallagher
e8f2c98786 [add] mark focusable DOM nodes with 'data-focusable'
Focus-based UIs can use out-of-tree focus algorithms to manage focus
using remote control devices. This patch marks DOM nodes that React
Native considers "focusable".

Close #827
2018-05-18 11:56:56 -07:00
Nicolas Gallagher
dcdf1468f9 [fix] onLayout timing without ResizeObserver
onLayout is called after the component is mounted to the DOM. This makes
both the fallback and ResizeObserver code path behave the same as React
Native.

Fix #911
Fix #941
Close #939
2018-05-18 11:44:30 -07:00
Nicolas Gallagher
ee5e80064f [add] support for CSP policy requiring 'nonce' on <style>
CSP policy may prevent writing to `<style>` unless a `nonce` attribute
is set. This change makes that possible by moving the modality-related
styles into the main style sheet, and allowing additional props to be
provided to the `<style>` element when rendering on the server. For
example:

```
const { element, getStyleElement } = AppRegistry.getApplication('App');
const html = renderToString(element);
const css = renderToStaticMarkup(getStyleElement({ nonce }));
```
2018-05-18 11:44:24 -07:00
Philipp Spiess
3e4d8d6b2f Upgrade React and all transitive dependencies
Close #942
2018-05-16 15:01:19 -07:00
Nicolas Gallagher
d3a8270d55 Add a development sandbox to storybook 2018-05-09 10:38:34 -07:00
Nicolas Gallagher
18933724d6 Remove @providesModule from all modules
Ref: d5e9e55fa3
2018-05-08 10:28:25 -07:00
Nicolas Gallagher
e7f84a9228 Add GitHub issue templates
Close #931
2018-05-08 10:11:27 -07:00
Scott Kyle
8b1e6f816f [change] remove NetInfo.isConnected.getConnectionInfo()
React Native doesn't have `NetInfo.isConnected.getConnectionInfo()`.
This was incorrectly added to the API while updating the main `NetInfo`
API.

Close #937
2018-05-08 10:01:41 -07:00
Scott Kyle
02b6f3ff3c [fix] findNodeHandle import in VirtualizedList
Close #936
2018-05-08 09:53:50 -07:00
odalay
6f52007cc8 Fix CheckboxScreen link on website
Close #926
2018-05-08 09:53:16 -07:00
Nicolas Gallagher
1e59e53e66 Update jest 2018-05-08 09:49:02 -07:00
Nicolas Gallagher
f6a65210ca Update formatter and linter 2018-05-08 09:24:08 -07:00
Nicolas Gallagher
2e9071eb0e Update benchmarks dependencies 2018-05-08 09:15:51 -07:00
Nicolas Gallagher
8f25fcc05b Update storybook dependencies 2018-05-07 15:00:22 -07:00
Nicolas Gallagher
1e8577fc9e [fix] setting flex styles as inline styles
Reverts #648 as browsers are inconsistent in how they handle
'flex-basis', so this hack isn't effective. And React has no support for
using '!important' in inline styles.

Fix #798
2018-04-30 11:51:18 -07:00
Nicolas Gallagher
c51f567d19 Simplify tests for several exported modules 2018-04-24 15:54:40 -07:00
Nicolas Gallagher
bce5957991 0.6.1 2018-04-21 12:16:05 -07:00
Nicolas Gallagher
34d8409d43 Update yarn.lock 2018-04-21 12:15:18 -07:00
Sébastien Lorber
8442f13e96 Add Gatsby integration to README
Close #868
2018-04-21 12:12:50 -07:00
Nicolas Gallagher
c6e2b584af [fix] AppRegistry.getApplication shouldn't throw if missing options
Fix #899
2018-04-21 12:05:42 -07:00
Philipp Spiess
38affa9bae Fix Game2048 on desktop browsers
The React Native example only listens for touch events but on web we can
listen to mouse events to improve support.

Close #909
2018-04-21 11:56:14 -07:00
Nicolas Gallagher
f9ebdb6327 Documentation layout fixes 2018-04-21 11:50:39 -07:00
Nicolas Gallagher
83a8758f68 0.6.0 2018-04-15 15:56:10 -07:00
MoOx
e022d166dd Update Phenomic integration link to official example 2018-04-15 15:26:24 -07:00
Nicolas Gallagher
1a225bc449 [fix] Text onPress event propagation
The press event should not propagate.

Fix #897
2018-04-15 15:23:32 -07:00
Nicolas Gallagher
cf2612663b [fix] createElement uses provided function component
If the component provided to 'createElement' is not a string alias for a
DOM component, it will no longer attempt to replace that component with
a DOM component inferred from the 'accessibility{Component,Role,Traits}'
prop.

Fix #895
2018-04-15 15:03:34 -07:00
Nicolas Gallagher
1aec803086 [fix] Picker.Item support for 'color' prop
Not well supported by browsers.

Fix #810
2018-04-08 11:43:20 -07:00
Nicolas Gallagher
2050730b77 [fix] consistency of nativeEvent.location{X,Y} between touch and mouse
Calculate `location{X,Y}` in the same way for both touch and mouse
events. Also defer the call to `getBoundingClientRect` to avoid
unnecessary DOM queries when the data is not used.
2018-04-08 11:05:12 -07:00
Nicolas Gallagher
a67bf0f490 [change] synchronous layout measurement 2018-04-08 10:38:31 -07:00
Nicolas Gallagher
4529a4ac0a Update benchmarks dependencies 2018-04-07 17:20:01 -07:00
Giuseppe Gurgone
5a04d07a35 [change] call 'onLayout' when elements are resized
Uses ResizeObserver to monitor layout changes. Falls back to
window.onresize (with initial firing).

Fix #60
Close #848
2018-04-07 17:03:17 -07:00
Nicolas Gallagher
9427eea293 [add] VirtualizedList support
Make changes needed to run VirtualizedList using React Native for Web.

Close #659
2018-04-07 10:25:15 -07:00
Nicolas Gallagher
b9803e1e07 Add snapshot of VirtualizedList from React Native
Exact source code of the module and its dependencies, for the given SHA.
2018-04-07 10:25:15 -07:00
slorber
7a3a9a5c3f [add] AppRegistry support for React render callback
Close #858

Co-authored-by: Nicolas Gallagher <nicolasgallagher@gmail.com>
2018-04-07 10:15:17 -07:00
Nicolas Gallagher
4c59343fd3 [fix] SafeAreaView inset padding for Safari
See https://webkit.org/blog/7929/designing-websites-for-iphone-x/

Fix #859
2018-04-02 14:32:15 -07:00
Nicolas Gallagher
ce89b7e3ec [change] move 'react-art' to peerDependencies 2018-04-02 14:31:26 -07:00
Nicolas Gallagher
23fa663a6e [fix] Text font styling
Don't use 'font' shorthand internally to reset font styles, as the
framework's internal styles may be injected after application styles,
causing 'font' to override 'font-*' properties.

Fix #881
2018-04-02 11:37:59 -07:00
Nicolas Gallagher
b96dd668d3 [add] AppRegistry provider methods
Adds support for the following methods in React Native:
- setComponentProviderInstrumentationHook
- setWrapperComponentProvider
2018-04-01 10:29:27 -07:00
Nicolas Gallagher
a9cacb2ef5 [change] AppRegistry container is not absolutely positioned
Aligned with latest layout of AppContainer in React Native.

Fix #871
2018-04-01 09:47:02 -07:00
Nicolas Gallagher
c122814591 0.5.4 2018-04-01 09:34:51 -07:00
Bruno Lemos
efe18f1b7e [fix] Linking.openURL on Firefox
Fix #883
Close #884
2018-04-01 09:25:16 -07:00
Nicolas Gallagher
e1b576e427 [add] StyleSheet support for 'System' in font stack
Allow 'System' to be used in a font-family stack, e.g., "Noto, System".

Update the 'System' font stack to include "system-ui", which is the
latest keyword for system fonts.
2018-03-31 14:57:29 -07:00
Nicolas Gallagher
5d77d6e30f [fix] TextInput focus management
Defer to the browser's native handling of 'blur' and 'focus'; directly
update the internal state of TextInputState. Fixes an issue with
preserving focus when the tab is backgrounded.

Also ensure the TextInputState is correctly set when a component mounts.
When 'autoFocus' is true, 'onFocus' can be called before the internal
ref is set.

Fix #880
2018-03-31 14:01:19 -07:00
Jesús Darío
8fb9a88ee6 Add webpack-cli to getting started guide 2018-03-29 15:49:43 -07:00
Kyle Goggin
a7cda988ef [fix] ignore 'tvParallaxProperties' prop
Close #867
2018-03-29 15:47:39 -07:00
Nicolas Gallagher
b239cfb04d [fix] update InteractionManager stub
Fix #875
2018-03-29 15:32:38 -07:00
Nicolas Gallagher
8e94d858b2 [fix] Picker prop types
Fix #879
2018-03-29 15:11:16 -07:00
Nicolas Gallagher
aa22b06359 0.5.3 2018-03-15 13:32:24 -07:00
Paul Armstrong
2aa565c7c3 [fix] Picker default fontFamily
Close #860
2018-03-15 13:08:23 -07:00
Sakamoto, Kazunori
7b9b57960d Update getting-started.md
Fix __DEV__ value in example web/webpack.config.js

Close #862
2018-03-15 13:06:58 -07:00
Nicolas Gallagher
eae3ee9dca 0.5.2 2018-03-07 19:56:46 -08:00
Nicolas Gallagher
74e1a196b6 [fix] Linking.openURL works with nonce use
`Linking` no longer uses an iframe hack to add 'noopener' support to
older Safari. Instead it relies on browsers having support for
'noopener'.

Fix #837
Close #846
2018-03-05 11:50:08 -08:00
Nicolas Gallagher
48da9814e7 Update benchmark libraries 2018-03-05 10:40:02 -08:00
Nicolas Gallagher
4d391ef57c Link to example integrations in README 2018-03-05 10:22:26 -08:00
Nicolas Gallagher
780df69a80 Update link to Glitch starter 2018-03-01 19:37:12 -08:00
Nicolas Gallagher
6c229da01f Minor improvement to AppRegistry console.log message 2018-02-26 15:20:35 -08:00
Nicolas Gallagher
ae7aa818fb 0.5.1 2018-02-20 17:48:12 -08:00
Nicolas Gallagher
306cf67932 [add] Image style support for animations, interactions, and filters 2018-02-20 17:45:19 -08:00
Nicolas Gallagher
619c2048be [add] support for 'transformStyle' style prop 2018-02-20 17:44:22 -08:00
Nicolas Gallagher
b9f9a4f8d7 Move some View style propTypes to InteractionPropTypes 2018-02-20 17:43:22 -08:00
Nicolas Gallagher
58bc18c2f5 Group 'transition' style propTypes with animations 2018-02-20 17:42:19 -08:00
Nicolas Gallagher
073940fc4e Group 'perspective' style propType with transforms 2018-02-20 17:40:57 -08:00
Nicolas Gallagher
5c462303de Inline some values in TextStylePropTypes 2018-02-20 17:32:45 -08:00
Nicolas Gallagher
5fb92da317 [fix] Switch layout when left/right do not flip
The I18nManager can now disable the automatic BiDi flipping of
left/right, which caused the Switch layout to break in RTL mode. Change
the styles to use start/end.
2018-02-20 16:44:46 -08:00
Nicolas Gallagher
cafe10d851 Improve View 'pointerEvents' example 2018-02-19 18:48:31 -08:00
Nicolas Gallagher
b28581f44e [fix] Touchable 'touchAction' value 2018-02-19 18:27:49 -08:00
Nicolas Gallagher
9333e7e887 0.5.0 2018-02-19 15:48:05 -08:00
Nicolas Gallagher
b28cbbb37e [fix] TextInput default font styles
Match React Native (and web) expected defaults.
2018-02-19 15:29:05 -08:00
Nicolas Gallagher
a53372ceb3 [fix] babel-plugin only rewrites paths for known modules
Don't rewrite import paths for non-existent modules or types. They will
attempt to be imported from the package's main export. This change
currently requires a module map to be generated for the babel-plugin to
use. The map is automatically regenerated for any commit that alters the
entry file of react-native-web.

Fix #822
2018-02-19 13:05:42 -08:00
Nicolas Gallagher
239a43978f Update yarn.lock 2018-02-18 17:43:22 -08:00
Nicolas Gallagher
b4e4bfbb3c Fix the event handlers with event normalization
Events are now normalized within the responder system, so those handlers
don't need to be listed anymore. Recently added events (blur,
contextmenu, focus) are now also normalized.
2018-02-18 17:42:02 -08:00
Nicolas Gallagher
893963a799 [fix] PanResponder grant firing twice on touch devices
Touch events can produce trailing mouse events, which cause unwanted
events to fire in the responder event system.  This issue is avoided in
`Touchable` by cancelling the event in the responder release callback.
To fix the issue in other areas, like the PaneResponder, this hack is
moved into `createElement` and applied to event `onResponderRelease`
callback.

Fix #802
2018-02-18 17:41:48 -08:00
Nicolas Gallagher
e5adc5a37c Add 'coveragePathIgnorePatterns' to jest config 2018-02-17 18:03:36 -08:00
Nicolas Gallagher
6d908189a7 [fix] Text support for 'fontVariant' style
Fix #824
Close #825
2018-02-17 18:03:31 -08:00
Nicolas Gallagher
31db333ba3 [fix] Image support for SVG base64 data
The previous fix to support inline SVG data in utf-8 format broke images
that were rendering base64 SVGs.
2018-02-17 15:55:45 -08:00
Nicolas Gallagher
9fe089ca21 [add] development warning about using '!important' in styles
React Native for Web may use '!important' as part of the internal
resolving of styles. But user styles should never be allowed to include
'!important' in the value. Print a warning to the console when they do.
2018-02-17 14:46:31 -08:00
Nicolas Gallagher
a314d5b2e4 [change] MIT license
Now possible to license under MIT following the change to React Native's
license.

26684cf3ad

Fix #828
2018-02-17 12:23:45 -08:00
Nicolas Gallagher
fb845ebf44 Update benchmarks dependencies 2018-02-16 19:08:19 -08:00
Nicolas Gallagher
0d0c7e6e27 Document 'onBlur', 'onFocus', and 'onContextMenu' props 2018-02-16 18:53:33 -08:00
Nicolas Gallagher
f37003a079 Add end/start to BorderPropTypes 2018-02-15 19:08:34 -08:00
Nicolas Gallagher
f1fc2a9e37 Rewrite I18nManager and i18nStyle unit tests 2018-02-15 18:46:08 -08:00
Nicolas Gallagher
92794cdc9f [add] I18nManager and StyleSheet support for RTL without left/right flip
I18nManager supports `doLeftAndRightSwapInRTL` and
`swapLeftAndRightInRTL` to query and control the BiDi-flipping of
left/right properties and values. For example, you may choose to use
`end`/`start` for positioning that flips with writing direction, and
then disable `left`/`right` swapping in RTL so that `left` will always
be `left`.

The StyleSheet resolver cache must also account for the third "direction"
variant: RTL with no swapping of left/right.
2018-02-15 18:45:45 -08:00
Nicolas Gallagher
b754776373 [add] StyleSheet support for start/end properties and values
Add support for new style properties and values that automatically
account for the writing direction (as introduced in React Native
0.51.0). The start/end variants are automatically resolved to match the
global writing direction, as defined by I18nManager.isRTL. Start/End
take precedence over Left/Right.

Adds support for the following properties:

* `borderTop{End,Start}Radius`
* `borderBottom{End,Start}Radius`
* `border{End,Start}Color`
* `border{End,Start}Style`
* `border{End,Start}Width`
* `end`
* `margin{End,Start}`
* `padding{End,Start}`
* `start`

And values:

* `clear: "end" | "start"`
* `float: "end" | "start"`
* `textAlign: "end" | "start"`
2018-02-15 18:43:09 -08:00
Nicolas Gallagher
155b34e495 Fix unit test timeouts on Travis CI
Jest recommends using 'runInBand' for Travis CI. It runs all tests
serially in the current process, rather than creating a worker pool of
child processes that run tests.
2018-02-14 14:26:03 -08:00
Nicolas Gallagher
00c9dc4236 [fix] setNativeProps can apply flex styles
Updates the 'setValueForStyle' implementation to support style values
that contain "!important". This allows the 'flex{Basis,Grow,Shrink}'
values created by the style resolver to be applied. They currently use
the important priority as a work-around for browser-inconsistencies in
the 'flex' shorthand.

Upstream fix: https://github.com/facebook/react/pull/12181

Ref #798
Close #813
2018-02-14 13:51:00 -08:00
Nicolas Gallagher
b66aba1a06 website: fix ImageBackground source code link
Close #820
2018-02-14 13:39:14 -08:00
Nicolas Gallagher
17f8a674b8 [fix] ResponderEventPlugin skips 'mouseup' when no touch is active
Prevent the responder system recording 'mouseup' events if there is no
active 'touch'.

Fix #816
2018-02-13 15:02:09 -08:00
Nicolas Gallagher
b8080ba775 [fix] Touchable press events regression in 9ee89bc
Fix a regression introduced by "ResponderEvent event filtering"
9ee89bc7f7. Touchable press events were
firing twice after the event normalization was moved up into
'extractEvents'. The reason is:

1) events were previously not normalized throughout the responder system
2) once they were normalized, the fix introduced by a535c558d8 stopped working
3) because normalized nativeEvent did not include event data like 'cancelable'.

This patch adds more of the original nativeEvent fields to the
normalized event, so that React's synthetic event can return the correct
information for 'bubbles', 'cancelable', 'isDefaultPrevented()', etc.
2018-02-13 13:34:17 -08:00
Nicolas Gallagher
7265736545 [fix] StyleSheet accepts 'space-evenly' value for 'justifyContent' 2018-02-12 10:35:30 -08:00
Bulat Kidasov
399f465e59 [fix] NativeMethodsMixin measure/measureInWindow with scroll
Account for scroll offsets when calculating measurements.

Fix #702
Fix #805
Close #806
2018-02-12 10:27:47 -08:00
Nicolas Gallagher
9ee89bc7f7 [fix] ResponderEventPlugin event filtering
Exclude middle, wheel, and right click mouse events from the responder
system. This fixes the Touchables incorrectly triggering 'onPress' in
response to these events.

Filter mousemove events in the 'extractEvents' methods, and check for
active touches rather than the length of the touch bank. This fixes the
PanResponder not functioning after the first touch in Firefox.

Fix #719
Fix #729
Close #804
2018-02-12 10:08:44 -08:00
Jakub Rimal
748b2d0f3f [fix] Image 'source' prop update when missing in initial render
Close #811
2018-02-12 08:27:48 -08:00
Nicolas Gallagher
fb4635e013 Update README's 2018-02-08 16:09:56 -08:00
Nicolas Gallagher
73b459e770 [add] TextInput onKeyPress support for arrow keys
Close #791
Close #792
2018-02-08 10:57:32 -08:00
Nicolas Gallagher
a41af0f65f 0.4.0 2018-02-06 16:35:20 -08:00
Nicolas Gallagher
96eecc0da3 Replace lerna with custom script
Each package version is now updated with each release.

Fix #783
2018-02-06 16:30:03 -08:00
Nicolas Gallagher
69d5373222 [fix] Firefox rendering of TextInput
Removes UA controls from numeric inputs in Firefox, and makes the
default border solid in all browsers.

Fix #789
2018-02-06 11:17:38 -08:00
Nicolas Gallagher
538ab88eda Separate modules for StyleSheet and related side-effects 2018-02-05 12:18:43 -08:00
Nicolas Gallagher
21b3f39c0b [fix] babel-plugin require call for compiled modules
require('module') => require('module').default

Fix #786
2018-02-04 10:42:21 -08:00
Nicolas Gallagher
998e275e65 [add] CSS keyframes support via 'animationName' style
Keyframes can be defined using an array of objects as the value of
'animationName'. Each keyframe is transformed into a CSS keyframe rule.
2018-02-03 11:48:57 -08:00
Nicolas Gallagher
31d428a649 [fix] add support for TextInput placeholderTextColor prop
This change also ensures that potential object-styles (user-provided or
placeholderTextColor) are group together at the end of the style array.

Fix #560
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
240cf7e05f [change] AppRegistry and StyleSheet APIs to fix SSR
SSR was not working correctly. Styles would accumulate in the style
cache and the styles returned for each 'getApplication' call would not
represent the styles needed to render a given tree, but rather all the
styles needed to render every tree that has been rendered by that server
instance.

This is now fixed by reinitializing the style resolver after a call to
'getStyleSheet' on the server. The return type of
'AppRegistry.getApplication' is changed – { element, getStyleElement }.
The 'getStyleElement' function must be called after the component tree
has been rendered.  Note that if 'getStyleElement' is not called for a
response, then its styles may leak into the next response's styles (but
will not affect the UX).

This patch also removes the 'StyleSheet.getStyleSheets' (web-only) API
and requires SSR to be done using 'AppRegistry.getApplication'.

Fix #778
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
2ad710d83a [change] StyleSheet does not eagerly resolve and inject styles
Resolving styles at the same time as they are registered is problematic.
This work doesn't need to be complete until the moment a component is
rendered, rather than being done at startup.
2018-01-31 10:55:45 -08:00
Nicolas Gallagher
dcce72b66e Reorganize style resolver
The style resolver is reorganized to remove React Native style
registration, and contain all the stateful resolver logic (previously
partially found in NativeMethodsMixin).

Style registration is done in 'StyleSheet.create', and will subsequently
be decoupled from eager style resolution.

The stateful resolver is fully contained in the
ReactNativeStyleResolver. The options are removed from the resolver as
they aren't needed to implement the correct i18n-ified styles. This
functionality is also covered by unit tests now.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
083769d642 [fix] resolve inline-style pointerEvents to classNames
pointerEvents styles are now always resolved to classNames, even if
working with dynamic styles. In practice, inline-style pointerEvents
have always resolved to classNames as an accident of 'createElement'
registering each pointerEvent style. However, this patch ensures that
the style resolver produces correct output, and introduces the basic
mechanism by which 'placeholderTextColor' (for which classNames cannot
be precomputed) could function similarly.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
a53dba8c62 [change] simplify CSS rule injection
WebStyleSheet handles injecting CSS rules and returning the style
sheet's text content.

All CSS, including the reset, is now added using WebStyleSheet and the DOM API 'insertRule'.
Browsers will throw a parse error on unrecognized vendor-prefixes in
selectors, so rules with unsafe selectors are wrapped in '@media all {}'
before being passed to 'insertRule'. (This will be relevant when
attempting to support 'placeholderTextColor'. And it is a way to
batch-insert rules with a single call to 'insertRule'.)

The html tag 'font-family' CSS reset is removed as it's not needed.
2018-01-31 10:55:44 -08:00
Nicolas Gallagher
670d43ba04 Refactor how pointerEvents styles are managed 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
73a731f2da Rename StyleManager and StyleRegistry 2018-01-31 10:55:44 -08:00
Nicolas Gallagher
1542f1f369 0.3.4 2018-01-31 10:31:50 -08:00
Nicolas Gallagher
6f58d7abe7 [fix] TextInput support for spellCheck prop
Fix #501
2018-01-31 10:27:08 -08:00
Nicolas Gallagher
7e0fbf9691 Fix yarn.lock
Yarn didn't make these changes to yarn.lock after updating packages.
2018-01-29 12:46:12 -08:00
Maximilian Stoiber
865034e8f7 Update benchmark instructions 2018-01-29 12:40:37 -08:00
Nicolas Gallagher
6e96ee4f3c Update benchmarked libraries 2018-01-29 08:59:18 -08:00
Maxime Thirouin
16d98b49f0 [fix] ignore native-only Image props
Close #788
2018-01-26 14:59:59 -08:00
Nicolas Gallagher
d04721c75a Update yarn.lock 2018-01-23 13:01:48 -08:00
Nicolas Gallagher
8512709251 Fix benchmark picker touch target size in Safari 2018-01-23 12:24:56 -08:00
Nicolas Gallagher
efeaea70a9 Benchmarks include forced layout time
This change to 'benchmarks' reports the time taken to perform a forced
layout after mounting the tree. Adding a forced layout to the stress
tests can surface how different approaches to styling may affect browser
render timings.

The total time displayed is now the sum of "scripting time" (previously
total time) and "layout time". The layout time is a reflection of the
time the browser takes to perform a style recalculation and relayout of
the document.

The Benchmark component now has a 'forceLayout' prop. When it is 'true'
a forced layout is triggered on componentDidUpdate. The time taken is
added to the sample's timing data.
2018-01-23 12:24:18 -08:00
Nicolas Gallagher
a403244e67 Update benchmarks library dependencies 2018-01-23 09:49:48 -08:00
Oleg Slobodskoi
985c1d63b6 Add benchmarks update test for react-jss
Close #785
2018-01-23 09:11:38 -08:00
Nicolas Gallagher
9d8d4057f6 Benchmark app component optimizations 2018-01-22 15:52:37 -08:00
Nicolas Gallagher
ec8843fe90 Compute benchmark props before each iteration
Fix a bug in the logic that was meant to perform component prop
computation in-between cycles.
2018-01-22 13:24:41 -08:00
Nicolas Gallagher
935970156c Benchmarks report total run time 2018-01-21 17:02:21 -08:00
Nicolas Gallagher
e4e6147081 0.3.3 2018-01-19 14:05:46 -08:00
Nicolas Gallagher
3e1b68d801 Add Moto G4 example benchmark results 2018-01-19 13:59:13 -08:00
Giuseppe Gurgone
1b493c9914 Add styled-jsx to benchmarks
Close #782
2018-01-19 12:51:21 -08:00
hushicai
6ecdc1a517 [fix] babel-plugin VariableDeclaration case
Convert VariableDeclarations, e.g.,

var ReactNative = require('react-native');

Close #781
2018-01-19 00:09:43 -08:00
Nicolas Gallagher
619079cedf Move babel-plugin npm badge to correct README 2018-01-18 10:24:02 -08:00
Javi Velasco
bbf7674b43 Add react-fela to benchmarks
Close #779
2018-01-18 10:03:13 -08:00
Nicolas Gallagher
f163e4f16f Use full URLs for README links 2018-01-18 09:50:14 -08:00
Nicolas Gallagher
bd8c2d6f24 Add npm badge to babel-plugin-react-native-web README 2018-01-18 09:49:54 -08:00
Nicolas Gallagher
3906b6b41b Add benchmarks app link to README 2018-01-17 19:38:58 -08:00
Nicolas Gallagher
753ef963f6 0.3.2 2018-01-17 19:18:50 -08:00
Nicolas Gallagher
0721245b3e Patch release script
Disable husky on version commit. Current publish script doesn't account
for attempts to republish the same version.
2018-01-17 19:15:23 -08:00
Nicolas Gallagher
b7adfd5f32 [fix] Picker.Item label text
Render the label as a child of 'option' rather than using the 'label'
attribute.

Fix #774
2018-01-17 17:59:05 -08:00
Nicolas Gallagher
a9342daee2 Add release script for benchmarks 2018-01-17 17:40:13 -08:00
Nicolas Gallagher
ed0cafac7c Rewrite benchmarks app
Reorganizes and rewrites the benchmarks. Each implementation is now
self-contained and the benchmarks can be run using a GUI. The benchmarks
themselves have been changed so that individual tests render over a
shorter time frame and more samples are taken.
2018-01-17 17:21:53 -08:00
Nicolas Gallagher
6e6fd4b5d0 Add note about Object.assign polyfill to docs 2018-01-16 17:14:17 -08:00
Nicolas Gallagher
5cd533e6cc 0.3.1 2018-01-16 11:15:45 -08:00
Nicolas Gallagher
d5e8d85ce9 Fix example in babel plugin README 2018-01-16 11:11:50 -08:00
Nicolas Gallagher
e234568a34 Fix source links in documentation 2018-01-11 12:59:44 -08:00
Nicolas Gallagher
19cf0711bc [add] StyleSheet support for 'overscrollBehavior'
An experimental CSS property to control the behavior when the scroll
position of a scroll container reaches the edge of the scrollport. This
allows web apps to get closer to native scrolling behaviour and
performance.

https://wicg.github.io/overscroll-behavior/
https://developers.google.com/web/updates/2017/11/overscroll-behavior

Fix #765
2018-01-11 12:58:43 -08:00
Johannes
067e3f346f [fix] KeyboardAvoidingView missing 'this' binding
Close #762
2018-01-11 12:53:54 -08:00
Nicolas Gallagher
2117e44e9d [fix] limit Image loader deferral to 1000ms
This patch introduces a limit on how long image loading is deferred, and
mitigates an issue with lengthy delays to 'requestIdleCallback' in the
Chrome browser.

Fix #759
2018-01-11 12:08:30 -08:00
Nicolas Gallagher
902ba22877 Update to flow-bin@0.63.1 2018-01-09 17:49:52 -08:00
Nicolas Gallagher
60c2cd65df Update to lint-staged@6.0.0 2018-01-09 17:36:19 -08:00
Nicolas Gallagher
fde29326f1 Update to lerna@2.6.0 2018-01-09 17:35:53 -08:00
Nicolas Gallagher
44d795437e Update to storybook@3.3.6 2018-01-09 17:34:28 -08:00
Nicolas Gallagher
03598d869b Update to babel-plugin-tester@5.0.0 2018-01-09 17:31:52 -08:00
Nicolas Gallagher
a3e44a5c60 Update to enzyme@3.3.0 2018-01-09 17:27:38 -08:00
Nicolas Gallagher
02b124eceb Update yarn.lock 2018-01-08 18:53:05 -08:00
711 changed files with 45849 additions and 12834 deletions

View File

@@ -2,3 +2,4 @@ coverage
dist
node_modules
packages/**/vendor/*
packages/examples

View File

@@ -25,7 +25,8 @@
"document": false,
"navigator": false,
"window": false,
// Flow global types
// Flow global types,
"$Enum": false,
"HTMLInputElement": false,
"ReactClass": false,
"ReactComponent": false,

View File

@@ -1,11 +1,11 @@
[version]
^0.61.0
^0.63.0
[ignore]
<PROJECT_ROOT>/.*/__tests__/.*
<PROJECT_ROOT>/packages/benchmarks/.*
<PROJECT_ROOT>/packages/.*/dist/.*
<PROJECT_ROOT>/website/.*
<PROJECT_ROOT>/packages/examples/.*
<PROJECT_ROOT>/packages/website/.*
.*/node_modules/babel-plugin-transform-react-remove-prop-types/*
[include]
@@ -14,4 +14,4 @@
<PROJECT_ROOT>/types
[options]
unsafe.enable_getters_and_setters=true

View File

@@ -4,7 +4,8 @@
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.
issue hasn't already been reported. Please note that your issue may be closed
if it doesn't include the information requested in the issue template.
## Getting started
@@ -74,7 +75,7 @@ yarn compile --watch
To run the interactive storybook:
```
yarn docs:start
yarn website
```
When you're also making changes to the 'react-native-web' source files, run this command in another process:
@@ -85,10 +86,18 @@ yarn compile --watch
## Benchmarks
To run the performance benchmarks in a browser (opening `./packages/benchmarks/index.html`):
To run the benchmarks locally:
```
yarn benchmark
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
To develop against these benchmarks:
```
yarn compile --watch
yarn benchmarks --watch
```
### New Features

View File

@@ -1,20 +0,0 @@
**Do you want to request a *feature* or report a *bug*?**
**What is the current behavior?**
**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.
**What is the expected behavior?**
**Environment (include versions). Did this work in previous versions?**
* OS:
* Device:
* Browser:
* React Native for Web (version):
* React (version):

49
.github/ISSUE_TEMPLATE/bug.md vendored Normal file
View File

@@ -0,0 +1,49 @@
---
name: "\U0001F41B Bug report"
about: "If something isn't working as expected \U0001F914"
---
<!--
Thank you for reporting an issue. Please note that an issue must include the
information that is marked as REQUIRED below, or it may be closed.
-->
**The problem**
<!--
REQUIRED: A clear and concise description of the bug or problem.
-->
**How to reproduce**
<!--
REQUIRED: Create a test case by forking this template https://codesandbox.io/s/6lx6ql1w5r
Failing to include a reduced test case may result in the issue being closed,
and will delay any potential fix. Your application or GitHub project is NOT
considered a reduced test case. If the issue only affects certain browsers,
providing screenshots is also helpful.
-->
Simplified test case: <!-- add link here -->
Steps to reproduce:
1.
2.
3.
**Expected behavior**
<!--
REQUIRED: A clear and concise description of what you expected to happen.
-->
**Environment (include versions). Did this work in previous versions?**
* React Native for Web (version): TBC
* React (version): TBC
* Browser: TBC
<!--
OPTIONAL:
**Additional context**
Add any other context about the problem here.
-->

17
.github/ISSUE_TEMPLATE/feature.md vendored Normal file
View File

@@ -0,0 +1,17 @@
---
name: "\U0001F680 Feature request"
about: If you have a suggestion
---
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is, e.g., I have an issue when [...] -->
**Describe a solution you'd like**
<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View File

@@ -1 +0,0 @@
**Before submitting a pull request,** please make sure you have followed the steps the CONTRIBUTING guide.

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
coverage
dist
node_modules
packages/**/vendor/*
packages/examples

45
LICENSE
View File

@@ -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.

189
README.md
View File

@@ -2,69 +2,70 @@
[![npm version][package-badge]][package-url] [![Build Status][ci-badge]][ci-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
"React Native for Web" brings the platform-agnostic Components and APIs of
[React Native][react-native-url] to the Web.
**Compatibility: React Native 0.55**.
* **High-quality user interfaces**: React Native for Web makes it easy to
create [fast](packages/benchmarks/README.md), adaptive web UIs in JavaScript.
It provides native-like interactions, support for multiple input modes (touch,
mouse, keyboard), optimized vendor-prefixed styles, built-in support for RTL
layout, built-in accessibility, and integrates with React Dev Tools.
"React Native for Web" makes it possible to run [React
Native][react-native-url] components and APIs on the web using React DOM. Check
out the live demo of the [React Native examples][examples-url] running on the
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.
* **High-quality web interfaces**: 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-quality 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.
Who is using React Native for Web? [Twitter](https://mobile.twitter.com),
[Major League Soccer](https://matchcenter.mlssoccer.com), [The
Times](https://github.com/newsuk/times-components), [React Native's
documentation](http://facebook.github.io/react-native/).
* **Write once, render anywhere**: 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.
Browser support: Chrome, Firefox, Safari >= 7, IE 10, Edge.
Who is using React Native in production web apps?
[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).
Browser support: Chrome, Firefox, Edge, Safari 7+, IE 10+.
## Quick start
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-web-playground).
You dont need to install anything to try it out.
The easiest way to get started is to edit this
[CodeSandbox](https://codesandbox.io/s/q4qymyp2l6) template (or
[Glitch](https://glitch.com/edit/#!/react-native)). You dont need to install
anything to try it out.
If you are unfamiliar with setting up a React web project, please follow the
recommendations in the [React documentation](https://reactjs.org/).
For installation and configuration details please read the [getting
started](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/getting-started.md)
guide.
## Documentation
You can find the React Native for Web API documentation [on the
website][website-url].
You can find the API documentation [on the website][website-url].
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).
### Installation
Install using `yarn` or `npm`:
```
yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web
```
### Guides
* [Getting started](website/guides/getting-started.md)
* [Style](website/guides/style.md)
* [Accessibility](website/guides/accessibility.md)
* [Internationalization](website/guides/internationalization.md)
* [Direct manipulation](website/guides/direct-manipulation.md)
* [Advanced use](website/guides/advanced.md)
* [Getting started](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/getting-started.md)
* [Style](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/style.md)
* [Accessibility](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/accessibility.md)
* [Internationalization](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/internationalization.md)
* [Direct manipulation](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/direct-manipulation.md)
* [Experimental / unstable use](https://github.com/necolas/react-native-web/blob/master/packages/website/guides/advanced.md)
## Examples
There are several examples [on the website][website-url] and in the [website's
source code](./website). Here is an example to get you started:
There are examples [on the website][website-url] ([source
code](https://github.com/necolas/react-native-web/blob/master/packages/website).
And all the [React Native examples][examples-url] ([source
code](https://github.com/necolas/react-native-web/blob/master/packages/examples))
are also available. Here is an example to get you started:
```js
import React from 'react';
@@ -95,6 +96,105 @@ 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:
* [Gatsby](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-react-native-web)
* [Next.js](https://github.com/zeit/next.js/tree/master/examples/with-react-native-web)
* [Phenomic](https://github.com/phenomic/phenomic/tree/master/examples/react-native-web-app)
* [Razzle](https://github.com/jaredpalmer/razzle/tree/master/examples/with-react-native-web)
* [Storybook](https://github.com/necolas/react-native-web/tree/master/packages/website/storybook/.storybook)
* [Styleguidist](https://github.com/styleguidist/react-styleguidist/tree/master/examples/react-native)
Example recipes for web-specific UI patterns:
* [Links](https://codesandbox.io/s/53r88k5opx)
* [Hover styles](https://codesandbox.io/s/o9q8vy70l5)
* [Root element styles](https://codesandbox.io/s/52x1871vjl)
## Compatibility with React Native
React Native v0.55
### Components
| Name | Status | Notes |
| :----------------------- | :------------------ | :---- |
| ActivityIndicator | Available | |
| ART | Available | |
| Button | Available | |
| CheckBox | Available | |
| FlatList | Available | |
| Image | Available (partial) | Missing multiple sources and HTTP headers. |
| ImageBackground | Available | |
| KeyboardAvoidingView | Available (mock) | |
| ListView | Available | |
| Modal | Not started | |
| Picker | Available | |
| RefreshControl | Not started | |
| SafeAreaView | Available | |
| ScrollView | Available (partial) | Missing momentum scroll events. |
| SectionList | Available | |
| Slider | Not started | |
| StatusBar | Mock | |
| SwipeableFlatList | Available | |
| SwipeableListView | Available | |
| Switch | Available | |
| Text | Available (partial) | Missing `onLongPress` support. |
| TextInput | Available (partial) | Missing rich text features and auto-expanding behaviour. |
| Touchable | Available | Includes additional support for mouse and keyboard interactions. |
| TouchableHighlight | Available | |
| TouchableNativeFeedback | Not started | |
| TouchableOpacity | Available | |
| TouchableWithoutFeedback | Available | |
| View | Available | |
| VirtualizedList | Available | |
| WebView | Not started | |
| YellowBox | Mock | |
### Modules
| Name | Status | Notes |
| :----------------------- | :------------------ | :---- |
| AccessibilityInfo | Mock | No equivalent web APIs. |
| Alert | Not started | |
| Animated | Available | Missing `useNativeDriver` support. |
| AppRegistry | Available | Includes additional support for SSR with `getApplication`. |
| AppState | Available | |
| AsyncStorage | Available | |
| BackHandler | Mock | No equivalent web APIs. |
| CameraRoll | Not started | No equivalent web APIs. |
| Clipboard | Available | |
| ColorPropType | Available | |
| DeviceInfo | Available (partial) | |
| Dimensions | Available | |
| Easing | Available | |
| EdgeInsetsPropType | Available | |
| Geolocation | Available | |
| I18nManager | Available | Includes additional support for runtime switch to RTL. |
| ImageEditor | Not started | No equivalent web APIs. |
| ImageStore | Not started | No equivalent web APIs. |
| InteractionManager | Available (partial) | |
| Keyboard | Mock | |
| LayoutAnimation | Available (partial) | Missing transform to web animation. |
| Linking | Available | |
| NativeEventEmitter | Available | |
| NativeMethodsMixin | Available | |
| NativeModules | Available (partial) | Mocked. Missing ability to load native modules. |
| NetInfo | Available (partial) | Missing functionality to detect extensive connections. |
| PanResponder | Available | |
| PixelRatio | Available | |
| Platform | Available | |
| PointPropType | Available | |
| Settings | Not started | |
| Share | Available | Only available over HTTPS. Read about the [Web Share API](https://wicg.github.io/web-share/). |
| StyleSheet | Available | |
| TextPropTypes | Available | |
| UIManager | Available | |
| Vibration | Available | |
| ViewPropTypes | Available | |
## Contributing
The main purpose of this repository is to help evolve React web and native
@@ -124,14 +224,17 @@ 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
[examples-url]: https://necolas.github.io/react-native-web/examples/
[website-url]: https://necolas.github.io/react-native-web/storybook/
[react-native-url]: https://facebook.github.io/react-native/
[contributing-url]: ./.github/CONTRIBUTING.md
[contributing-url]: https://github.com/necolas/react-native-web/blob/master/.github/CONTRIBUTING.md
[good-first-issue-url]: https://github.com/necolas/react-native-web/labels/good%20first%20issue
[code-of-conduct]: https://code.facebook.com/codeofconduct

View File

@@ -1,10 +0,0 @@
{
"lerna": "2.5.1",
"version": "0.3.0",
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*",
"website"
]
}

View File

@@ -1,29 +1,35 @@
{
"private": true,
"version": "0.8.9",
"name": "react-native-web-monorepo",
"scripts": {
"benchmark": "cd packages/benchmarks && yarn benchmark",
"clean": "del ./packages/*/dist",
"compile": "yarn clean && cd packages/react-native-web && babel src --out-dir dist --ignore \"**/__tests__\"",
"docs:start": "cd website && yarn start",
"docs:release": "cd website && yarn release",
"compile": "npm-run-all clean -p \"compile:* -- {@}\" --",
"compile:commonjs": "cd packages/react-native-web && BABEL_ENV=commonjs babel src --out-dir dist/cjs --ignore \"**/__tests__\"",
"compile:es": "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",
"examples": "cd packages/examples && yarn build",
"examples:release": "cd packages/examples && yarn release",
"website": "cd packages/website && yarn start",
"website:release": "cd packages/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",
"fmt": "prettier --write \"**/*.js\"",
"jest": "BABEL_ENV=commonjs jest --config ./scripts/jest/config.js",
"lint": "yarn lint:check --fix",
"lint:check": "eslint packages scripts website",
"lint:check": "eslint packages scripts",
"precommit": "lint-staged",
"prerelease": "yarn test && yarn compile",
"prerelease": "yarn test && yarn compile && yarn compile:commonjs",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn docs:release",
"test": "yarn flow && yarn lint:check && yarn jest"
"postrelease": "yarn benchmarks:release && yarn examples:release && yarn website:release",
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.2",
"babel-plugin-add-module-exports": "^0.2.1",
"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",
@@ -33,31 +39,37 @@
"babel-preset-react-native": "^4.0.0",
"caniuse-api": "^2.0.0",
"del-cli": "^1.1.0",
"enzyme": "^3.2.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.0",
"enzyme-to-json": "^3.2.2",
"eslint": "^4.12.1",
"enzyme-to-json": "^3.3.3",
"eslint": "^4.19.1",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"flow-bin": "^0.61.0",
"eslint-plugin-promise": "^3.7.0",
"eslint-plugin-react": "^7.7.0",
"flow-bin": "^0.63.1",
"glob": "^7.1.2",
"husky": "^0.14.3",
"jest": "^21.2.1",
"lerna": "^2.5.1",
"lint-staged": "^4.1.3",
"prettier": "^1.8.2",
"raf": "^3.4.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-test-renderer": "^16.2.0"
"jest": "^22.4.3",
"jest-canvas-mock": "^1.0.2",
"lint-staged": "^7.1.0",
"npm-run-all": "^4.1.3",
"prettier": "^1.12.1",
"react": "^16.4.1",
"react-art": "^16.4.1",
"react-dom": "^16.4.1",
"react-test-renderer": "^16.4.1"
},
"workspaces": [
"packages/*",
"website"
"packages/*"
],
"lint-staged": {
"packages/react-native-web/src/index.js": [
"node ./scripts/babel/createModuleMap.js",
"prettier --write ./packages/babel-plugin-react-native-web/src/moduleMap.js",
"git add ./packages/babel-plugin-react-native-web/src/moduleMap.js"
],
"**/*.js": [
"fmt:cmd",
"prettier --write",
"git update-index --again",
"eslint"
]
@@ -67,5 +79,5 @@
"singleQuote": true
},
"author": "Nicolas Gallagher",
"license": "BSD-3-Clause"
"license": "MIT"
}

View File

@@ -1,5 +1,7 @@
# babel-plugin-react-native-web
[![npm version][package-badge]][package-url] [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](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).
@@ -15,10 +17,18 @@ yarn add --dev babel-plugin-react-native-web
```
{
"plugins": ["react-native-web"]
"plugins": [
["react-native-web", { commonjs: true }]
]
}
```
You should configure the plugin to match the module format used by your
bundler. Most modern bundlers will use a package's ES modules by default (i.e.,
if `package.json` has a `module` field). But if you need the plugin to rewrite
import paths to point to CommonJS modules, you must set the `commonjs` option
to `true`.
## Example
NOTE: `react-native-web` internal paths are _not stable_ and you must not rely
@@ -34,6 +44,9 @@ import { StyleSheet, View } from 'react-native';
**After**
```js
import StyleSheet from 'react-native-web/dist/apis/StyleSheet';
import View from 'react-native-web/dist/components/View';
import StyleSheet from 'react-native-web/dist/exports/StyleSheet';
import View from 'react-native-web/dist/exports/View';
```
[package-badge]: https://img.shields.io/npm/v/babel-plugin-react-native-web.svg?style=flat
[package-url]: https://yarnpkg.com/en/package/babel-plugin-react-native-web

View File

@@ -1,10 +1,10 @@
{
"name": "babel-plugin-react-native-web",
"version": "0.3.0",
"version": "0.8.9",
"description": "Babel plugin for React Native for Web",
"main": "index.js",
"devDependencies": {
"babel-plugin-tester": "^4.0.0"
"babel-plugin-tester": "^5.0.0"
},
"author": "Nicolas Gallagher",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`export from "react-native" 1`] = `
exports[`Rewrite react-native to react-native-web export from "react-native": export from "react-native" 1`] = `
"
export { View } from 'react-native';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';
@@ -15,7 +15,7 @@ export { default as createElement } from 'react-native-web/dist/exports/createEl
"
`;
exports[`export from "react-native-web" 1`] = `
exports[`Rewrite react-native to react-native-web export from "react-native-web": export from "react-native-web" 1`] = `
"
export { View } from 'react-native-web';
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';
@@ -30,7 +30,7 @@ export { default as createElement } from 'react-native-web/dist/exports/createEl
"
`;
exports[`import from "native-native" 1`] = `
exports[`Rewrite react-native to react-native-web import from "native-native": import from "native-native" 1`] = `
"
import ReactNative from 'react-native';
import { View } from 'react-native';
@@ -41,14 +41,32 @@ 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/exports/Invalid';
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`] = `
exports[`Rewrite react-native to react-native-web import from "native-native": import from "native-native" 2`] = `
"
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/cjs/index';
import View from 'react-native-web/dist/cjs/exports/View';
import { Invalid } from 'react-native-web/dist/cjs/index';
import MyView from 'react-native-web/dist/cjs/exports/View';
import ViewPropTypes from 'react-native-web/dist/cjs/exports/ViewPropTypes';
import * as ReactNativeModules from 'react-native-web/dist/cjs/index';
"
`;
exports[`Rewrite react-native to react-native-web import from "react-native-web": import from "react-native-web" 1`] = `
"
import { createElement } from 'react-native-web';
import { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } from 'react-native-web';
@@ -66,7 +84,43 @@ import * as ReactNativeModules from 'react-native-web/dist/index';
"
`;
exports[`require "react-native-web" 1`] = `
exports[`Rewrite react-native to react-native-web require "react-native": 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[`Rewrite react-native to react-native-web require "react-native": require "react-native" 2`] = `
"
const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web/dist/cjs/index').default;
const View = require('react-native-web/dist/cjs/exports/View').default;
const StyleSheet = require('react-native-web/dist/cjs/exports/StyleSheet').default;
const TouchableOpacity = require('react-native-web/dist/cjs/exports/TouchableOpacity').default;
"
`;
exports[`Rewrite react-native to react-native-web require "react-native-web": require "react-native-web" 1`] = `
"
const ReactNative = require('react-native-web');
const { createElement } = require('react-native-web');
@@ -74,18 +128,18 @@ const { ColorPropType, StyleSheet, View, TouchableOpacity, processColor } = requ
↓ ↓ ↓ ↓ ↓ ↓
const ReactNative = require('react-native-web');
const ReactNative = require('react-native-web/dist/index').default;
const createElement = require('react-native-web/dist/exports/createElement');
const createElement = require('react-native-web/dist/exports/createElement').default;
const ColorPropType = require('react-native-web/dist/exports/ColorPropType');
const ColorPropType = require('react-native-web/dist/exports/ColorPropType').default;
const StyleSheet = require('react-native-web/dist/exports/StyleSheet');
const StyleSheet = require('react-native-web/dist/exports/StyleSheet').default;
const View = require('react-native-web/dist/exports/View');
const View = require('react-native-web/dist/exports/View').default;
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity');
const TouchableOpacity = require('react-native-web/dist/exports/TouchableOpacity').default;
const processColor = require('react-native-web/dist/exports/processColor');
const processColor = require('react-native-web/dist/exports/processColor').default;
"
`;

View File

@@ -11,6 +11,15 @@ import { Invalid, View as MyView, ViewPropTypes } from 'react-native';
import * as ReactNativeModules from 'react-native';`,
snapshot: true
},
{
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,
pluginOptions: { commonjs: true }
},
{
title: 'import from "react-native-web"',
code: `import { createElement } from 'react-native-web';
@@ -30,6 +39,21 @@ export { ColorPropType, StyleSheet, Text, createElement } from 'react-native';`,
export { ColorPropType, StyleSheet, Text, createElement } from 'react-native-web';`,
snapshot: true
},
{
title: 'require "react-native"',
code: `const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');`,
snapshot: true
},
{
title: 'require "react-native"',
code: `const ReactNative = require('react-native');
const { View } = require('react-native');
const { StyleSheet, TouchableOpacity } = require('react-native');`,
snapshot: true,
pluginOptions: { commonjs: true }
},
{
title: 'require "react-native-web"',
code: `const ReactNative = require('react-native-web');

View File

@@ -1,5 +1,15 @@
const getDistLocation = importName =>
importName ? `react-native-web/dist/exports/${importName}` : undefined;
const moduleMap = require('./moduleMap');
const isCommonJS = opts => opts.commonjs === true;
const getDistLocation = (importName, opts) => {
const format = isCommonJS(opts) ? 'cjs/' : '';
if (importName === 'index') {
return `react-native-web/dist/${format}index`;
} else if (importName && moduleMap[importName]) {
return `react-native-web/dist/${format}exports/${importName}`;
}
};
const isReactNativeRequire = (t, node) => {
const { declarations } = node;
@@ -8,7 +18,7 @@ const isReactNativeRequire = (t, node) => {
}
const { id, init } = declarations[0];
return (
t.isObjectPattern(id) &&
(t.isObjectPattern(id) || t.isIdentifier(id)) &&
t.isCallExpression(init) &&
t.isIdentifier(init.callee) &&
init.callee.name === 'require' &&
@@ -33,7 +43,7 @@ module.exports = function({ types: t }) {
.map(specifier => {
if (t.isImportSpecifier(specifier)) {
const importName = specifier.imported.name;
const distLocation = getDistLocation(importName);
const distLocation = getDistLocation(importName, state.opts);
if (distLocation) {
return t.importDeclaration(
@@ -44,7 +54,7 @@ module.exports = function({ types: t }) {
}
return t.importDeclaration(
[specifier],
t.stringLiteral('react-native-web/dist/index')
t.stringLiteral(getDistLocation('index', state.opts))
);
})
.filter(Boolean);
@@ -60,7 +70,7 @@ module.exports = function({ types: t }) {
if (t.isExportSpecifier(specifier)) {
const exportName = specifier.exported.name;
const localName = specifier.local.name;
const distLocation = getDistLocation(localName);
const distLocation = getDistLocation(localName, state.opts);
if (distLocation) {
return t.exportNamedDeclaration(
@@ -73,7 +83,7 @@ module.exports = function({ types: t }) {
return t.exportNamedDeclaration(
null,
[specifier],
t.stringLiteral('react-native-web/dist/index')
t.stringLiteral(getDistLocation('index', state.opts))
);
})
.filter(Boolean);
@@ -84,21 +94,41 @@ module.exports = function({ types: t }) {
VariableDeclaration(path, state) {
if (isReactNativeRequire(t, path.node)) {
const { id } = path.node.declarations[0];
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name);
if (distLocation) {
return t.variableDeclaration(path.node.kind, [
t.variableDeclarator(
t.identifier(identifier.value.name),
t.callExpression(t.identifier('require'), [t.stringLiteral(distLocation)])
)
]);
}
})
.filter(Boolean);
if (t.isObjectPattern(id)) {
const imports = id.properties
.map(identifier => {
const distLocation = getDistLocation(identifier.key.name, state.opts);
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);
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(getDistLocation('index', state.opts))
]),
t.identifier('default')
)
)
]);
path.replaceWith(importIndex);
}
}
}
}

View File

@@ -0,0 +1,70 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
module.exports = {
ART: true,
AccessibilityInfo: true,
ActivityIndicator: true,
Alert: true,
Animated: true,
AppRegistry: true,
AppState: true,
AsyncStorage: true,
BackHandler: true,
Button: true,
CheckBox: true,
Clipboard: true,
ColorPropType: true,
DeviceInfo: true,
Dimensions: true,
Easing: true,
EdgeInsetsPropType: true,
FlatList: true,
I18nManager: true,
Image: true,
ImageBackground: true,
InteractionManager: true,
Keyboard: true,
KeyboardAvoidingView: true,
LayoutAnimation: true,
Linking: true,
ListView: true,
Modal: true,
NativeEventEmitter: 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,
Share: true,
Slider: true,
StatusBar: true,
StyleSheet: true,
SwipeableFlatList: true,
SwipeableListView: 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,
YellowBox: true,
createElement: true,
findNodeHandle: true,
processColor: true,
render: true,
unmountComponentAtNode: true
};

View File

@@ -1,56 +1,70 @@
# benchmarks
To run these benchmarks:
Try the [benchmarks app](https://necolas.github.io/react-native-web/benchmarks) online.
To run the benchmarks locally:
```
yarn benchmark
yarn benchmarks
open ./packages/benchmarks/dist/index.html
```
To run benchmarks for individual implementations append `?<name>,<name>` to the
URL, e.g., `?css-modules,react-native-web`.
Develop against these benchmarks:
```
yarn compile --watch
yarn benchmarks --watch
```
## Notes
These benchmarks are crude approximations of extreme cases that libraries may
encounter. The deep and wide tree cases look at the performance of mounting and
rendering large trees of styled elements. The Triangle case looks at the
performance of repeated style updates to a large mounted tree. Some libraries
must inject new styles for each "dynamic style", whereas others may not.
Libraries without support for dynamic styles (i.e., they rely on user-authored
inline styles) do not include the `SierpinskiTriangle` benchmark.
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_.
## Results
No benchmark will run for more than 20 seconds.
Typical render timings*: mean ± two standard deviations.
### Mount deep/wide tree
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
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) |
| :--- | ---: | ---: | ---: |
| `react-native-web@0.2.2` | `89.67` `±28.51` | `167.46` `±27.03` | `65.40` `±19.50` |
| `css-modules` | `77.42` 45.50` | `141.44` 33.96` | - |
| `inline-styles` | `236.25` `±95.57` | `477.01` 88.30` | `40.95` 23.53` |
| `css-modules` | `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` |
Other libraries
### Moto G4
| Implementation | Deep tree (ms) | Wide tree (ms) | Triangle (ms) |
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) |
| :--- | ---: | ---: | ---: |
| `styletron@3.0.0-rc.5` | `83.53` 33.55` | `153.12` 39.13` | `56.47` `±24.22` |
| `aphrodite@1.2.5` | `88.23` 31.22` | `164.03` 34.70` | - |
| `glamor@2.20.40` | `110.09` 34.20` | `182.06` `±50.39` | ‡ |
| `emotion@8.0.12` | `103.44` `±32.12` | `204.45` `±41.00` | `110.28` `±26.94` |
| `react-jss@8.2.0` | `136.17` `±59.23` | `270.51` `±69.20` | - |
| `styled-components@2.3.2` | `217.57` `±51.90` | `437.57` `±65.74` | `76.99` `±41.79` |
| `reactxp@0.46.6` | `240.88` `±79.82` | `467.32` `±74.42` | `70.95` `±32.90`|
| `radium@0.19.6` | `400.19` `±94.58` | `816.59` `±91.10` | `71.13` `±27.22` |
These results indicate that render times when using `react-native-web`,
`css-modules`, `aphrodite`, and `styletron` are roughly equivalent and
significantly faster than alternatives.
*MacBook Pro (13-inch, Early 2011); 2.3 GHz Intel Core i5; 8 GB 1333 MHz DDR3. Google Chrome 62.
‡Glamor essentially crashes the browser tab.
| `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` |

View File

@@ -3,9 +3,14 @@
<head>
<meta charset="UTF-8">
<title>Performance tests</title>
<meta name="viewport" content="width=device-width">
<style>
html, body { height: 100%; width: 100%; overflow: hidden; }
.root { height: 100%; overflow: hidden; }
</style>
</head>
<body>
<div class="root"></div>
<script src="dist/performance.bundle.js"></script>
<script src="./bundle.js"></script>
</body>
</html>

View File

@@ -1,33 +1,37 @@
{
"private": true,
"name": "benchmarks",
"version": "0.3.0",
"version": "0.8.9",
"scripts": {
"benchmark": "webpack --config ./webpack.config.js && open index.html"
"build": "mkdir -p dist && cp -f index.html dist/index.html && ./node_modules/.bin/webpack-cli --config ./webpack.config.js",
"release": "yarn build && git checkout gh-pages && rm -rf ../../benchmarks && mv dist ../../benchmarks && git add -A && git commit -m \"Benchmarks deploy\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"aphrodite": "^1.2.5",
"babel-polyfill": "^6.26.0",
"classnames": "^2.2.5",
"d3-scale-chromatic": "^1.1.1",
"emotion": "^8.0.12",
"glamor": "^2.20.40",
"marky": "^1.2.0",
"radium": "^0.19.6",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-jss": "^8.2.0",
"react-native-web": "^0.3.0",
"reactxp": "^0.46.6",
"styled-components": "^2.3.2",
"styletron-client": "^3.0.0-rc.5",
"styletron-utils": "^3.0.0-rc.3"
"aphrodite": "^2.2.2",
"classnames": "^2.2.6",
"d3-scale-chromatic": "^1.3.0",
"emotion": "^9.2.4",
"fela": "^6.1.9",
"glamor": "2.20.40",
"radium": "^0.24.0",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-fela": "^7.3.1",
"react-jss": "^8.6.1",
"react-native-web": "0.8.9",
"reactxp": "^1.3.0",
"styled-components": "^3.3.3",
"styled-jsx": "^2.2.7",
"styletron-engine-atomic": "^1.0.5",
"styletron-react": "^4.3.1"
},
"devDependencies": {
"babel-plugin-react-native-web": "^0.3.0",
"css-loader": "^0.28.7",
"style-loader": "^0.19.1",
"webpack": "^3.10.0",
"webpack-bundle-analyzer": "^2.9.1"
"babel-plugin-react-native-web": "0.8.9",
"css-loader": "^1.0.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.15.1",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-cli": "^3.0.8"
}
}

View File

@@ -0,0 +1,297 @@
/* 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,
flex: 1
}
});

View File

@@ -0,0 +1,253 @@
/**
* The MIT License (MIT)
* Copyright (c) 2017 Paul Armstrong
* https://github.com/paularmstrong/react-component-benchmark
*/
/* 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)
});
}
}

View 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;
};

View 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();
}
}

View 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
};

View File

@@ -0,0 +1,71 @@
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',
fontSize: 20,
fontWeight: '500',
padding: 8,
textAlign: 'center',
textTransform: 'uppercase'
},
buttonDisabled: {
backgroundColor: '#dfdfdf'
},
textDisabled: {
color: '#a1a1a1'
}
});

View 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>
);

View 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' }]
}
});

View 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;

View 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;

View 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, " 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);
});
}

View File

@@ -1,98 +0,0 @@
import * as marky from 'marky';
const fmt = time => `${Math.round(time * 100) / 100}ms`;
const measure = (name, fn) => {
marky.mark(name);
fn();
const performanceMeasure = marky.stop(name);
return performanceMeasure.duration;
};
const mean = values => {
const sum = values.reduce((sum, value) => sum + value, 0);
return sum / values.length;
};
const median = values => {
if (!Array.isArray(values)) {
return 0;
}
if (values.length === 1) {
return values[0];
}
const numbers = [...values].sort((a, b) => a - b);
return (numbers[(numbers.length - 1) >> 1] + numbers[numbers.length >> 1]) / 2;
};
const standardDeviation = values => {
const avg = mean(values);
const squareDiffs = values.map(value => {
const diff = value - avg;
return diff * diff;
});
const meanSquareDiff = mean(squareDiffs);
return Math.sqrt(meanSquareDiff);
};
export const log = (name, description, durations) => {
const stdDev = standardDeviation(durations);
const formattedMean = fmt(mean(durations));
const formattedMedian = fmt(median(durations));
const formattedStdDev = fmt(stdDev);
console.groupCollapsed(`${name}\n${formattedMean} ±${fmt(2 * stdDev)}`);
description && console.log(description);
console.log(`Median: ${formattedMedian}`);
console.log(`Mean: ${formattedMean}`);
console.log(`Standard deviation: ${formattedStdDev}`);
console.log(durations);
console.groupEnd();
};
const benchmark = ({ name, description, setup, teardown, task, runs }) => {
return new Promise(resolve => {
const durations = [];
let i = 0;
setup();
teardown();
const done = () => {
log(name, description, durations);
resolve();
};
const a = () => {
setup();
window.requestAnimationFrame(b);
};
const b = () => {
const duration = measure('mean', task);
durations.push(duration);
window.requestAnimationFrame(c);
};
const c = () => {
teardown();
window.requestAnimationFrame(d);
};
const d = () => {
i += 1;
if (i < runs) {
window.requestAnimationFrame(a);
} else {
window.requestAnimationFrame(done);
}
};
window.requestAnimationFrame(a);
});
};
export default benchmark;

View File

@@ -1,58 +0,0 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
class DeepTree extends Component {
static propTypes = {
breadth: PropTypes.number.isRequired,
components: PropTypes.object,
depth: PropTypes.number.isRequired,
id: PropTypes.number.isRequired,
wrap: PropTypes.number.isRequired
};
/* necessary for reactxp to work without errors */
static childContextTypes = {
focusManager: PropTypes.object
};
getChildContext() {
return {
focusManager: {
addFocusableComponent() {},
removeFocusableComponent() {},
restrictFocusWithin() {},
removeFocusRestriction() {},
limitFocusWithin() {},
removeFocusLimitation() {}
}
};
}
render() {
const { breadth, components, depth, id, wrap } = this.props;
const { Box } = components;
let result = (
<Box color={id % 3} components={components} layout={depth % 2 === 0 ? 'column' : 'row'} outer>
{depth === 0 && <Box color={id % 3 + 3} components={components} fixed />}
{depth !== 0 &&
Array.from({ length: breadth }).map((el, i) => (
<DeepTree
breadth={breadth}
components={components}
depth={depth - 1}
id={i}
key={i}
wrap={wrap}
/>
))}
</Box>
);
for (let i = 0; i < wrap; i++) {
result = <Box components={components}>{result}</Box>;
}
return result;
}
}
export default DeepTree;

View File

@@ -1,17 +1,22 @@
import PropTypes from 'prop-types';
import { BenchmarkType } from '../app/Benchmark';
import { number, object } from 'prop-types';
import React from 'react';
import { interpolatePurples, interpolateBuPu, interpolateRdPu } from 'd3-scale-chromatic';
const targetSize = 25;
const targetSize = 10;
class SierpinskiTriangle extends React.Component {
static displayName = 'SierpinskiTriangle';
static benchmarkType = BenchmarkType.UPDATE;
static propTypes = {
Dot: PropTypes.node,
depth: PropTypes.number,
renderCount: PropTypes.number,
s: PropTypes.number,
x: PropTypes.number,
y: PropTypes.number
components: object,
depth: number,
renderCount: number,
s: number,
x: number,
y: number
};
static defaultProps = {
@@ -20,64 +25,65 @@ class SierpinskiTriangle extends React.Component {
};
render() {
const { x, y, depth, renderCount, Dot } = this.props;
const { components, x, y, depth, renderCount } = this.props;
let { s } = this.props;
const { Dot } = components;
if (s <= targetSize) {
let fn;
switch (depth) {
case 1:
fn = interpolatePurples;
break;
case 2:
fn = interpolateBuPu;
break;
case 3:
default:
fn = interpolateRdPu;
if (Dot) {
if (s <= targetSize) {
let fn;
switch (depth) {
case 1:
fn = interpolatePurples;
break;
case 2:
fn = interpolateBuPu;
break;
case 3:
default:
fn = interpolateRdPu;
}
// introduce randomness to ensure that repeated runs don't produce the same colors
const color = fn(renderCount * Math.random() / 20);
return (
<Dot color={color} size={targetSize} x={x - targetSize / 2} y={y - targetSize / 2} />
);
}
s /= 2;
return (
<Dot
color={fn(renderCount / 20)}
size={targetSize}
x={x - targetSize / 2}
y={y - targetSize / 2}
/>
<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>;
}
s /= 2;
return [
<SierpinskiTriangle
Dot={Dot}
depth={1}
key={1}
renderCount={renderCount}
s={s}
x={x}
y={y - s / 2}
/>,
<SierpinskiTriangle
Dot={Dot}
depth={2}
key={2}
renderCount={renderCount}
s={s}
x={x - s}
y={y + s / 2}
/>,
<SierpinskiTriangle
Dot={Dot}
depth={3}
key={3}
renderCount={renderCount}
s={s}
x={x + s}
y={y + s / 2}
/>
];
}
}

View File

@@ -0,0 +1,45 @@
import { BenchmarkType } from '../app/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;

View File

@@ -1,14 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from './NestedTree';
import React from 'react';
const renderDeepTree = (label, components) =>
createRenderBenchmark({
name: `[${label}] Deep tree`,
runs: 20,
getElement() {
return <NestedTree breadth={3} components={components} depth={6} id={0} wrap={1} />;
}
});
export default renderDeepTree;

View File

@@ -1,112 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import SierpinskiTriangle from './SierpinskiTriangle';
import { log } from '../benchmark';
const node = document.querySelector('.root');
let runs = 20;
class Speedometer extends React.Component {
/* necessary for reactxp to work without errors */
static childContextTypes = {
focusManager: PropTypes.object
};
getChildContext() {
return {
focusManager: {
addFocusableComponent() {},
removeFocusableComponent() {},
restrictFocusWithin() {},
removeFocusRestriction() {},
limitFocusWithin() {},
removeFocusLimitation() {}
}
};
}
static propTypes = {
Dot: PropTypes.node.isRequired,
description: PropTypes.string,
name: PropTypes.string.isRequired,
onComplete: PropTypes.node.isRequired
};
state = { renderCount: -1 };
async componentDidMount() {
const durations = [];
while ((runs -= 1)) {
const prev = window.performance.now();
await new Promise(resolve => {
this.raf = window.requestAnimationFrame(() => {
this.setState({ renderCount: this.state.renderCount + 1 }, () => {
const now = window.performance.now();
durations.push(now - prev);
resolve();
});
});
});
}
const { description, name } = this.props;
log(name, description, durations);
runs = 20;
this.props.onComplete();
}
componentWillUnmount() {
window.cancelAnimationFrame(this.raf);
}
render() {
return (
<div style={styles.wrapper}>
<SierpinskiTriangle
Dot={this.props.Dot}
renderCount={this.state.renderCount}
s={1000}
x={0}
y={0}
/>
</div>
);
}
}
const styles = {
wrapper: {
position: 'absolute',
transformOrigin: '0 0',
left: '50%',
top: '50%',
width: '10px',
height: '10px',
backgroundColor: '#eee',
transform: 'scale(0.33)'
}
};
const renderSierpinskiTriangle = (name, { Dot }) => () => {
return new Promise(resolve => {
/* eslint-disable react/jsx-no-bind */
ReactDOM.render(
<Speedometer
Dot={Dot}
description="Dynamic styles"
name={`[${name}] Triangle`}
onComplete={() => {
ReactDOM.unmountComponentAtNode(node);
resolve();
}}
/>,
node
);
/* eslint-enable react/jsx-no-bind */
});
};
export default renderSierpinskiTriangle;

View File

@@ -1,112 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import React from 'react';
const tweet1 = {
favorite_count: 30,
favorited: true,
id: '834889712556875776',
lang: 'en',
retweet_count: 6,
retweeted: false,
textParts: [
{
prefix: '',
text: 'Living burrito to burrito '
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
},
{
emoji: 'https://abs-0.twimg.com/emoji/v2/svg/1f32f.svg',
isEmoji: true,
prefix: '',
text: '🌯'
}
],
timestamp: 'Feb 23',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const tweet2 = {
favorite_count: 84,
favorited: false,
id: '730896800060579840',
lang: 'en',
media: {
source: {
uri: 'https://pbs.twimg.com/media/CiSqvsJVEAAtLZ1.jpg',
width: 600,
height: 338
}
},
retweet_count: 4,
retweeted: true,
textParts: [
{
prefix: '',
text: 'Presenting '
},
{
displayUrl: 'mobile.twitter.com',
expandedUrl: 'https://mobile.twitter.com',
isEntity: true,
isUrl: true,
linkRelation: 'nofollow',
prefix: '',
text: '',
textDirection: 'ltr',
url: 'https://t.co/4hRCAxiUUG'
},
{
prefix: '',
text: ' with '
},
{
isEntity: true,
isMention: true,
prefix: '@',
text: 'davidbellona',
textDirection: 'ltr',
url: '/davidbellona'
},
{
prefix: '',
text: " at Twitter's all hands meeting "
}
],
timestamp: 'May 12',
user: {
fullName: 'Nicolas',
screenName: 'necolas',
profileImageUrl: 'https://pbs.twimg.com/profile_images/804365942360719360/dQnPejph_normal.jpg'
}
};
const renderTweet = (label, { Tweet }) =>
createRenderBenchmark({
name: `[${label}] Tweet`,
runs: 10,
getElement() {
return (
<div style={{ width: 500 }}>
<Tweet tweet={tweet1} />
<Tweet tweet={tweet2} />
</div>
);
}
});
export default renderTweet;

View File

@@ -1,14 +0,0 @@
import createRenderBenchmark from '../createRenderBenchmark';
import NestedTree from './NestedTree';
import React from 'react';
const renderWideTree = (label, components) =>
createRenderBenchmark({
name: `[${label}] Wide tree`,
runs: 20,
getElement() {
return <NestedTree breadth={10} components={components} depth={3} id={0} wrap={4} />;
}
});
export default renderWideTree;

View File

@@ -1,24 +0,0 @@
import benchmark from './benchmark';
import ReactDOM from 'react-dom';
const node = document.querySelector('.root');
const createRenderBenchmark = ({ description, getElement, name, runs }) => () => {
const setup = () => {};
const teardown = () => {
ReactDOM.unmountComponentAtNode(node);
};
return benchmark({
name,
description,
runs,
setup,
teardown,
task: () => {
ReactDOM.render(getElement(), node);
}
});
};
export default createRenderBenchmark;

View 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;

View File

@@ -17,32 +17,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = StyleSheet.create({
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
});

View File

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

View File

@@ -1,7 +1,9 @@
import Box from './Box';
import Provider from './Provider';
import View from './View';
export default {
Box,
Provider,
View
};

View File

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

View File

@@ -1,4 +1,5 @@
.outer {
align-self: flex-start;
padding: 4px;
}
@@ -7,30 +8,30 @@
}
.color0 {
background-color: #222;
background-color: #14171A;
}
.color1 {
background-color: #666;
background-color: #AAB8C2;
}
.color2 {
background-color: #999;
background-color: #E6ECF0;
}
.color3 {
background-color: blue;
background-color: #FFAD1F;
}
.color4 {
background-color: orange;
background-color: #F45D22;
}
.color5 {
background-color: red;
background-color: #E0245E;
}
.fixed {
width: 20px;
height: 20px;
width: 6px;
height: 6px;
}

View File

@@ -1,7 +1,9 @@
import Box from './Box';
import Provider from './Provider';
import View from './View';
export default {
Box,
Provider,
View
};

View File

@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
};

View File

@@ -9,8 +9,8 @@ const Dot = ({ size, x, y, children, color }) => (
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
marginLeft: `${x}px`,
marginTop: `${y}px`
})}
>
{children}
@@ -25,7 +25,8 @@ const styles = {
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};

View File

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

View File

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

View File

@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
};

View File

@@ -9,8 +9,8 @@ const Dot = ({ size, x, y, children, color }) => (
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
marginLeft: `${x}px`,
marginTop: `${y}px`
})}
>
{children}
@@ -25,7 +25,8 @@ const styles = {
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};

View File

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

View File

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

View File

@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
};

View File

@@ -10,8 +10,8 @@ const Dot = ({ size, x, y, children, color }) => (
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
marginLeft: `${x}px`,
marginTop: `${y}px`
}
}}
>
@@ -27,7 +27,8 @@ const styles = {
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};

View File

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

View File

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

View File

@@ -17,32 +17,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
};

View File

@@ -11,8 +11,8 @@ const Dot = ({ size, x, y, children, color }) => (
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
marginLeft: `${x}px`,
marginTop: `${y}px`
}
]}
>
@@ -28,7 +28,8 @@ const styles = {
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
}
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { createRenderer } from 'fela';
import { Provider as FelaProvider } from 'react-fela';
import View from './View';
const renderer = createRenderer();
class Provider extends React.Component {
render() {
return (
<FelaProvider renderer={renderer}>
<View>{this.props.children}</View>
</FelaProvider>
);
}
}
export default Provider;

View File

@@ -0,0 +1,24 @@
/* eslint-disable react/prop-types */
import { createComponent } from 'react-fela';
const View = createComponent(
() => ({
alignItems: 'stretch',
borderWidth: '0px',
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: '0',
margin: '0px',
padding: '0px',
position: 'relative',
// fix flexbox bugs
minHeight: '0px',
minWidth: '0px'
}),
'div'
);
export default View;

View File

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

View File

@@ -18,32 +18,33 @@ const Box = ({ classes, color, fixed = false, layout = 'column', outer = false,
const styles = {
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
};

View File

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

View File

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

View File

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

View File

@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = StyleSheet.create({
outer: {
alignSelf: 'flex-start',
padding: 4
},
row: {
flexDirection: 'row'
},
color0: {
backgroundColor: '#222'
backgroundColor: '#14171A'
},
color1: {
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
},
color2: {
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
},
color3: {
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
},
color4: {
backgroundColor: 'orange'
backgroundColor: '#F45D22'
},
color5: {
backgroundColor: 'red'
backgroundColor: '#E0245E'
},
fixed: {
width: 20,
height: 20
width: 6,
height: 6
}
});

View File

@@ -11,8 +11,8 @@ const Dot = ({ size, x, y, children, color }) =>
borderRightWidth: size / 2,
borderBottomWidth: size / 2,
borderLeftWidth: size / 2,
left: x,
top: y
marginLeft: x,
marginTop: y
}
]
});
@@ -25,7 +25,8 @@ const styles = StyleSheet.create({
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: [{ translateX: '50%' }, { translateY: '50%' }]
}
});

View File

@@ -0,0 +1,2 @@
import { View } from 'react-native';
export default View;

View File

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

View File

@@ -16,32 +16,33 @@ const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other
const styles = {
outer: Styles.createViewStyle({
alignSelf: 'flex-start',
padding: 4
}),
row: Styles.createViewStyle({
flexDirection: 'row'
}),
color0: Styles.createViewStyle({
backgroundColor: '#222'
backgroundColor: '#14171A'
}),
color1: Styles.createViewStyle({
backgroundColor: '#666'
backgroundColor: '#AAB8C2'
}),
color2: Styles.createViewStyle({
backgroundColor: '#999'
backgroundColor: '#E6ECF0'
}),
color3: Styles.createViewStyle({
backgroundColor: 'blue'
backgroundColor: '#FFAD1F'
}),
color4: Styles.createViewStyle({
backgroundColor: 'orange'
backgroundColor: '#F45D22'
}),
color5: Styles.createViewStyle({
backgroundColor: 'red'
backgroundColor: '#E0245E'
}),
fixed: Styles.createViewStyle({
width: 20,
height: 20
width: 6,
height: 6
})
};

View File

@@ -12,8 +12,8 @@ const Dot = ({ size, x, y, children, color }) => (
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
marginLeft: `${x}px`,
marginTop: `${y}px`
}
]}
/>
@@ -27,7 +27,8 @@ const styles = {
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
borderTopWidth: 0,
transform: 'translate(50%, 50%)'
})
};

View File

@@ -0,0 +1,31 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { object } from 'prop-types';
import { View } from 'reactxp';
class Provider extends React.Component {
/* this mock context is necessary for reactxp to work without errors… ¯\_(ツ)_/¯ */
static childContextTypes = {
focusManager: object
};
getChildContext() {
return {
focusManager: {
addFocusableComponent() {},
removeFocusableComponent() {},
restrictFocusWithin() {},
removeFocusRestriction() {},
limitFocusWithin() {},
removeFocusLimitation() {}
}
};
}
render() {
return <View style={{ overflow: 'visible' }}>{this.props.children}</View>;
}
}
export default Provider;

View File

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

View File

@@ -4,28 +4,28 @@ import View from './View';
const getColor = color => {
switch (color) {
case 0:
return '#222';
return '#14171A';
case 1:
return '#666';
return '#AAB8C2';
case 2:
return '#999';
return '#E6ECF0';
case 3:
return 'blue';
return '#FFAD1F';
case 4:
return 'orange';
return '#F45D22';
case 5:
return 'red';
return '#E0245E';
default:
return 'transparent';
}
};
const Box = styled(View)`
align-self: flex-start;
flex-direction: ${props => (props.layout === 'column' ? 'column' : 'row')};
padding: ${props => (props.outer ? '4px' : '0')};
height: ${props => (props.fixed ? '20px' : 'auto')};
width: ${props => (props.fixed ? '20px' : 'auto')};
background-color: ${props => getColor(props.color)};
${props => props.fixed && 'height:6px;'} ${props =>
props.fixed && 'width:6px;'} background-color: ${props => getColor(props.color)};
`;
export default Box;

View File

@@ -4,8 +4,8 @@ import View from './View';
const Dot = styled(View).attrs({
style: props => ({
left: `${props.x}px`,
top: `${props.y}px`,
marginLeft: `${props.x}px`,
marginTop: `${props.y}px`,
borderRightWidth: `${props.size / 2}px`,
borderBottomWidth: `${props.size / 2}px`,
borderLeftWidth: `${props.size / 2}px`,
@@ -19,6 +19,7 @@ const Dot = styled(View).attrs({
border-color: transparent;
border-style: solid;
border-top-width: 0;
transform: translate(50%, 50%);
`;
export default Dot;

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import View from './View';
const getColor = color => {
switch (color) {
case 0:
return '#14171A';
case 1:
return '#AAB8C2';
case 2:
return '#E6ECF0';
case 3:
return '#FFAD1F';
case 4:
return '#F45D22';
case 5:
return '#E0245E';
default:
return 'transparent';
}
};
const Box = props => {
const { className, children: styles } = (
<scope className={classnames('Box', props.fixed && 'fixed')}>
<style jsx>{`
.Box {
align-self: flex-start;
flex-direction: ${props.layout === 'column' ? 'column' : 'row'};
padding: ${props.outer ? '4px' : '0'};
background-color: ${getColor(props.color)};
}
.fixed {
height: 6px;
width: 6px;
}
`}</style>
</scope>
).props;
return <View className={className}>{[props.children, styles]}</View>;
};
export default Box;

View File

@@ -0,0 +1,36 @@
/* eslint-disable react/prop-types */
import React from 'react';
import View from './View';
const Dot = props => {
const { className, children: styles } = (
<scope className="Dot">
<style jsx>{`
.Dot {
position: absolute;
cursor: pointer;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-top-width: 0;
transform: translate(50%, 50%);
}
`}</style>
<style jsx>{`
.Dot {
margin-left: ${props.x}px;
margin-top: ${props.y}px;
border-right-width: ${props.size / 2}px;
border-bottom-width: ${props.size / 2}px;
border-left-width: ${props.size / 2}px;
border-bottom-color: ${props.color};
}
`}</style>
</scope>
).props;
return <View className={className}>{[props.children, styles]}</View>;
};
export default Dot;

View File

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

View File

@@ -0,0 +1,32 @@
/* eslint-disable react/prop-types */
import React from 'react';
class View extends React.Component {
render() {
const { children, className, ...props } = this.props;
return (
<div {...props} className={`initial ${className}`}>
{children}
<style jsx>{`
.initial {
align-items: stretch;
border-width: 0;
border-style: solid;
box-sizing: border-box;
display: flex;
flex-basis: auto;
flex-direction: column;
flex-shrink: 0;
margin: 0;
padding: 0;
position: relative;
min-height: 0;
min-width: 0;
}
`}</style>
</div>
);
}
}
export default View;

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { Client as Styletron } from 'styletron-engine-atomic';
import { Provider as StyletronProvider } from 'styletron-react';
import View from './View';
const styletron = new Styletron();
class Provider extends React.Component {
render() {
return (
<StyletronProvider value={styletron}>
<View>{this.props.children}</View>
</StyletronProvider>
);
}
}
export default Provider;

View File

@@ -0,0 +1,26 @@
/* eslint-disable react/prop-types */
import { styled } from 'styletron-react';
const View = styled('div', ({ style }) => ({
...viewStyle,
style
}));
const viewStyle = {
alignItems: 'stretch',
borderWidth: '0px',
borderStyle: 'solid',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: '0',
margin: '0px',
padding: '0px',
position: 'relative',
// fix flexbox bugs
minHeight: '0px',
minWidth: '0px'
};
export default View;

View File

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

View File

@@ -1,49 +0,0 @@
/* eslint-disable react/prop-types */
import { injectStylePrefixed } from 'styletron-utils';
import React from 'react';
import View, { styletron } from './View';
const Box = ({ color, fixed = false, layout = 'column', outer = false, ...other }) => (
<View
{...other}
style={[
styles[`color${color}`],
fixed && styles.fixed,
layout === 'row' && styles.row,
outer && styles.outer
]}
/>
);
const styles = {
outer: injectStylePrefixed(styletron, {
padding: '4px'
}),
row: injectStylePrefixed(styletron, {
flexDirection: 'row'
}),
color0: injectStylePrefixed(styletron, {
backgroundColor: '#222'
}),
color1: injectStylePrefixed(styletron, {
backgroundColor: '#666'
}),
color2: injectStylePrefixed(styletron, {
backgroundColor: '#999'
}),
color3: injectStylePrefixed(styletron, {
backgroundColor: 'blue'
}),
color4: injectStylePrefixed(styletron, {
backgroundColor: 'orange'
}),
color5: injectStylePrefixed(styletron, {
backgroundColor: 'red'
}),
fixed: injectStylePrefixed(styletron, {
width: '20px',
height: '20px'
})
};
export default Box;

View File

@@ -1,37 +0,0 @@
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import React from 'react';
import { injectStylePrefixed } from 'styletron-utils';
import { styletron } from './View';
const Dot = ({ size, x, y, children, color }) => (
<div
className={classnames(
styles.root,
injectStylePrefixed(styletron, {
borderBottomColor: color,
borderRightWidth: `${size / 2}px`,
borderBottomWidth: `${size / 2}px`,
borderLeftWidth: `${size / 2}px`,
left: `${x}px`,
top: `${y}px`
})
)}
>
{children}
</div>
);
const styles = {
root: injectStylePrefixed(styletron, {
position: 'absolute',
cursor: 'pointer',
width: 0,
height: 0,
borderColor: 'transparent',
borderStyle: 'solid',
borderTopWidth: 0
})
};
export default Dot;

View File

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

Some files were not shown because too many files have changed in this diff Show More