diff --git a/.gitignore b/.gitignore index c925c21d..82504a63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /dist +/dist-storybook /node_modules diff --git a/examples/.storybook/config.js b/examples/.storybook/config.js new file mode 100644 index 00000000..f92adf43 --- /dev/null +++ b/examples/.storybook/config.js @@ -0,0 +1,12 @@ +import { configure, addDecorator } from '@kadira/storybook' +import centered from './decorator-centered' + +const context = require.context('../', true, /Example\.js$/) + +addDecorator(centered) + +function loadStories() { + context.keys().forEach(context) +} + +configure(loadStories, module) diff --git a/examples/.storybook/decorator-centered.js b/examples/.storybook/decorator-centered.js new file mode 100644 index 00000000..b1d7978e --- /dev/null +++ b/examples/.storybook/decorator-centered.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { StyleSheet, View } from 'react-native' + +const styles = StyleSheet.create({ + root: { + alignItems: 'center', + height: '100vh', + justifyContent: 'center' + } +}); + +export default function (storyFn) { + return {storyFn()}; +} diff --git a/examples/webpack.config.js b/examples/.storybook/webpack.config.js similarity index 64% rename from examples/webpack.config.js rename to examples/.storybook/webpack.config.js index fd223f49..07c84129 100644 --- a/examples/webpack.config.js +++ b/examples/.storybook/webpack.config.js @@ -1,15 +1,7 @@ const path = require('path') const webpack = require('webpack') -const EXAMPLES_DIRECTORY = __dirname - module.exports = { - devServer: { - contentBase: EXAMPLES_DIRECTORY - }, - entry: { - example: EXAMPLES_DIRECTORY - }, module: { loaders: [ { @@ -17,27 +9,28 @@ module.exports = { exclude: /node_modules/, loader: 'babel-loader', query: { cacheDirectory: true } + }, + { + test: /\.(gif|jpe?g|png|svg)$/, + loader: 'url-loader', + query: { name: '[name].[ext]' } } ] }, - output: { - filename: 'bundle.js' - }, plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development') }), - new webpack.optimize.DedupePlugin(), // https://github.com/animatedjs/animated/issues/40 new webpack.NormalModuleReplacementPlugin( /es6-set/, - path.join(__dirname, '../src/modules/polyfills/Set.js') + path.join(__dirname, '../../src/modules/polyfills/Set.js') ), new webpack.optimize.OccurenceOrderPlugin() ], resolve: { alias: { - 'react-native': path.join(__dirname, '../src') + 'react-native': path.join(__dirname, '../../src') } } } diff --git a/examples/ActivityIndicator/ActivityIndicatorExample.js b/examples/ActivityIndicator/ActivityIndicatorExample.js new file mode 100644 index 00000000..19cf2331 --- /dev/null +++ b/examples/ActivityIndicator/ActivityIndicatorExample.js @@ -0,0 +1,175 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { ActivityIndicator, StyleSheet, View } from 'react-native' +import TimerMixin from 'react-timer-mixin'; + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +const ToggleAnimatingActivityIndicator = React.createClass({ + mixins: [TimerMixin], + + getInitialState() { + return { + animating: true, + }; + }, + + setToggleTimeout() { + this.setTimeout(() => { + this.setState({animating: !this.state.animating}); + this.setToggleTimeout(); + }, 2000); + }, + + componentDidMount() { + this.setToggleTimeout(); + }, + + render() { + return ( + + ); + } +}); + +const examples = [ + { + title: 'Default (small, white)', + render() { + return ( + + ); + } + }, + { + title: 'Gray', + render() { + return ( + + + + + ); + } + }, + { + title: 'Custom colors', + render() { + return ( + + + + + + + ); + } + }, + { + title: 'Large', + render() { + return ( + + ); + } + }, + { + title: 'Large, custom colors', + render() { + return ( + + + + + + + ); + } + }, + { + title: 'Start/stop', + render() { + return ; + } + }, + { + title: 'Custom size', + render() { + return ( + + ); + } + }, +]; + +const styles = StyleSheet.create({ + centering: { + alignItems: 'center', + justifyContent: 'center', + padding: 8, + }, + gray: { + backgroundColor: '#cccccc', + }, + horizontal: { + flexDirection: 'row', + justifyContent: 'space-around', + padding: 8, + }, +}); + +examples.forEach((example) => { + storiesOf('', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/2048/Game2048.js b/examples/Game2048/Game2048.js similarity index 100% rename from examples/2048/Game2048.js rename to examples/Game2048/Game2048.js diff --git a/examples/Game2048/Game2048Example.js b/examples/Game2048/Game2048Example.js new file mode 100644 index 00000000..fab8e072 --- /dev/null +++ b/examples/Game2048/Game2048Example.js @@ -0,0 +1,8 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import Game2048 from './Game2048' + +storiesOf('Game2048', module) + .add('the game', () => ( + + )) diff --git a/examples/2048/GameBoard.js b/examples/Game2048/GameBoard.js similarity index 100% rename from examples/2048/GameBoard.js rename to examples/Game2048/GameBoard.js diff --git a/examples/Image/ImageExample.js b/examples/Image/ImageExample.js new file mode 100644 index 00000000..09bc5aaf --- /dev/null +++ b/examples/Image/ImageExample.js @@ -0,0 +1,658 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { ActivityIndicator, Image, Platform, StyleSheet, Text, View } from 'react-native' + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg=='; + +//var ImageCapInsetsExample = require('./ImageCapInsetsExample'); +//const IMAGE_PREFETCH_URL = 'http://facebook.github.io/origami/public/images/blog-hero.jpg?r=1&t=' + Date.now(); +//var prefetchTask = Image.prefetch(IMAGE_PREFETCH_URL); + +/* +var NetworkImageCallbackExample = React.createClass({ + getInitialState: function() { + return { + events: [], + startLoadPrefetched: false, + mountTime: new Date(), + }; + }, + + componentWillMount() { + this.setState({mountTime: new Date()}); + }, + + render: function() { + var { mountTime } = this.state; + + return ( + + this._loadEventFired(`✔ onLoadStart (+${new Date() - mountTime}ms)`)} + onLoad={() => this._loadEventFired(`✔ onLoad (+${new Date() - mountTime}ms)`)} + onLoadEnd={() => { + this._loadEventFired(`✔ onLoadEnd (+${new Date() - mountTime}ms)`); + this.setState({startLoadPrefetched: true}, () => { + prefetchTask.then(() => { + this._loadEventFired(`✔ Prefetch OK (+${new Date() - mountTime}ms)`); + }, error => { + this._loadEventFired(`✘ Prefetch failed (+${new Date() - mountTime}ms)`); + }); + }); + }} + /> + {this.state.startLoadPrefetched ? + this._loadEventFired(`✔ (prefetched) onLoadStart (+${new Date() - mountTime}ms)`)} + onLoad={() => this._loadEventFired(`✔ (prefetched) onLoad (+${new Date() - mountTime}ms)`)} + onLoadEnd={() => this._loadEventFired(`✔ (prefetched) onLoadEnd (+${new Date() - mountTime}ms)`)} + /> + : null} + + {this.state.events.join('\n')} + + + ); + }, + + _loadEventFired(event) { + this.setState((state) => { + return state.events = [...state.events, event]; + }); + } +}); +*/ + +var NetworkImageExample = React.createClass({ + getInitialState: function() { + return { + error: false, + loading: false, + progress: 0 + }; + }, + render: function() { + var loader = this.state.loading ? + + {this.state.progress}% + + : null; + return this.state.error ? + {this.state.error} : + this.setState({loading: true})} + onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})} + onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})} + onLoad={() => this.setState({loading: false, error: false})}> + {loader} + ; + } +}); + +/* +var ImageSizeExample = React.createClass({ + getInitialState: function() { + return { + width: 0, + height: 0, + }; + }, + componentDidMount: function() { + Image.getSize(this.props.source.uri, (width, height) => { + this.setState({width, height}); + }); + }, + render: function() { + return ( + + + + Actual dimensions:{'\n'} + Width: {this.state.width}, Height: {this.state.height} + + + ); + }, +}); +*/ +/* +var MultipleSourcesExample = React.createClass({ + getInitialState: function() { + return { + width: 30, + height: 30, + }; + }, + render: function() { + return ( + + + + Decrease image size + + + Increase image size + + + Container image size: {this.state.width}x{this.state.height} + + + + + ); + }, + increaseImageSize: function() { + if (this.state.width >= 100) { + return; + } + this.setState({ + width: this.state.width + 10, + height: this.state.height + 10, + }); + }, + decreaseImageSize: function() { + if (this.state.width <= 10) { + return; + } + this.setState({ + width: this.state.width - 10, + height: this.state.height - 10, + }); + }, +}); +*/ + +const examples = [ + { + title: 'Plain Network Image', + description: 'If the `source` prop `uri` property is prefixed with ' + + '"http", then it will be downloaded from the network.', + render: function() { + return ( + + ); + }, + }, + { + title: 'Plain Static Image', + description: 'Static assets should be placed in the source code tree, and ' + + 'required in the same way as JavaScript modules.', + render: function() { + return ( + + + + {/**/} + {/**/} + + ); + }, + }, + /* + { + title: 'Image Loading Events', + render: function() { + return ( + + ); + }, + }, + */ + { + title: 'Error Handler', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + { + title: 'Image Download Progress', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + { + title: 'defaultSource', + description: 'Show a placeholder image when a network image is loading', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + { + title: 'Border Color', + render: function() { + return ( + + + + ); + }, + }, + { + title: 'Border Width', + render: function() { + return ( + + + + ); + }, + }, + { + title: 'Border Radius', + render: function() { + return ( + + + + + ); + }, + }, + { + title: 'Background Color', + render: function() { + return ( + + + + + + + ); + }, + }, + { + title: 'Opacity', + render: function() { + return ( + + + + + + + + + ); + }, + }, + { + title: 'Nesting', + render: function() { + return ( + + + React + + + ); + }, + }, + { + title: 'Tint Color', + description: 'The `tintColor` style prop changes all the non-alpha ' + + 'pixels to the tint color.', + render: function() { + return ( + + + + + + + + + It also works with downloaded images: + + + + + + + + + ); + }, + }, + { + title: 'Resize Mode', + description: 'The `resizeMode` style prop controls how the image is ' + + 'rendered within the frame.', + render: function() { + return ( + + {[smallImage, fullImage].map((image, index) => { + return ( + + + + + Contain + + + + + + Cover + + + + + + + + Stretch + + + + { Platform.OS === 'ios' ? + + + Repeat + + + + : null } + { Platform.OS === 'android' ? + + + Center + + + + : null } + + + ); + })} + + ); + }, + }, + { + title: 'Animated GIF', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + { + title: 'Base64 image', + render: function() { + return ( + + ); + }, + platform: 'ios', + }, + /* + { + title: 'Cap Insets', + description: + 'When the image is resized, the corners of the size specified ' + + 'by capInsets will stay a fixed size, but the center content and ' + + 'borders of the image will be stretched. This is useful for creating ' + + 'resizable rounded buttons, shadows, and other resizable assets.', + render: function() { + return ; + }, + platform: 'ios', + }, + */ + /* + { + title: 'Image Size', + render: function() { + return ; + }, + }, + */ + /* + { + title: 'MultipleSourcesExample', + description: + 'The `source` prop allows passing in an array of uris, so that native to choose which image ' + + 'to diplay based on the size of the of the target image', + render: function() { + return ; + }, + platform: 'android', + }, + */ +]; + +var fullImage = {uri: 'http://facebook.github.io/react/img/logo_og.png'}; +var smallImage = {uri: 'http://facebook.github.io/react/img/logo_small_2x.png'}; + +var styles = StyleSheet.create({ + base: { + width: 38, + height: 38, + }, + progress: { + flex: 1, + alignItems: 'center', + flexDirection: 'row', + width: 100 + }, + leftMargin: { + marginLeft: 10, + }, + background: { + backgroundColor: '#222222' + }, + sectionText: { + marginVertical: 6, + }, + nestedText: { + marginLeft: 12, + marginTop: 20, + backgroundColor: 'transparent', + color: 'white' + }, + resizeMode: { + width: 90, + height: 60, + borderWidth: 0.5, + borderColor: 'black' + }, + resizeModeText: { + fontSize: 11, + marginBottom: 3, + }, + icon: { + width: 15, + height: 15, + }, + horizontal: { + flexDirection: 'row', + }, + gif: { + flex: 1, + height: 200, + }, + base64: { + flex: 1, + height: 50, + resizeMode: 'contain', + }, + touchableText: { + fontWeight: '500', + color: 'blue', + }, +}); + +examples.forEach((example) => { + storiesOf('', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/Image/bunny.png b/examples/Image/bunny.png new file mode 100644 index 00000000..0d94af66 Binary files /dev/null and b/examples/Image/bunny.png differ diff --git a/examples/Image/uie_thumb_normal@2x.png b/examples/Image/uie_thumb_normal@2x.png new file mode 100644 index 00000000..72683dfa Binary files /dev/null and b/examples/Image/uie_thumb_normal@2x.png differ diff --git a/examples/Image/uie_thumb_selected@2x.png b/examples/Image/uie_thumb_selected@2x.png new file mode 100644 index 00000000..79eb69cf Binary files /dev/null and b/examples/Image/uie_thumb_selected@2x.png differ diff --git a/examples/ListView/ListViewExample.js b/examples/ListView/ListViewExample.js new file mode 100644 index 00000000..55fc6502 --- /dev/null +++ b/examples/ListView/ListViewExample.js @@ -0,0 +1,4 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { ListView } from 'react-native' + diff --git a/examples/ScrollView/ScrollViewExample.js b/examples/ScrollView/ScrollViewExample.js new file mode 100644 index 00000000..4a70cb32 --- /dev/null +++ b/examples/ScrollView/ScrollViewExample.js @@ -0,0 +1,57 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { ScrollView, StyleSheet, Text, View } from 'react-native' + +storiesOf('', module) + .add('vertical', () => ( + + console.log('ScrollView.onScroll', e)} + scrollEventThrottle={1} // 1 event per second + style={styles.scrollViewStyle} + > + {Array.from({ length: 50 }).map((item, i) => ( + + {i} + + ))} + + + )) + .add('horizontal', () => ( + + console.log('ScrollView.onScroll', e)} + scrollEventThrottle={1} // 1 event per second + style={styles.scrollViewStyle} + > + {Array.from({ length: 50 }).map((item, i) => ( + + {i} + + ))} + + + )) + +const styles = StyleSheet.create({ + box: { + alignItems: 'center', + flexGrow: 1, + justifyContent: 'center', + borderWidth: 1 + }, + scrollViewContainer: { + height: '200px', + width: 300 + }, + scrollViewStyle: { + borderWidth: '1px' + }, + scrollViewContentContainerStyle: { + padding: '10px' + } +}) diff --git a/examples/Text/TextExample.js b/examples/Text/TextExample.js new file mode 100644 index 00000000..6da259f4 --- /dev/null +++ b/examples/Text/TextExample.js @@ -0,0 +1,471 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { Image, StyleSheet, Text, View } from 'react-native' + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +var Entity = React.createClass({ + render: function() { + return ( + + {this.props.children} + + ); + } +}); + +var AttributeToggler = React.createClass({ + getInitialState: function() { + return {fontWeight: 'bold', fontSize: 15}; + }, + toggleWeight: function() { + this.setState({ + fontWeight: this.state.fontWeight === 'bold' ? 'normal' : 'bold' + }); + }, + increaseSize: function() { + this.setState({ + fontSize: this.state.fontSize + 1 + }); + }, + render: function() { + var curStyle = {fontWeight: this.state.fontWeight, fontSize: this.state.fontSize}; + return ( + + + Tap the controls below to change attributes. + + + See how it will even work on this nested text + + + Toggle Weight + + + Increase Size + + + ); + } +}); + +const examples = [ +{ + title: 'Wrap', + render: function() { + return ( + + The text should wrap if it goes on multiple lines. See, this is going to + the next line. + + ); + }, +}, { + title: 'Padding', + render: function() { + return ( + + This text is indented by 10px padding on all sides. + + ); + }, +}, { + title: 'Font Family', + render: function() { + return ( + + + Cochin + + + Cochin bold + + + Helvetica + + + Helvetica bold + + + Verdana + + + Verdana bold + + + ); + }, +}, { + title: 'Font Size', + render: function() { + return ( + + + Size 23 + + + Size 8 + + + ); + }, +}, { + title: 'Color', + render: function() { + return ( + + + Red color + + + Blue color + + + ); + }, +}, { + title: 'Font Weight', + render: function() { + return ( + + + Move fast and be ultralight + + + Move fast and be light + + + Move fast and be normal + + + Move fast and be bold + + + Move fast and be ultrabold + + + ); + }, +}, { + title: 'Font Style', + render: function() { + return ( + + + Normal text + + + Italic text + + + ); + }, +}, { + title: 'Text Decoration', + render: function() { + return ( + + + Solid underline + + + Double underline with custom color + + + Dashed underline with custom color + + + Dotted underline with custom color + + + None textDecoration + + + Solid line-through + + + Double line-through with custom color + + + Dashed line-through with custom color + + + Dotted line-through with custom color + + + Both underline and line-through + + + ); + }, +}, { + title: 'Nested', + description: 'Nested text components will inherit the styles of their ' + + 'parents (only backgroundColor is inherited from non-Text parents). ' + + ' only supports other and raw text (strings) as children.', + render: function() { + return ( + + + (Normal text, + + (and bold + + (and tiny inherited bold blue) + + ) + + ) + + + (opacity + + (is inherited + + (and accumulated + + (and also applies to the background) + + ) + + ) + + ) + + + Entity Name + + + ); + }, +}, { + title: 'Text Align', + render: function() { + return ( + + + auto (default) - english LTR + + + أحب اللغة العربية auto (default) - arabic RTL + + + left left left left left left left left left left left left left left left + + + center center center center center center center center center center center + + + right right right right right right right right right right right right right + + + justify: this text component{"'"}s contents are laid out with "textAlign: justify" + and as you can see all of the lines except the last one span the + available width of the parent container. + + + ); + }, +}, { + title: 'Letter Spacing', + render: function() { + return ( + + + letterSpacing = 0 + + + letterSpacing = 2 + + + letterSpacing = 9 + + + letterSpacing = -1 + + + ); + }, +}, { + title: 'Spaces', + render: function() { + return ( + + A {'generated'} {' '} {'string'} and some     spaces + + ); + }, +}, { + title: 'Line Height', + render: function() { + return ( + + + A lot of space between the lines of this long passage that should + wrap once. + + + ); + }, +}, { + title: 'Empty Text', + description: 'It\'s ok to have Text with zero or null children.', + render: function() { + return ( + + ); + }, +}, { + title: 'Toggling Attributes', + render: function(): ReactElement { + return ; + }, +}, { + title: 'backgroundColor attribute', + description: 'backgroundColor is inherited from all types of views.', + render: function() { + return ( + + Yellow container background, + + {' '}red background, + + {' '}blue background, + + {' '}inherited blue background, + + {' '}nested green background. + + + + + + ); + }, +}, { + title: 'numberOfLines attribute', + render: function() { + return ( + + + Maximum of one line, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after one line. + + + Maximum of two lines, no matter how much I write here. If I keep writing, it{"'"}ll just truncate after two lines. + + + No maximum lines specified, no matter how much I write here. If I keep writing, it{"'"}ll just keep going and going. + + + ); + }, +}, { + title: 'Text highlighting (tap the link to see highlight)', + render: function() { + return ( + + Lorem ipsum dolor sit amet, null}>consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + ); + }, +}, { + title: 'allowFontScaling attribute', + render: function() { + return ( + + + By default, text will respect Text Size accessibility setting on iOS. + It means that all font sizes will be increased or descreased depending on the value of Text Size setting in + {" "}Settings.app - Display & Brightness - Text Size + + + You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop. + + + This text will not scale. + + + ); + }, +}, { + title: 'Inline views', + render: function() { + return ( + + + This text contains an inline blue view and + an inline image . Neat, huh? + + + ); + }, +}, { + title: 'Text shadow', + render: function() { + return ( + + + Demo text shadow + + + ); + }, +}, { + title: 'Line break mode', + render: function() { + return ( + + + This very long text should be truncated with dots in the end. + + + This very long text should be truncated with dots in the middle. + + + This very long text should be truncated with dots in the beginning. + + + This very looooooooooooooooooooooooooooong text should be clipped. + + + ); + }, +}]; + +var styles = StyleSheet.create({ + backgroundColorText: { + margin: 5, + marginBottom: 0, + backgroundColor: 'rgba(100, 100, 100, 0.3)' + }, +}); + +examples.forEach((example) => { + storiesOf('', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/TextInput/TextInputExample.js b/examples/TextInput/TextInputExample.js new file mode 100644 index 00000000..8da68309 --- /dev/null +++ b/examples/TextInput/TextInputExample.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { StyleSheet, TextInput, View } from 'react-native' + +storiesOf('', module) + .add('tbd', () => ( + + { console.log('TextInput.onBlur', e) }} + onChange={(e) => { console.log('TextInput.onChange', e) }} + onChangeText={(e) => { console.log('TextInput.onChangeText', e) }} + onFocus={(e) => { console.log('TextInput.onFocus', e) }} + onSelectionChange={(e) => { console.log('TextInput.onSelectionChange', e) }} + /> + + + + + + + + + )) + +const styles = StyleSheet.create({ + textInput: { + borderWidth: 1 + } +}) diff --git a/examples/TicTacToe/TicTacToeExample.js b/examples/TicTacToe/TicTacToeExample.js new file mode 100644 index 00000000..7273ba96 --- /dev/null +++ b/examples/TicTacToe/TicTacToeExample.js @@ -0,0 +1,8 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import TicTacToe from './TicTacToe' + +storiesOf('TicTacToe', module) + .add('the game', () => ( + + )) diff --git a/examples/Touchable/TouchableExample.js b/examples/Touchable/TouchableExample.js new file mode 100644 index 00000000..c6122b04 --- /dev/null +++ b/examples/Touchable/TouchableExample.js @@ -0,0 +1,450 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { + Image, + StyleSheet, + Text, + TouchableHighlight, + TouchableOpacity, + Platform, + TouchableNativeFeedback, + View, + } from 'react-native' + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +const examples = [ +{ + title: '', + description: 'TouchableHighlight works by adding an extra view with a ' + + 'black background under the single child view. This works best when the ' + + 'child view is fully opaque, although it can be made to work as a simple ' + + 'background color change as well with the activeOpacity and ' + + 'underlayColor props.', + render: function() { + return ( + + + console.log('stock THW image - highlight')}> + + + console.log('custom THW text - highlight')}> + + + Tap Here For Custom Highlight! + + + + + + ); + }, +}, { + title: ' with highlight', + render: function(): ReactElement { + return ; + }, +}, { + title: 'Touchable feedback events', + description: ' components accept onPress, onPressIn, ' + + 'onPressOut, and onLongPress as props.', + render: function(): ReactElement { + return ; + }, +}, { + title: 'Touchable delay for events', + description: ' components also accept delayPressIn, ' + + 'delayPressOut, and delayLongPress as props. These props impact the ' + + 'timing of feedback events.', + render: function(): ReactElement { + return ; + }, +}, { + title: '3D Touch / Force Touch', + description: 'iPhone 6s and 6s plus support 3D touch, which adds a force property to touches', + render: function(): ReactElement { + return ; + }, + platform: 'ios', +}, { + title: 'Touchable Hit Slop', + description: ' components accept hitSlop prop which extends the touch area ' + + 'without changing the view bounds.', + render: function(): ReactElement { + return ; + }, + }, { + title: 'Disabled Touchable*', + description: ' components accept disabled prop which prevents ' + + 'any interaction with component', + render: function(): ReactElement { + return ; + }, + }]; + +var TextOnPressBox = React.createClass({ + getInitialState: function() { + return { + timesPressed: 0, + }; + }, + textOnPress: function() { + this.setState({ + timesPressed: this.state.timesPressed + 1, + }); + }, + render: function() { + var textLog = ''; + if (this.state.timesPressed > 1) { + textLog = this.state.timesPressed + 'x text onPress'; + } else if (this.state.timesPressed > 0) { + textLog = 'text onPress'; + } + + return ( + + + Text has built-in onPress handling + + + + {textLog} + + + + ); + } +}); + +var TouchableFeedbackEvents = React.createClass({ + getInitialState: function() { + return { + eventLog: [], + }; + }, + render: function() { + return ( + + + this._appendEvent('press')} + onPressIn={() => this._appendEvent('pressIn')} + onPressOut={() => this._appendEvent('pressOut')} + onLongPress={() => this._appendEvent('longPress')}> + + Press Me + + + + + {this.state.eventLog.map((e, ii) => {e})} + + + ); + }, + _appendEvent: function(eventName) { + var limit = 6; + var eventLog = this.state.eventLog.slice(0, limit - 1); + eventLog.unshift(eventName); + this.setState({eventLog}); + }, +}); + +var TouchableDelayEvents = React.createClass({ + getInitialState: function() { + return { + eventLog: [], + }; + }, + render: function() { + return ( + + + this._appendEvent('press')} + delayPressIn={400} + onPressIn={() => this._appendEvent('pressIn - 400ms delay')} + delayPressOut={1000} + onPressOut={() => this._appendEvent('pressOut - 1000ms delay')} + delayLongPress={800} + onLongPress={() => this._appendEvent('longPress - 800ms delay')}> + + Press Me + + + + + {this.state.eventLog.map((e, ii) => {e})} + + + ); + }, + _appendEvent: function(eventName) { + var limit = 6; + var eventLog = this.state.eventLog.slice(0, limit - 1); + eventLog.unshift(eventName); + this.setState({eventLog}); + }, +}); + +var ForceTouchExample = React.createClass({ + getInitialState: function() { + return { + force: 0, + }; + }, + _renderConsoleText: function() { + return View.forceTouchAvailable ? + 'Force: ' + this.state.force.toFixed(3) : + '3D Touch is not available on this device'; + }, + render: function() { + return ( + + + {this._renderConsoleText()} + + + true} + onResponderMove={(event) => this.setState({force: event.nativeEvent.force})} + onResponderRelease={(event) => this.setState({force: 0})}> + + Press Me + + + + + ); + }, +}); + +var TouchableHitSlop = React.createClass({ + getInitialState: function() { + return { + timesPressed: 0, + }; + }, + onPress: function() { + this.setState({ + timesPressed: this.state.timesPressed + 1, + }); + }, + render: function() { + var log = ''; + if (this.state.timesPressed > 1) { + log = this.state.timesPressed + 'x onPress'; + } else if (this.state.timesPressed > 0) { + log = 'onPress'; + } + + return ( + + + + + Press Outside This View + + + + + + {log} + + + + ); + } +}); + +var TouchableDisabled = React.createClass({ + render: function() { + return ( + + + Disabled TouchableOpacity + + + + Enabled TouchableOpacity + + + console.log('custom THW text - highlight')}> + + Disabled TouchableHighlight + + + + console.log('custom THW text - highlight')}> + + Enabled TouchableHighlight + + + + {Platform.OS === 'android' && + console.log('custom TNF has been clicked')} + background={TouchableNativeFeedback.SelectableBackground()}> + + + Enabled TouchableNativeFeedback + + + + } + + {Platform.OS === 'android' && + console.log('custom TNF has been clicked')} + background={TouchableNativeFeedback.SelectableBackground()}> + + + Disabled TouchableNativeFeedback + + + + } + + ); + } +}); + +var heartImage = {uri: 'https://pbs.twimg.com/media/BlXBfT3CQAA6cVZ.png:small'}; + +var styles = StyleSheet.create({ + row: { + justifyContent: 'center', + flexDirection: 'row', + }, + icon: { + width: 24, + height: 24, + }, + image: { + width: 50, + height: 50, + }, + text: { + fontSize: 16, + }, + block: { + padding: 10, + }, + button: { + color: '#007AFF', + }, + disabledButton: { + color: '#007AFF', + opacity: 0.5, + }, + nativeFeedbackButton: { + textAlign: 'center', + margin: 10, + }, + hitSlopButton: { + color: 'white', + }, + wrapper: { + borderRadius: 8, + }, + wrapperCustom: { + borderRadius: 8, + padding: 6, + }, + hitSlopWrapper: { + backgroundColor: 'red', + marginVertical: 30, + }, + logBox: { + padding: 20, + margin: 10, + borderWidth: StyleSheet.hairlineWidth, + borderColor: '#f0f0f0', + backgroundColor: '#f9f9f9', + }, + eventLogBox: { + padding: 10, + margin: 10, + height: 120, + borderWidth: StyleSheet.hairlineWidth, + borderColor: '#f0f0f0', + backgroundColor: '#f9f9f9', + }, + forceTouchBox: { + padding: 10, + margin: 10, + borderWidth: StyleSheet.hairlineWidth, + borderColor: '#f0f0f0', + backgroundColor: '#f9f9f9', + alignItems: 'center', + }, + textBlock: { + fontWeight: '500', + color: 'blue', + }, +}); + +examples.forEach((example) => { + storiesOf('', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/View/ViewExample.js b/examples/View/ViewExample.js new file mode 100644 index 00000000..580dff50 --- /dev/null +++ b/examples/View/ViewExample.js @@ -0,0 +1,250 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native' + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * @flow + */ + +var styles = StyleSheet.create({ + box: { + backgroundColor: '#527FE4', + borderColor: '#000033', + borderWidth: 1, + }, + zIndex: { + justifyContent: 'space-around', + width: 100, + height: 50, + marginTop: -10, + }, +}); + +var ViewBorderStyleExample = React.createClass({ + getInitialState() { + return { + showBorder: true + }; + }, + + render() { + return ( + + + + + Dashed border style + + + + + Dotted border style + + + + + ); + }, + + _handlePress() { + this.setState({showBorder: !this.state.showBorder}); + } +}); + +var ZIndexExample = React.createClass({ + getInitialState() { + return { + flipped: false + }; + }, + + render() { + const indices = this.state.flipped ? [-1, 0, 1, 2] : [2, 1, 0, -1]; + return ( + + + Tap to flip sorting order + + ZIndex {indices[0]} + + + ZIndex {indices[1]} + + + ZIndex {indices[2]} + + + ZIndex {indices[3]} + + + + ); + }, + + _handlePress() { + this.setState({flipped: !this.state.flipped}); + } +}); + +const examples = [ + { + title: 'Background Color', + render: function() { + return ( + + + Blue background + + + ); + }, + }, { + title: 'Border', + render: function() { + return ( + + 5px blue border + + ); + }, + }, { + title: 'Padding/Margin', + render: function() { + return ( + + + 5px padding + + + 5px margin + + + + 5px margin and padding, + + + widthAutonomous=true + + + + ); + }, + }, { + title: 'Border Radius', + render: function() { + return ( + + + Too much use of `borderRadius` (especially large radii) on + anything which is scrolling may result in dropped frames. + Use sparingly. + + + ); + }, + }, { + title: 'Border Style', + render: function() { + return ; + }, + }, { + title: 'Circle with Border Radius', + render: function() { + return ( + + ); + }, + }, { + title: 'Overflow', + render: function() { + return ( + + + + Overflow hidden + + + + + Overflow visible + + + + ); + }, + }, { + title: 'Opacity', + render: function() { + return ( + + Opacity 0 + Opacity 0.1 + Opacity 0.3 + Opacity 0.5 + Opacity 0.7 + Opacity 0.9 + Opacity 1 + + ); + }, + }, { + title: 'ZIndex', + render: function() { + return ; + }, + }, +]; + +examples.forEach((example) => { + storiesOf('', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/View/ViewTransformsExample.js b/examples/View/ViewTransformsExample.js new file mode 100644 index 00000000..c50d5c1d --- /dev/null +++ b/examples/View/ViewTransformsExample.js @@ -0,0 +1,286 @@ +import React from 'react'; +import { storiesOf, action } from '@kadira/storybook'; +import { Animated, StyleSheet, Text, View } from 'react-native' + +/** + * Copyright (c) 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * @flow + */ + +var Flip = React.createClass({ + getInitialState() { + return { + theta: new Animated.Value(45), + }; + }, + + componentDidMount() { + this._animate(); + }, + + _animate() { + this.state.theta.setValue(0); + Animated.timing(this.state.theta, { + toValue: 360, + duration: 5000, + }).start(this._animate); + }, + + render() { + return ( + + + + This text is flipping great. + + + + + On the flip side... + + + + ); + } +}); + +var styles = StyleSheet.create({ + box1: { + left: 0, + backgroundColor: 'green', + height: 50, + top: 0, + transform: [ + {translateX: 100}, + {translateY: 50}, + {rotate: '30deg'}, + {scaleX: 2}, + {scaleY: 2}, + ], + width: 50, + }, + box2: { + left: 0, + backgroundColor: 'purple', + height: 50, + top: 0, + transform: [ + {scaleX: 2}, + {scaleY: 2}, + {translateX: 100}, + {translateY: 50}, + {rotate: '30deg'}, + ], + width: 50, + }, + box3step1: { + left: 0, + backgroundColor: 'lightpink', + height: 50, + top: 0, + transform: [ + {rotate: '30deg'}, + ], + width: 50, + }, + box3step2: { + left: 0, + backgroundColor: 'hotpink', + height: 50, + opacity: 0.5, + top: 0, + transform: [ + {rotate: '30deg'}, + {scaleX: 2}, + {scaleY: 2}, + ], + width: 50, + }, + box3step3: { + left: 0, + backgroundColor: 'deeppink', + height: 50, + opacity: 0.5, + top: 0, + transform: [ + {rotate: '30deg'}, + {scaleX: 2}, + {scaleY: 2}, + {translateX: 10}, + {translateY: 50}, + ], + width: 50, + }, + box4: { + left: 0, + backgroundColor: 'darkorange', + height: 50, + top: 0, + transform: [ + {translateX: 20}, + {translateY: 35}, + {scale: 2.5}, + {rotate: '-0.2rad'}, + ], + width: 100, + }, + box5: { + backgroundColor: 'maroon', + height: 50, + right: 0, + top: 0, + width: 50, + }, + box5Transform: { + transform: [ + {translateX: -50}, + {translateY: 35}, + {rotate: '50deg'}, + {scale: 2}, + ], + }, + flipCardContainer: { + marginVertical: 40, + flex: 1, + alignSelf: 'center', + }, + flipCard: { + width: 200, + height: 200, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'blue', + backfaceVisibility: 'hidden', + }, + flipText: { + width: 90, + fontSize: 20, + color: 'white', + fontWeight: 'bold', + } +}); + +const examples = [ + { + title: 'Perspective', + description: 'perspective: 850, rotateX: Animated.timing(0 -> 360)', + render(): ReactElement { return ; } + }, + { + title: 'Translate, Rotate, Scale', + description: "translateX: 100, translateY: 50, rotate: '30deg', scaleX: 2, scaleY: 2", + render() { + return ( + + + + ); + } + }, + { + title: 'Scale, Translate, Rotate, ', + description: "scaleX: 2, scaleY: 2, translateX: 100, translateY: 50, rotate: '30deg'", + render() { + return ( + + + + ); + } + }, + { + title: 'Rotate', + description: "rotate: '30deg'", + render() { + return ( + + + + ); + } + }, + { + title: 'Rotate, Scale', + description: "rotate: '30deg', scaleX: 2, scaleY: 2", + render() { + return ( + + + + ); + } + }, + { + title: 'Rotate, Scale, Translate ', + description: "rotate: '30deg', scaleX: 2, scaleY: 2, translateX: 100, translateY: 50", + render() { + return ( + + + + ); + } + }, + { + title: 'Translate, Scale, Rotate', + description: "translate: [200, 350], scale: 2.5, rotate: '-0.2rad'", + render() { + return ( + + + + ); + } + }, + { + title: 'Translate, Rotate, Scale', + description: "translate: [-50, 35], rotate: '50deg', scale: 2", + render() { + return ( + + + + ); + } + } +]; + +examples.forEach((example) => { + storiesOf(' transforms', module) + .add(example.title, () => example.render()) +}) diff --git a/examples/components/App.js b/examples/components/App.js deleted file mode 100644 index b5f5c153..00000000 --- a/examples/components/App.js +++ /dev/null @@ -1,271 +0,0 @@ -import GridView from './GridView' -import Heading from './Heading' -import React from 'react' -import { Image, StyleSheet, ScrollView, Text, TextInput, TouchableHighlight, View } from 'react-native' - -export default class App extends React.Component { - static propTypes = { - style: View.propTypes.style - } - - constructor(props) { - super(props) - this.state = { - scrollEnabled: true - } - } - - render() { - const finalRootStyles = [ - rootStyles.common - ] - - return ( - - - React Native for Web - React Native Web takes the core components from React - Native and brings them to the web. These components provide - simple building blocks – touch handling, flexbox layout, - scroll views – from which more complex components and apps can be - constructed. - - Image - { console.log(e.nativeEvent.layout) }} - accessibilityLabel='accessible image' - children={Inner content} - defaultSource={{ - uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAIAAAAP3aGbAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wkGESkdPWMDggAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAD5UlEQVR42u3UMQ0AAAgEMcC/x7eCCgaSVsIN10kK4IORADAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAswLAkAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAADAvAsADDAjAsAMMCDAvAsAAMCzAsAMMCMCzAsAAMC8CwAMMCMCzAsAAMC8CwAMMCMCwAwwIMC8CwAAwLMCwAwwIwLMCwAAwLwLAAwwIwLADDAgwLwLAADAswLADDAjAswLAALi04UQW9HF910gAAAABJRU5ErkJggg==' - }} - onError={(e) => { console.log('Image.onError', e) }} - onLoad={(e) => { console.log('Image.onLoad', e) }} - onLoadEnd={() => { console.log('Image.onLoadEnd') }} - onLoadStart={() => { console.log('Image.onLoadStart') }} - resizeMode={'contain'} - source={{ - height: 400, - uri: 'http://facebook.github.io/react/img/logo_og.png', - width: 400 - }} - style={{ - borderWidth: '5px' - }} - testID='Example.image' - /> - - Text - { console.log('Text.onPress', e) }} - onLayout={(e) => { console.log(e.nativeEvent.layout) }} - testID={'Example.text'} - > - PRESS ME. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent vel - lectus urna. Aliquam vitae justo porttitor, aliquam erat nec, - venenatis diam. Vivamus facilisis augue non urna mattis ultricies. - Suspendisse et vulputate enim, a maximus nulla. Vivamus imperdiet - hendrerit consequat. Aliquam lorem quam, elementum eget ex nec, - ultrices porttitor nibh. Nulla pellentesque urna leo, a aliquet elit - rhoncus a. Aenean ultricies, nunc a interdum dictum, dui odio - scelerisque mauris, a fringilla elit ligula vel sem. Sed vel aliquet - ipsum, sed rhoncus velit. Vivamus commodo pretium libero id placerat. - - - TRUNCATED after 1 line. - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent vel - lectus urna. Aliquam vitae justo porttitor, aliquam erat nec, - venenatis diam. Vivamus facilisis augue non urna mattis ultricies. - Suspendisse et vulputate enim, a maximus nulla. Vivamus imperdiet - hendrerit consequat. - - - TextInput - { console.log('TextInput.onBlur', e) }} - onChange={(e) => { console.log('TextInput.onChange', e) }} - onChangeText={(e) => { console.log('TextInput.onChangeText', e) }} - onFocus={(e) => { console.log('TextInput.onFocus', e) }} - onSelectionChange={(e) => { console.log('TextInput.onSelectionChange', e) }} - /> - - - - - - - - - Touchable - { console.log('Touchable.onLongPress', e) }} - onPress={(e) => { console.log('Touchable.onPress', e) }} - onPressIn={(e) => { console.log('Touchable.onPressIn', e) }} - onPressOut={(e) => { console.log('Touchable.onPressOut', e) }} - > - - Touchable area (press, long press) - - - - View - Default layout - - {[ 1, 2, 3, 4, 5, 6 ].map((item, i) => { - return ( - - {item} - - ) - })} - - - Row layout - - {[ 1, 2, 3, 4, 5, 6 ].map((item, i) => { - return ( - - {item} - - ) - })} - - - pointerEvents - - {['box-none', 'box-only', 'none'].map((value, i) => { - return ( - - ) - })} - - - ScrollView - - - Default layout - - console.log('ScrollView.onScroll', e)} - scrollEnabled={this.state.scrollEnabled} - scrollEventThrottle={1} // 1 event per second - style={styles.scrollViewStyle} - > - {Array.from({ length: 50 }).map((item, i) => ( - - {i} - - ))} - - - - Horizontal layout - - console.log('ScrollView.onScroll', e)} - scrollEnabled={this.state.scrollEnabled} - scrollEventThrottle={1} // 1 event per second - style={styles.scrollViewStyle} - > - {Array.from({ length: 50 }).map((item, i) => ( - - {i} - - ))} - - - - - ) - } -} - -const rootStyles = StyleSheet.create({ - common: { - marginVertical: 0, - marginHorizontal: 'auto' - }, - mqSmall: { - maxWidth: '400px' - }, - mqLarge: { - maxWidth: '600px' - } -}) - -const styles = StyleSheet.create({ - row: { - flexDirection: 'row', - flexWrap: 'wrap' - }, - textInput: { - borderWidth: 1 - }, - box: { - alignItems: 'center', - flexGrow: 1, - justifyContent: 'center', - borderWidth: 1 - }, - horizontalBox: { - width: '50px' - }, - boxFull: { - width: '100%' - }, - pointerEventsBox: { - alignItems: 'center', - borderWidth: '1px', - flexGrow: 1, - height: '100px', - justifyContent: 'center' - }, - touchableArea: { - alignItems: 'center', - borderWidth: 1, - height: '200px', - justifyContent: 'center' - }, - scrollViewContainer: { - height: '200px' - }, - scrollViewStyle: { - borderWidth: '1px' - }, - scrollViewContentContainerStyle: { - padding: '10px' - } -}) diff --git a/examples/components/GridView.js b/examples/components/GridView.js deleted file mode 100644 index 28b77bd3..00000000 --- a/examples/components/GridView.js +++ /dev/null @@ -1,66 +0,0 @@ -import React, { Component, PropTypes } from 'react' -import { StyleSheet, View } from 'react-native' - -export default class GridView extends Component { - static propTypes = { - alley: PropTypes.string, - children: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.arrayOf(PropTypes.element) - ]), - gutter: PropTypes.string, - style: PropTypes.object - } - - static defaultProps = { - alley: '0px', - gutter: '0px' - } - - render() { - const { alley, children, gutter, style, ...other } = this.props - - const rootStyle = [ - style, - styles.root - ] - - const contentContainerStyle = [ - styles.contentContainer, - { marginHorizontal: `calc(-0.5 * ${alley})` }, - { paddingHorizontal: `${gutter}` } - ] - - const newChildren = React.Children.map(children, (child) => { - return child && React.cloneElement(child, { - style: [ - child.props.style, - styles.column, - { marginHorizontal: `calc(0.5 * ${alley})` } - ] - }) - }) - - return ( - - - {newChildren} - - - ) - } -} - -const styles = StyleSheet.create({ - root: { - overflow: 'hidden' - }, - contentContainer: { - flexDirection: 'row', - flexGrow: 1 - }, - // distribute all space (rather than extra space) - column: { - flexBasis: '0%' - } -}) diff --git a/examples/components/Heading.js b/examples/components/Heading.js deleted file mode 100644 index fd53b4c4..00000000 --- a/examples/components/Heading.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react' -import { StyleSheet, Text } from 'react-native' - -const Heading = ({ children, size = 'normal' }) => ( - -) - -const sizeStyles = StyleSheet.create({ - xlarge: { - fontSize: '2rem', - marginBottom: '1em' - }, - large: { - fontSize: '1.5rem', - marginBottom: '1em', - marginTop: '1em' - }, - normal: { - fontSize: '1.25rem', - marginBottom: '0.5em', - marginTop: '0.5em' - } -}) - -const styles = StyleSheet.create({ - root: { - fontFamily: '"Helvetica Neue", arial, sans-serif' - } -}) - -export default Heading diff --git a/examples/index.html b/examples/index.html deleted file mode 100644 index 6ee40eea..00000000 --- a/examples/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - -React Native for Web - -
- diff --git a/examples/index.js b/examples/index.js deleted file mode 100644 index 3bb55a99..00000000 --- a/examples/index.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import ReactNative, { AppRegistry } from 'react-native' -import App from './components/App' -import Game2048 from './2048/Game2048' -import TicTacToeApp from './TicTacToe/TicTacToe' - -const rootTag = document.getElementById('react-root') -AppRegistry.registerComponent('App', () => App) -AppRegistry.runApplication('App', { rootTag }) -// ReactNative.render(, rootTag) diff --git a/package.json b/package.json index 6aed2d9c..f0a6889e 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,11 @@ ], "scripts": { "build": "del ./dist && mkdir dist && babel src -d dist --ignore **/__tests__", + "build:storybook": "build-storybook -o dist-storybook -c ./examples/.storybook", "build:umd": "webpack --config webpack.config.js --sort-assets-by --progress", - "examples": "webpack-dev-server --config examples/webpack.config.js --inline --hot --colors --quiet", "lint": "eslint src", "prepublish": "npm run build && npm run build:umd", + "storybook": "start-storybook -p 9001 -c ./examples/.storybook", "test": "karma start karma.config.js", "test:watch": "npm run test -- --no-single-run" }, @@ -26,6 +27,7 @@ "react-timer-mixin": "^0.13.3" }, "devDependencies": { + "@kadira/storybook": "^1.38.0", "babel-cli": "^6.10.1", "babel-core": "^6.10.4", "babel-eslint": "^6.1.0", @@ -39,6 +41,7 @@ "eslint-plugin-promise": "^1.3.2", "eslint-plugin-react": "^5.1.1", "eslint-plugin-standard": "^1.3.2", + "file-loader": "^0.9.0", "karma": "^0.13.22", "karma-browserstack-launcher": "^1.0.1", "karma-chrome-launcher": "^1.0.1", @@ -51,8 +54,8 @@ "node-libs-browser": "^0.5.3", "react": "^15.2.0", "react-addons-test-utils": "^15.2.0", - "webpack": "^1.13.1", - "webpack-dev-server": "^1.14.1" + "url-loader": "^0.5.7", + "webpack": "^1.13.1" }, "peerDependencies": { "react": "^15.1.0"