From 94f37740af3362028870c7f15f72cf450ab45cac Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Wed, 13 Jul 2016 22:19:39 -0700 Subject: [PATCH] use react-storybook to display examples Initial setup of react-storybook. Includes examples copied from the React Native repository. Close #148 --- .gitignore | 1 + examples/.storybook/config.js | 12 + examples/.storybook/decorator-centered.js | 14 + examples/{ => .storybook}/webpack.config.js | 21 +- .../ActivityIndicatorExample.js | 175 +++++ examples/{2048 => Game2048}/Game2048.js | 0 examples/Game2048/Game2048Example.js | 8 + examples/{2048 => Game2048}/GameBoard.js | 0 examples/Image/ImageExample.js | 658 ++++++++++++++++++ examples/Image/bunny.png | Bin 0 -> 18738 bytes examples/Image/uie_thumb_normal@2x.png | Bin 0 -> 850 bytes examples/Image/uie_thumb_selected@2x.png | Bin 0 -> 1110 bytes examples/ListView/ListViewExample.js | 4 + examples/ScrollView/ScrollViewExample.js | 57 ++ examples/Text/TextExample.js | 471 +++++++++++++ examples/TextInput/TextInputExample.js | 40 ++ examples/TicTacToe/TicTacToeExample.js | 8 + examples/Touchable/TouchableExample.js | 450 ++++++++++++ examples/View/ViewExample.js | 250 +++++++ examples/View/ViewTransformsExample.js | 286 ++++++++ examples/components/App.js | 271 -------- examples/components/GridView.js | 66 -- examples/components/Heading.js | 35 - examples/index.html | 6 - examples/index.js | 10 - package.json | 9 +- 26 files changed, 2447 insertions(+), 405 deletions(-) create mode 100644 examples/.storybook/config.js create mode 100644 examples/.storybook/decorator-centered.js rename examples/{ => .storybook}/webpack.config.js (64%) create mode 100644 examples/ActivityIndicator/ActivityIndicatorExample.js rename examples/{2048 => Game2048}/Game2048.js (100%) create mode 100644 examples/Game2048/Game2048Example.js rename examples/{2048 => Game2048}/GameBoard.js (100%) create mode 100644 examples/Image/ImageExample.js create mode 100644 examples/Image/bunny.png create mode 100644 examples/Image/uie_thumb_normal@2x.png create mode 100644 examples/Image/uie_thumb_selected@2x.png create mode 100644 examples/ListView/ListViewExample.js create mode 100644 examples/ScrollView/ScrollViewExample.js create mode 100644 examples/Text/TextExample.js create mode 100644 examples/TextInput/TextInputExample.js create mode 100644 examples/TicTacToe/TicTacToeExample.js create mode 100644 examples/Touchable/TouchableExample.js create mode 100644 examples/View/ViewExample.js create mode 100644 examples/View/ViewTransformsExample.js delete mode 100644 examples/components/App.js delete mode 100644 examples/components/GridView.js delete mode 100644 examples/components/Heading.js delete mode 100644 examples/index.html delete mode 100644 examples/index.js 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 0000000000000000000000000000000000000000..0d94af660bcfc793372a84e0dad82e5641bfe375 GIT binary patch literal 18738 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 IM6N<$f=0}iFaQ7m literal 0 HcmV?d00001 diff --git a/examples/Image/uie_thumb_normal@2x.png b/examples/Image/uie_thumb_normal@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..72683dfac123c8724b2fa6b5705524ad64b58247 GIT binary patch literal 850 zcmV-Y1FigtP){7FPXRA}Dqm^*J1K@i9DFtHPqf+7+UU0N#S%Cew$ zlW*c89TGx1CB$I+e7@UztbK`-;1u)}G!*m*J^*|JB#=y$@Q@-cGv{4Y6&FAy05^!;5Ah}#_ z*>ZeQvr%58is{&LS7rx-nb(~_@bDlK^Fsk(-w|-uAgMazqPh1nQe*c};Ag{lzCLvj ztz%NrjzHkB)dn3!?~d0LTQ98pQ!UmwM6p<0Qy{zheGtPi?kZ22I7kcvi!oiPIo0mb z^moMe8pnk~A+2CFrG^>aI1ribQh5Uk^UMu)B}hA}lR69DDNyMvN@>kw&ehK#mmHKu zN}G=$o*2&_LCc<6&@ot`-5O~N-aHW1nrq0w*g3Z~t;o4_-0%`-enA#WnWtN&hi2XT zYJqq7jaIY^3dG}k0^0c$zVCp+bpWbUsWA|YE%pucW^?oLdff2hR-KX5@hlWjdS}Nh zfN7l|1P1Lx9BD_x1@2VOc1#}z zkE;&bHX@vx(bcBG$$vg_^`=T*I6wtizj0ijC)CA8Otv|TO|#d^*mW#Pqz z!ByMYZll*BgCPEHz{N`tPPDx!7BzY4ZgpCM-3E^6c^Ls-djCy7@BnX=T zNOYede1EAYG&Tj0aR2B>v(^3>gooQf5Zsv<2>lyCAOu1n1VSJLLLdY}AOu1n1VSJL c@{fc30h_?_Xm?+0KL7v#07*qoM6N<$g3xSt9RL6T literal 0 HcmV?d00001 diff --git a/examples/Image/uie_thumb_selected@2x.png b/examples/Image/uie_thumb_selected@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..79eb69cf92b3638954dd11b045006803abe6d904 GIT binary patch literal 1110 zcmV-c1gZOpP)5AvWIw6 zQBjmADu|kc9`b`XF-IW;B%ml>LOcnIkb^hLO-R6>gLn=i8YGK{TnssgQ9OlU#O%5| zU7v^Up6QvL*`0BdWe-(Q%rI1SJ>UENRWo#VY=#QqcxNEjK%WSbv`wX0sr;`>|KDM_>N% zYmiUF{koUsnakgO{N69$f3{qM`j!N%@{Y^G3!*xb?zAjBy7$@RCr8B1tp)l0hqGNt zu#mw*4q1R@#gTiTI&52=zHVj+S%IvWLC6Z2*=-L$dF-xRp1wmF!$uKVK$K7-LUTK3 zCXJ8(1=4~L06uf%%x)5p0EvJLE}>k$c6BJFZyv}HgaB~Q{SUn@0U{w15aHtAmo9#B ztyxRbhHA{3f)_Rcp#$$Pz8H$)ElH3FsXC%$ym0y3Qx3p}vd3F>#!1EoCXDj=h|TQ+ zMgV&CopVogW_N#1$t(nz2_Qf^Sa#2~s;x$V5J1@+?e0FROlyo4y-Yjw?)k@yUGra3 zHZRK&vJPY!9FEvsLAkPgqbyfeD`Q*R9VO`J|N8CX(QiIIc%@b*y1R!VOKl8DY@YVo ziN)uO9k-taIV%NNR=`3AP=LoOMvOba0CfP^4`0$ZMGSrw}_0cJT=L$K1j zU1w_#k0`-##8Ti&ywK{<1By5Rngv*vg9^R2>9IuYt~MYrGgua278<8d(F_1MFWo4w z@2?f|QN6~^gb*@hS&onw)o18Q?e6X-!o;d>80z{l$*fZw@qtd5y>H#hI=HF!h!ZZ3 zSu=$kPDDB)Vj1Xh__Cz6AgC;6WqD(Ldrj|mPBBG@_y<@yaO%x_eqQ$)&@`@5vTnR) z{Yg52I4gxi8#XopT63?cC} z;?-jlDAJd`Nlo|R7!hQGq^a^YR!Z;t@yee|8(tNwcCG>Wt61!NCHV92_%6e ckgXa20ii_@% literal 0 HcmV?d00001 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"