Rewrite interactive documentation

Consolidate all docs within the latest storybook

Ref #1172
This commit is contained in:
Nicolas Gallagher
2019-10-15 09:32:13 -07:00
parent 8cf00a5c5a
commit 297cda7901
463 changed files with 10993 additions and 36176 deletions

View File

@@ -3,7 +3,7 @@
"react": {
"pragma": "React",
"version": "16.6",
"flowVersion": "0.78" // Flow version
"flowVersion": "0.109.0" // Flow version
}
},
// babel parser to support ES6/7 features
@@ -139,7 +139,7 @@
// react
"react/display-name": 0,
"react/jsx-no-bind": 2,
"react/jsx-no-bind": 0,
"react/jsx-no-duplicate-props": 2,
"react/jsx-no-undef": 2,
"react/jsx-pascal-case": 2,
@@ -153,7 +153,7 @@
"react/no-string-refs": 2,
"react/no-unknown-property": 2,
"react/prefer-es6-class": 2,
"react/prop-types": 2,
"react/prop-types": 0,
"react/react-in-jsx-scope": 0,
"react/self-closing-comp": 2,
"react/sort-comp": 0,

View File

@@ -70,15 +70,16 @@ yarn compile
yarn compile --watch
```
## Website and visual tests
## Documentation and visual tests
To run the interactive storybook:
```
yarn website
yarn docs
```
When you're also making changes to the 'react-native-web' source files, run this command in another process:
When you're also making changes to the 'react-native-web' source files, run this
command in another process:
```
yarn compile --watch

View File

@@ -1,7 +1,7 @@
{
"private": true,
"version": "0.11.7",
"name": "react-native-web-monorepo",
"name": "monorepo",
"scripts": {
"clean": "del-cli ./packages/*/dist",
"compile": "npm-run-all clean -p \"compile:* -- {@}\" --",
@@ -9,10 +9,8 @@
"compile:es": "cd packages/react-native-web && babel --root-mode upward src --out-dir dist --ignore \"**/__tests__\"",
"benchmarks": "cd packages/benchmarks && yarn build",
"benchmarks:release": "cd packages/benchmarks && yarn release",
"examples": "cd packages/examples && yarn build",
"examples:release": "cd packages/examples && yarn release",
"website": "cd packages/website && yarn start",
"website:release": "cd packages/website && yarn release",
"docs": "cd packages/docs && yarn start",
"docs:release": "cd packages/docs && yarn release",
"flow": "flow",
"fmt": "prettier --write \"**/*.js\"",
"jest": "jest --config ./scripts/jest/config.js",
@@ -21,7 +19,7 @@
"precommit": "lint-staged",
"prerelease": "yarn test && yarn compile && yarn compile:commonjs",
"release": "node ./scripts/release/publish.js",
"postrelease": "yarn benchmarks:release && yarn examples:release && yarn website:release",
"postrelease": "yarn benchmarks:release && yarn docs:release",
"test": "yarn flow && yarn lint:check && yarn jest --runInBand"
},
"devDependencies": {

View File

@@ -0,0 +1,20 @@
const path = require('path');
const webpack = require('webpack');
module.exports = async ({ config, mode }) => {
config.module.rules.push({
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: { name: '[name].[ext]' }
}
});
config.resolve.extensions = ['.web.js', '.js', '.json', '.web.jsx', '.jsx'];
config.resolve.alias = {
'react-native': 'react-native-web'
};
return config;
};

View File

@@ -0,0 +1 @@
// import '@storybook/addon-options/register';

View File

@@ -0,0 +1,48 @@
import { create } from '@storybook/theming';
// import centered from './decorator-centered';
import { addParameters, configure, addDecorator } from '@storybook/react';
// Option defaults:
addParameters({
options: {
storySort: (a, b) => {
const sectionA = a[1].id.split('-')[0];
const sectionB = b[1].id.split('-')[0];
return sectionB.localeCompare(sectionA);
},
theme: create({
base: 'light',
brandTitle: 'React Native for Web',
brandUrl: 'https://necolas.github.io/react-native-web'
// To control appearance:
// brandImage: 'http://url.of/some.svg',
}),
/**
* regex for finding the hierarchy separator
* @example:
* null - turn off hierarchy
* /\// - split by `/`
* /\./ - split by `.`
* /\/|\./ - split by `/` or `.`
* @type {Regex}
*/
hierarchySeparator: /\/|\./,
/**
* regex for finding the hierarchy root separator
* @example:
* null - turn off multiple hierarchy roots
* /\|/ - split by `|`
* @type {Regex}
*/
hierarchyRootSeparator: /\|/,
panelPosition: 'bottom'
}
});
// addDecorator(centered);
const context = require.context('../src', true, /\.stories\.(js|mdx)$/);
configure(context, module);

View File

@@ -3,9 +3,7 @@ import { StyleSheet, View } from 'react-native';
const styles = StyleSheet.create({
root: {
minHeight: '100vh',
maxWidth: 680,
marginHorizontal: 'auto'
maxWidth: '100%'
}
});

View File

@@ -0,0 +1,10 @@
module.exports = [
{
name: '@storybook/addon-docs/preset',
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null
}
}
];

View File

@@ -0,0 +1,23 @@
{
"private": true,
"name": "docs",
"version": "0.11.7",
"scripts": {
"build": "build-storybook --docs -o ./dist -c ./.storybook",
"start": "start-storybook --docs -p 9001 -c ./.storybook",
"release": "yarn build && git checkout gh-pages && rm -rf ../../docs && mv dist ../../docs && git add -A && git commit -m \"Deploy documentation\" && git push origin gh-pages && git checkout -"
},
"dependencies": {
"@storybook/addon-docs": "5.3.0-alpha.43",
"@storybook/addon-options": "5.3.0-alpha.43",
"@storybook/cli": "5.3.0-alpha.43",
"@storybook/react": "5.3.0-alpha.43",
"@storybook/theming": "5.3.0-alpha.43",
"react-native-web": "0.11.7"
},
"devDependencies": {
"babel-plugin-react-native-web": "0.11.7",
"url-loader": "^2.2.0",
"webpack": "^4.41.2"
}
}

View File

@@ -0,0 +1,107 @@
import { Meta, Props } from '@storybook/addon-docs/blocks';
<Meta title="APIs|AppRegistry" />
# AppRegistry
AppRegistry is the control point for registering, running, prerendering, and
unmounting all apps. App root components should register themselves with
`AppRegistry.registerComponent`. Apps can be run by invoking
`AppRegistry.runApplication`.
## Methods
### getAppKeys()
Returns an array of all registered app keys
```js
const appKeys = AppRegistry.getAppKeys();
```
### getApplication(appKey, appParams)
A web-only method for server-side rendering to HTML and CSS. It returns an
object containing the given application's element and a function to get styles
once the element is rendered.
Additional props can be passed to the `getStyleElement` function, e.g., your CSP
policy may require a `nonce` to be set on style elements.
```js
const appKey = 'MyApp';
const appParams = { ... };
const { element, getStyleElement } = AppRegistry.getApplication(appKey, appParams);
```
### registerComponent(appKey, getComponent)
Register a component provider under the given appKey.
```js
const appKey = 'MyApp';
const getComponent = () => App;
AppRegistry.registerComponent(appKey, getComponent)
```
### registerConfig(config)
Register multiple applications. AppConfig type is:
```js
type AppConfig = {
appKey: string;
component: ComponentProvider;
run?: function
}
const config = [{
appKey: 'FirstApp',
component: () => FirstApp
}, {
appKey: 'SecondApp',
component: () => SecondApp
}];
AppRegistry.registerConfig(config)
```
### registerRunnable(appKey, run)
Register a custom render function for an application. The function will receive
the `appParameters` passed to `runApplication`.
```js
AppRegistry.registerRunnable('MyApp', (appParams) => { ... });
```
### runApplication(appKey, appParams)
Runs the application that was registered under `appKey`. The `appParameters` must
include the `rootTag` into which the application is rendered, and optionally any
`initialProps` or render callback.
```js
AppRegistry.runApplication('MyApp', {
initialProps: {},
rootTag: document.getElementById('react-root'),
callback: () => { console.log('React rendering has finished') }
})
```
### setComponentProviderInstrumentationHook(componentProvider)
```js
type setComponentProviderInstrumentationHook = (componentProvider: func) => Component;
```
### setWrapperComponentProvider(appParams)
```js
type setWrapperComponentProvider = (appParameters: object) => Component;
```
### unmountApplicationComponentAtRootTag(rootTag)
To "stop" an application when a view should be destroyed, call
`AppRegistry.unmountApplicationComponentAtRootTag` with the `rootTag` that was passed
into `runApplication`.

View File

@@ -0,0 +1,6 @@
export default {
title: 'APIs|AppState',
includeStories: []
};
export { default as stateChanges } from './examples/StateChanges';

View File

@@ -0,0 +1,44 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as stories from './AppState.stories.js';
<Meta title="APIs|AppState" />
# AppState
AppState can tell you if the app is in the foreground or background, and notify
you when the state changes. States: `active` (the app is running in the
foreground), `background` (the app is running in the background, i.e., the user
has not focused the app's tab).
## Properties
### isAvailable
Determines whether the browser environment supports `AppState`.
### currentState
Returns the current state of the app: "active" or "background".
## Methods
### addEventListener(type, handler)
Add a handler to AppState changes by listening to the `change` event type and
providing the handler. The handler is called with the app state value.
```js
AppState.addEventListener('change', (currentState) => {});
```
### removeEventListener(type, handler)
Remove a handler by passing the `change` event type and the handler.
AppState.removeEventListener('change', handler);
## Example
<Preview withSource='none'>
<Story name="stateChanges">{stories.stateChanges}</Story>
</Preview>

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { AppState, Text, View } from 'react-native';
export default function StateChanges() {
const [state, updateState] = React.useState({
active: 0,
background: 0,
currentState: AppState.currentState
});
const handleChange = nextState => {
updateState(previousState => ({
...previousState,
[nextState]: previousState[nextState] + 1
}));
};
React.useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
};
}, [handleChange]);
return (
<View>
<Text>Active count: {state.active}</Text>
<Text>Background count: {state.background}</Text>
<Text>Current state is: {state.currentState}</Text>
</View>
);
}

View File

@@ -0,0 +1,6 @@
export default {
title: 'APIs|Clipboard',
includeStories: []
};
export { default as setString } from './examples/SetString';

View File

@@ -0,0 +1,39 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as stories from './Clipboard.stories.js';
<Meta title="APIs|Clipboard" />
# Clipboard
Clipboard gives you an interface for setting to the clipboard. (Getting
clipboard content is not currently supported on web.)
## Methods
### isAvailable()
Determines whether the browser environment supports Clipboard at all.
```js
if (Clipboard.isAvailable) {
// you can use the Clipboard API
}
```
### setString(string)
Copies a string to the clipboard. On web, some browsers may not support copying
to the clipboard, therefore, this function returns a boolean to indicate if the
copy was successful.
```js
const wasSet = Clipboard.setString('add this to clipboard');
```
<Preview withSource='none'>
<Story name="setString">{stories.setString}</Story>
</Preview>
### getString()
Not properly supported on Web. Returns a `Promise` of an empty string.

View File

@@ -0,0 +1,35 @@
import { Button, Clipboard, StyleSheet, TextInput, View } from 'react-native';
import React from 'react';
export default function SetString() {
const setString = () => {
const success = Clipboard.setString('This text was copied to the clipboard by React Native');
console.log(`Clipboard.setString success? ${success}`);
};
return (
<View>
<View style={styles.buttonBox}>
<Button onPress={setString} title="Copy to clipboard" />
</View>
<TextInput
multiline={true}
placeholder={'Try pasting here afterwards'}
style={styles.textInput}
/>
</View>
);
}
const styles = StyleSheet.create({
buttonBox: {
maxWidth: 300
},
textInput: {
borderColor: '#AAB8C2',
borderWidth: 1,
height: 50,
marginTop: 20,
padding: 5
}
});

View File

@@ -0,0 +1,6 @@
export default {
title: 'APIs|Dimensions',
includeStories: []
};
export { default as stateChanges } from './examples/StateChanges';

View File

@@ -0,0 +1,50 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as stories from './Dimensions.stories.js';
<Meta title="APIs|Dimensions" />
# Dimensions
Note: dimensions may change (e.g., due to device rotation) so any rendering
logic or styles that depend on these constants should try to call this function
on every render, rather than caching the value.
## Methods
### get(dimension)
Get a dimension (e.g., `window` or `screen`).
```js
const { height, width } = Dimensions.get('window')
```
### set(dimensions)
This should only be called server-side with an estimate for initial dimensions
to be used when pre-rendering pages on the server.
```js
Dimensions.set({
window: {
height: estimatedHeight,
width: estimatedWidth
}
})
```
### addEventListener(type, handler)
Add an event handler. Supported events:
* `change`: Fires when a property within the `Dimensions` object changes. The argument to the event handler is an object with `window` and `screen` properties whose values are the same as the return values of `Dimensions.get('window')` and `Dimensions.get('screen')`, respectively.
### removeEventListener(type, handler)
Remove an event handler.
## Example
<Preview withSource='none'>
<Story name="stateChanges">{stories.stateChanges}</Story>
</Preview>

View File

@@ -1,7 +1,7 @@
import { Button, Dimensions, ScrollView, StyleSheet, Text, View } from 'react-native';
import React, { Component } from 'react';
export default class ChangeEventExample extends Component {
class StateChangeExample extends Component {
state = {
listened: false,
logs: []
@@ -53,6 +53,10 @@ export default class ChangeEventExample extends Component {
};
}
export default function StateChange() {
return <StateChangeExample />;
}
const styles = StyleSheet.create({
logs: {
maxHeight: 256

View File

@@ -0,0 +1,6 @@
export default {
title: 'APIs|I18nManager',
includeStories: []
};
export { default as layoutRTL } from './examples/LayoutRTL';

View File

@@ -0,0 +1,48 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as stories from './I18nManager.stories.js';
<Meta title="APIs|I18nManager" />
# I18nManager
Control and query the layout and writing direction of the application.
## Properties
### isRTL
Whether the application is currently in RTL mode.
### doLeftAndRightSwapInRTL
Whether the application swaps left/right styles in RTL mode. (Default is `true`
but will eventually be changed to `false`).
## Methods
### allowRTL(bool)
Allow the application to display in RTL mode.
### forceRTL(bool)
Force the application to display in RTL mode.
### swapLeftAndRightInRTL(bool)
Control whether the application swaps `left`/`right` styles in RTL mode. It is
recommended that applications rely on `start`/`end` styles and disable automatic
BiDi-flipping of `left`/`right` styles, as this will eventually become the
default.
### setPreferredLanguageRTL(bool)
Set the application's preferred writing direction to RTL. You may need to infer
the user's preferred locale on the server (from HTTP headers) and decide whether
it's an RTL language. (Web-only)
## Example
<Preview withSource='none'>
<Story inline={false} name="layoutRTL">{stories.layoutRTL}</Story>
</Preview>

View File

@@ -0,0 +1,67 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class Block extends React.Component {
constructor(props) {
super(props);
this.state = { description: null };
}
render() {
let description;
if (this.props.description) {
description = <Text style={styles.descriptionText}>{this.props.description}</Text>;
}
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.titleText}>{this.props.title}</Text>
{description}
</View>
<View style={styles.children}>{this.props.children}</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
borderRadius: 3,
borderWidth: 0.5,
borderColor: '#d6d7da',
backgroundColor: '#ffffff',
margin: 10,
marginVertical: 5,
overflow: 'hidden'
},
titleContainer: {
borderBottomWidth: 0.5,
borderTopLeftRadius: 3,
borderTopRightRadius: 2.5,
borderBottomColor: '#d6d7da',
backgroundColor: '#f6f7f8',
paddingHorizontal: 10,
paddingVertical: 10
},
titleText: {
fontSize: 16,
fontWeight: '500'
},
descriptionText: {
fontSize: 14
},
disclosure: {
position: 'absolute',
top: 0,
right: 0,
padding: 10
},
disclosureIcon: {
width: 12,
height: 8
},
children: {
margin: 10
}
});

View File

@@ -0,0 +1,627 @@
/**
* Copyright (c) 2015-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.
*
* @noflow
*/
/* eslint-disable no-use-before-define */
import React from 'react';
import Page from './Page';
import Block from './Block';
import {
Animated,
Button,
I18nManager,
Image,
PanResponder,
PixelRatio,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableWithoutFeedback,
Switch,
View
} from 'react-native';
const SCALE = PixelRatio.get();
const IMAGE_DIMENSION = 100 * SCALE;
const IMAGE_SIZE = [IMAGE_DIMENSION, IMAGE_DIMENSION];
const iconSource =
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMjAwJyBoZWlnaHQ9JzIwMCcgZmlsbD0iIzAwMDAwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEwMCAxMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnPjxwYXRoIGQ9Ik0yNS44NjcsNDguODUzQzMyLjgwNiw1MC4xNzYsNDYuNDYsNTIuNSw2MS4yMTUsNTIuNWgwLjAwNWM5LjcxLDAsMTguNDAxLTEuMDU3LDI1LjkzOC0yLjkxMyAgIGMwLjE1OS0wLjA0NiwwLjM1LTAuMTM1LDAuNTY1LTAuMTg3YzAuMjgyLTAuMDcyLDAuNTY1LTAuMTY0LDAuODQ0LTAuMjM4YzMuMTg0LTAuOTY0LDIuNTc3LTMuMDUxLDIuMTk5LTMuODUyICAgYy00LjE2Ni03LjcxOS0xNS4wODYtMjMuNDE1LTM1LjAyOC0yMy40MTVjLTIyLjE2OSwwLTMwLjI2MiwxMC42MzUtMzMuMTQsMTkuNTg5QzIyLjU0NSw0Mi4zMzMsMjIuNDA3LDQ3LjEzNSwyNS44NjcsNDguODUzeiAgICBNMjguNjc2LDM4LjAzMmMwLjAxMy0wLjAzNiwwLjYxNC0xLjYyNiwxLjkyMy0xLjAwOGMxLjEzMywwLjUzNSwwLjk2MSwxLjU2MywwLjg4NywxLjg1Yy0wLjAwNywwLjAyNC0wLjAxNCwwLjA0OC0wLjAyMSwwLjA3MyAgIGMwLDAuMDAxLTAuMDAxLDAuMDA0LTAuMDAxLDAuMDA0bDAsMGMtMC4yNDksMC45MjktMC40MDQsMi4wODYtMC4wMTcsMi44NmMwLjE2LDAuMzE5LDAuNDkyLDAuNzY4LDEuNTQyLDAuOTg3bDAuMzY2LDAuMDc3ICAgYzIwLjgxNiw0LjM2LDM2LDIuOTMzLDQ1LjY3OCwwLjYyNmwtMC4wMDQsMC4wMDJjMCwwLDAuMDA1LTAuMDAyLDAuMDA3LTAuMDAzYzAuMjEyLTAuMDUsMC40MjEtMC4xMDEsMC42MjgtMC4xNTIgICBjMC41MDktMC4wNSwxLjE3MywwLjA3OCwxLjM5OSwxYzAuMzUxLDEuNDI0LTAuOTczLDEuODk1LTEuMjE3LDEuOTY5Yy01LjMyNSwxLjI3OS0xMi4yNjYsMi4zMDYtMjAuODM1LDIuMzA3ICAgYy03LjUwNSwwLTE2LjI1NS0wLjc4Ny0yNi4yNTctMi44ODJsLTAuMzY0LTAuMDc3Yy0yLjEyLTAuNDQyLTMuMTExLTEuNjMzLTMuNTY5LTIuNTU1QzI3Ljk4NSw0MS40MjEsMjguMjgxLDM5LjQxNiwyOC42NzYsMzguMDMyICAgeiI+PC9wYXRoPjxjaXJjbGUgY3g9IjEwLjQ5MyIgY3k9IjIzLjQ1NSIgcj0iMC42MTkiPjwvY2lyY2xlPjxwYXRoIGQ9Ik0yLjA4LDI4LjMwOGMwLjY3Ni0wLjE3OCwwLjk4My0wLjM1MiwxLjE3NC0wLjVDNC42OSwyNi42OSw2LjUsMjcuNDgzLDcuNSwyOC4zNTd2MC4wMDJjMCwwLDEuNzExLDEuMjM1LDAuNzM3LDIuMjAyICAgYy0wLjk3NCwwLjk2NS0yLjMxOSwwLjAwNi0yLjMxOSwwLjAwNmwwLjAzNSwwLjAxNmMtMC4zMjctMC4yMDMtMC42LTAuNTYxLTAuNzgtMC41ODRjLTAuMzcsMC4yNi0wLjg3NiwwLjUtMS40NzYsMC41SDMuNyAgIGMwLDAtMS4zNDUsMC43MDksMC4xNzgsMS42NTJjMC4wMDEsMC4wMDEsMC4wMDIsMC4wNzIsMC4wMDQsMC4wNzNjMy45MzksMi4zNDIsOC4yNzEsNS43MDEsOC4yNzEsOC44OCAgIGMwLDAuNjkxLDAuMiwxNy4wNDIsMTcuNjI2LDI0LjczOWwwLjk2NywwLjQ0MmwtMC4xLDEuMDU5Yy0wLjQyMSw0LjM5LDEuMTQ1LDEwLjE5MSwxMC45OTMsMTIuODg4bDAuMTEzLDAuMDM4ICAgYzAuMDY3LDAuMDIzLDYuNzMyLDIuNDI5LDEwLjkwNywyLjQyOWMxLjU4NCwwLDIuMTU1LTAuMzUyLDIuMjQzLTAuNTYxYzAuMDg1LTAuMjAyLDAuNjEyLTIuMTY0LTYuMzMyLTkuMzg3bDAuMDAyLTAuMTgzICAgYzAsMC0yLjQ3Ny0zLjA3LDEuNTMzLTMuMDdjMC4wMSwwLDAuMDE5LDAsMC4wMjksMGMxLjI4NSwwLDIuNjA4LDAuMjE1LDMuOTgsMC4xODRjNC43NzEtMC4xMTcsOS4zMTYtMC40MjUsMTMuNTA2LTEuMDk2ICAgbDAuNDc0LTAuMDI4bDAuNjY4LDAuMTU4YzkuNjUxLDQuOTQ4LDE2LjczOCw3LjcxNiwxOS43MzgsNy43MTZ2MC4wMDZjMCwwLDAuMTY0LDAuMDExLDAuMjMsMC4wMDQgICBjLTAuMTg5LTAuNzIzLTIuMjMtMi44LTcuMjMtOS4wNzl2MC4wMjFjMCwwLTEuNTEyLTEuNjU4LDAuNzk3LTIuNjUzYzAuMDYzLTAuMDI2LDAuMDA4LDAuMDIzLDAuMDYtMC4wMDEgICBjOC42MzktMy41MDksMTMuNTAxLTguMjA0LDE1LjQxMS0xMS43NzVjMS4xNDUtMi4xMjksMC4yMDYtMi43ODQtMC42NTktMi45NzZjLTAuMzE3LTAuMDM4LTAuNjM0LTAuMDYyLTAuOTEyLTAuMDYyICAgYy0wLjIwNSwwLTAuMzc5LDAuMDEtMC41MjgsMC4wMjdsLTMuMTQzLDEuMjE0QzgzLjczMiw1My45MjYsNzMuMjE4LDU1LjUsNjEuMjIsNTUuNWMtMC4wMDIsMC0wLjAwNSwwLTAuMDA1LDAgICBjLTE1LjEyOCwwLTI5LjEwMS0yLjQzMi0zNi4wODMtMy43NzFsLTAuMTczLTAuMTExbC0wLjE2LTAuMTI2Yy01Ljg1OC0yLjY4MS01LjEzNy0xMC4yMDItNS4xMDMtMTAuNTE5bDAuMDYtMC4zICAgYzAuODk1LTIuODM4LDIuNDY3LTYuMzUyLDUuMjEzLTkuNzE5Yy0xLjgwOC0xLjM2OS00LjU5LTQuMTg4LTQuNDMtOC40OTRjMC4wNDYtMS4yNDQtMC40ODYtMi41MDgtMS40OTgtMy41NTkgICBjLTEuNDk4LTEuNTU1LTMuNzg1LTIuNDQ2LTYuMjc0LTIuNDQ2Yy0xLjc3LDAtMy41NTMsMC40NDItNS4yOTMsMS4zMTRjLTQuMDYxLDIuMDM1LTQuODU1LDQuNzM2LTUuNjkyLDcuNTk2ICAgYy0wLjEzNiwwLjQ2OC0wLjI4NCwwLjkzOS0wLjQzOCwxLjQxYy0wLjAwNiwwLjAxOS0wLjAyMiwwLjAzNS0wLjAyOCwwLjA1NkMwLjgzMywyOC40MjMsMS42OTEsMjguMzksMi4wOCwyOC4zMDh6IE0xMC40OTMsMTkuOTA4ICAgYzEuOTU2LDAsMy41NDgsMS41OTEsMy41NDgsMy41NDdjMCwxLjk1Ny0xLjU5MiwzLjU0OC0zLjU0OCwzLjU0OGMtMS45NTcsMC0zLjU0OC0xLjU5Mi0zLjU0OC0zLjU0OCAgIEM2Ljk0NCwyMS40OTksOC41MzYsMTkuOTA4LDEwLjQ5MywxOS45MDh6Ij48L3BhdGg+PC9nPjwvc3ZnPg==';
function ListItem(props) {
return (
<View style={styles.row}>
<View style={styles.column1}>
<Image source={props.imageSource} style={styles.icon} />
</View>
<View style={styles.column2}>
<View style={styles.textBox}>
<Text>Text Text Text</Text>
</View>
</View>
<View style={styles.column3}>
<Button onPress={() => {}} style={styles.smallButton} title="Button" />
</View>
</View>
);
}
function TextAlignmentExample(props) {
return (
<Block description={props.description} title={props.title}>
<View>
<Text style={props.style}>Left-to-Right language text alignment.</Text>
<Text style={props.style}>
{'\u0645\u0646 \u0627\u0644\u064A\u0645\u064A\u0646 ' +
'\u0625\u0644\u0649 \u0627\u0644\u064A\u0633\u0627\u0631 ' +
'\u0627\u0644\u0644\u063A\u0629 \u062F\u0648\u0646 ' +
'\u0645\u062D\u0627\u0630\u0627\u0629 \u0627\u0644\u0646\u0635'}
</Text>
<Text style={props.style}>
{'\u05DE\u05D9\u05DE\u05D9\u05DF \u05DC\u05E9\u05DE\u05D0\u05DC ' +
'\u05D4\u05E9\u05E4\u05D4 \u05D1\u05DC\u05D9 ' +
'\u05D9\u05D9\u05E9\u05D5\u05E8 \u05D8\u05E7\u05E1\u05D8'}
</Text>
</View>
</Block>
);
}
function AnimationBlock(props) {
return (
<View style={styles.block}>
<TouchableWithoutFeedback onPress={props.onPress}>
<Animated.Image
source={{ uri: 'https://picsum.photos/130/130/?image=909' }}
style={[styles.img, props.imgStyle]}
/>
</TouchableWithoutFeedback>
</View>
);
}
function withRTLState(Component) {
return class extends React.Component {
constructor(...args) {
super(...args);
this.state = {
isRTL: false
};
}
render() {
const isRTL = Platform === 'ios' ? this.state.isRTL : I18nManager.isRTL;
const setRTL = isRTL => this.setState({ isRTL: isRTL });
return <Component isRTL={isRTL} setRTL={setRTL} />;
}
};
}
const RTLToggler = ({ isRTL, setRTL }) => {
if (Platform.OS !== 'ios') {
return <Text style={styles.rtlToggler}>{isRTL ? 'RTL' : 'LTR'}</Text>;
}
const toggleRTL = () => setRTL(!isRTL);
return (
<Button
accessibilityLabel="Change layout direction"
color="gray"
onPress={toggleRTL}
title={isRTL ? 'RTL' : 'LTR'}
/>
);
};
const PaddingExample = withRTLState(({ isRTL, setRTL }) => {
const color = 'teal';
return (
<Block title={'padding{Start,End}'}>
<Text style={styles.bold}>Styles</Text>
<Text>paddingStart: 50,</Text>
<Text>paddingEnd: 10</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<Text>The {color} is padding.</Text>
<View
style={{
backgroundColor: color,
paddingStart: 50,
paddingEnd: 10,
borderWidth: 1,
borderColor: color,
direction: isRTL ? 'rtl' : 'ltr'
}}
>
<View
style={{
backgroundColor: 'white',
paddingTop: 5,
paddingBottom: 5,
borderLeftWidth: 1,
borderRightWidth: 1,
borderColor: 'gray'
}}
>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</Block>
);
});
const MarginExample = withRTLState(({ isRTL, setRTL }) => {
return (
<Block title={'margin{Start,End}'}>
<Text style={styles.bold}>Styles</Text>
<Text>marginStart: 50,</Text>
<Text>marginEnd: 10</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<Text>The green is margin.</Text>
<View
style={{
backgroundColor: 'green',
borderWidth: 1,
borderColor: 'green',
direction: isRTL ? 'rtl' : 'ltr'
}}
>
<View
style={{
backgroundColor: 'white',
paddingTop: 5,
paddingBottom: 5,
marginStart: 50,
marginEnd: 10,
borderLeftWidth: 1,
borderRightWidth: 1,
borderColor: 'gray'
}}
>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</Block>
);
});
const PositionExample = withRTLState(({ isRTL, setRTL }) => {
return (
<Block title={'position: "start" | "end"'}>
<Text style={styles.bold}>Styles</Text>
<Text>start: 50</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<Text>The orange is position.</Text>
<View
style={{
backgroundColor: 'orange',
borderWidth: 1,
borderColor: 'orange',
direction: isRTL ? 'rtl' : 'ltr'
}}
>
<View
style={{
backgroundColor: 'white',
start: 50,
borderColor: 'gray'
}}
>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
<Text />
<Text style={styles.bold}>Styles</Text>
<Text>end: 50</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<Text>The orange is position.</Text>
<View
style={{
backgroundColor: 'orange',
borderWidth: 1,
borderColor: 'orange',
direction: isRTL ? 'rtl' : 'ltr'
}}
>
<View
style={{
backgroundColor: 'white',
end: 50,
borderColor: 'gray'
}}
>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</Block>
);
});
const BorderWidthExample = withRTLState(({ isRTL, setRTL }) => {
return (
<Block title={'border{Start,End}Width'}>
<Text style={styles.bold}>Styles</Text>
<Text>borderStartWidth: 10,</Text>
<Text>borderEndWidth: 50</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<View style={{ direction: isRTL ? 'rtl' : 'ltr' }}>
<View
style={{
borderStartWidth: 10,
borderEndWidth: 50
}}
>
<View>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</View>
</Block>
);
});
const BorderColorExample = withRTLState(({ isRTL, setRTL }) => {
return (
<Block title={'border{Start,End}Color'}>
<Text style={styles.bold}>Styles</Text>
<Text>borderStartColor: 'red',</Text>
<Text>borderEndColor: 'green',</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<View style={{ direction: isRTL ? 'rtl' : 'ltr' }}>
<View
style={{
borderStartColor: 'red',
borderEndColor: 'green',
borderLeftWidth: 20,
borderRightWidth: 20,
padding: 10
}}
>
<View>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</View>
</Block>
);
});
const BorderRadiiExample = withRTLState(({ isRTL, setRTL }) => {
return (
<Block title={'border{Top,Bottom}{Start,End}Radius'}>
<Text style={styles.bold}>Styles</Text>
<Text>borderTopStartRadius: 10,</Text>
<Text>borderTopEndRadius: 20,</Text>
<Text>borderBottomStartRadius: 30,</Text>
<Text>borderBottomEndRadius: 40</Text>
<Text />
<Text style={styles.bold}>Demo: </Text>
<View style={{ direction: isRTL ? 'rtl' : 'ltr' }}>
<View
style={{
borderWidth: 10,
borderTopStartRadius: 10,
borderTopEndRadius: 20,
borderBottomStartRadius: 30,
borderBottomEndRadius: 40,
padding: 10
}}
>
<View>
<RTLToggler isRTL={isRTL} setRTL={setRTL} />
</View>
</View>
</View>
</Block>
);
});
class LayoutRTLExample extends React.Component {
constructor(props) {
super(props);
const pan = new Animated.ValueXY();
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: this._onPanResponderGrant,
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }]),
onPanResponderRelease: this._onPanResponderEnd,
onPanResponderTerminate: this._onPanResponderEnd
});
const { doLeftAndRightSwapInRTL, isRTL } = I18nManager;
this.state = {
toggleStatus: {},
pan,
linear: new Animated.Value(0),
isRTL,
doLeftAndRightSwapInRTL,
containerWidth: 0
};
this._linearTap = this._linearTap.bind(this);
this._onContainerLayout = this._onContainerLayout.bind(this);
this._onDirectionChange = this._onDirectionChange.bind(this);
this._onPanResponderGrant = this._onPanResponderGrant.bind(this);
this._onPanResponderEnd = this._onPanResponderEnd.bind(this);
this._onSwapChange = this._onSwapChange.bind(this);
}
render() {
return (
<ScrollView
style={[
styles.container,
// `direction` property is not supported on Android.
Platform.OS !== 'android' ? { direction: this.state.isRTL ? 'rtl' : 'ltr' } : null
]}
>
<Page title={'React Native: Right-to-Left (RTL) UI Layout'}>
<Block title={'Current layout direction'}>
<View style={styles.directionBox}>
<Text style={styles.directionText}>
{this.state.isRTL ? 'Right-to-Left' : 'Left-to-Right'}
</Text>
</View>
</Block>
<Block title={'Quickly test RTL layout'}>
<View style={[styles.flexDirectionRow, styles.switchRow]}>
<Text style={{ fontWeight: 'bold' }}>forceRTL</Text>
<View>
<Switch onValueChange={this._onDirectionChange} value={this.state.isRTL} />
</View>
</View>
<View style={[styles.flexDirectionRow, styles.switchRow]}>
<Text style={{ fontWeight: 'bold' }}>swapLeftAndRightInRTL</Text>
<View>
<Switch
onValueChange={this._onSwapChange}
value={this.state.doLeftAndRightSwapInRTL}
/>
</View>
</View>
</Block>
<TextAlignmentExample
description={'Depends on the text content.'}
style={styles.fontSizeSmall}
title={'Default text alignment'}
/>
<TextAlignmentExample
style={[styles.fontSizeSmall, styles.textAlignLeft]}
title={'textAlign: "left"'}
/>
<TextAlignmentExample
style={[styles.fontSizeSmall, styles.textAlignRight]}
title={'textAlign: "right"'}
/>
<TextAlignmentExample
style={[styles.fontSizeSmall, styles.textAlignStart]}
title={'textAlign: "start"'}
/>
<TextAlignmentExample
style={[styles.fontSizeSmall, styles.textAlignEnd]}
title={'textAlign: "end"'}
/>
<PaddingExample />
<MarginExample />
<PositionExample />
<BorderColorExample />
<BorderWidthExample />
<BorderRadiiExample />
<Block title={'A simple list-item layout'}>
<View style={styles.list}>
<ListItem imageSource={{ uri: 'https://picsum.photos/130/130?image=222' }} />
<ListItem imageSource={{ uri: 'https://picsum.photos/130/130?image=250' }} />
</View>
</Block>
<Block title={'Working with icons'}>
<View style={[styles.flexDirectionRow, { justifyContent: 'space-around' }]}>
<View style={{ alignItems: 'center' }}>
<Image source={iconSource} style={styles.image} />
<Text style={styles.fontSizeSmall}>No RTL flip</Text>
</View>
<View style={{ alignItems: 'center' }}>
<Image
source={iconSource}
style={[styles.image, { transform: [{ scaleX: this.state.isRTL ? -1 : 1 }] }]}
/>
<Text style={styles.fontSizeSmall}>RTL flip</Text>
</View>
</View>
</Block>
<Block
description={'Animation direction according to layout'}
title={'Controlling animation'}
>
<View onLayout={this._onContainerLayout} style={styles.view}>
<AnimationBlock
imgStyle={{
transform: [
{ translateX: this.state.linear },
{ scaleX: this.state.isRTL ? -1 : 1 }
]
}}
onPress={this._linearTap}
/>
</View>
</Block>
</Page>
</ScrollView>
);
}
_onContainerLayout(e) {
this.setState(() => ({ containerWidth: e.nativeEvent.layout.width }));
}
_onDirectionChange() {
I18nManager.forceRTL(!this.state.isRTL);
this.setState({ isRTL: !this.state.isRTL });
}
_onSwapChange() {
I18nManager.swapLeftAndRightInRTL(!this.state.doLeftAndRightSwapInRTL);
this.setState({
doLeftAndRightSwapInRTL: !this.state.doLeftAndRightSwapInRTL
});
}
_linearTap(refName, e) {
this.setState({
toggleStatus: {
...this.state.toggleStatus,
[refName]: !this.state.toggleStatus[refName]
}
});
const offset = IMAGE_SIZE[0] / SCALE / 2 + 10;
const toMaxDistance = (this.state.isRTL ? -1 : 1) * (this.state.containerWidth / 2 - offset);
Animated.timing(this.state.linear, {
toValue: this.state.toggleStatus[refName] ? toMaxDistance : 0,
duration: 2000,
useNativeDriver: false
}).start();
}
_onPanResponderGrant(e, gestureState) {
this.state.pan.stopAnimation(value => {
this.state.pan.setOffset(value);
});
}
_onPanResponderEnd(e, gestureState) {
this.state.pan.flattenOffset();
Animated.sequence([
Animated.decay(this.state.pan, {
velocity: { x: gestureState.vx, y: gestureState.vy },
deceleration: 0.995
}),
Animated.spring(this.state.pan, { toValue: { x: 0, y: 0 } })
]).start();
}
}
export default function LayoutRTL() {
return <LayoutRTLExample />;
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#e9eaed'
},
directionBox: {
flex: 1,
backgroundColor: '#f8f8f8',
borderWidth: 0.5,
borderColor: 'black'
},
directionText: {
padding: 10,
fontSize: 16,
textAlign: 'center',
fontWeight: 'bold'
},
switchRow: {
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 5
},
list: {
height: 120,
marginBottom: 5,
borderTopWidth: 0.5,
borderLeftWidth: 0.5,
borderRightWidth: 0.5,
borderColor: '#e5e5e5'
},
row: {
height: 60,
flexDirection: 'row',
borderBottomWidth: 0.5,
borderColor: '#e5e5e5'
},
column1: {
width: 60,
padding: 6
},
column2: {
flex: 1,
padding: 6
},
column3: {
justifyContent: 'center',
padding: 6
},
icon: {
width: 48,
height: 48,
borderWidth: 0.5,
borderColor: '#e5e5e5'
},
image: {
width: 48,
height: 48
},
img: {
width: IMAGE_SIZE[0] / SCALE,
height: IMAGE_SIZE[1] / SCALE
},
view: {
flex: 1
},
block: {
padding: 10,
alignItems: 'center'
},
smallButton: {
height: 24,
width: 64
},
fontSizeSmall: {
fontSize: 14
},
fontSizeExtraSmall: {
fontSize: 12
},
textAlignLeft: {
textAlign: 'left'
},
textAlignRight: {
textAlign: 'right'
},
textAlignStart: {
textAlign: 'start'
},
textAlignEnd: {
textAlign: 'end'
},
flexDirectionRow: {
flexDirection: 'row'
},
bold: {
fontWeight: 'bold'
},
rtlToggler: {
color: 'gray',
padding: 8,
textAlign: 'center',
fontWeight: '500'
}
});

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { ScrollView, StyleSheet, View } from 'react-native';
import Title from './Title';
export default class Page extends React.Component {
render() {
let ContentWrapper;
const wrapperProps = {};
if (this.props.noScroll) {
ContentWrapper = View;
} else {
ContentWrapper = ScrollView;
// $FlowFixMe found when converting React.createClass to ES6
wrapperProps.automaticallyAdjustContentInsets = !this.props.title;
wrapperProps.keyboardShouldPersistTaps = 'handled';
wrapperProps.keyboardDismissMode = 'interactive';
}
const title = this.props.title ? <Title title={this.props.title} /> : null;
const spacer = this.props.noSpacer ? null : <View style={styles.spacer} />;
return (
<View style={styles.container}>
{title}
<ContentWrapper style={styles.wrapper} {...wrapperProps}>
{this.props.children}
{spacer}
</ContentWrapper>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#e9eaed',
flex: 1,
maxWidth: 600,
margin: 'auto',
width: '100%'
},
spacer: {
height: 270
},
wrapper: {
flex: 1,
paddingTop: 10
}
});

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class Title extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>{this.props.title}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: '#d6d7da',
margin: 10,
marginBottom: 0,
height: 45,
padding: 10,
backgroundColor: 'white'
},
text: {
fontSize: 20,
fontWeight: '500'
}
});

View File

@@ -0,0 +1,6 @@
export default {
title: 'APIs|Linking',
includeStories: []
};
export { default as openURL } from './examples/OpenURL';

View File

@@ -0,0 +1,30 @@
import { Meta, Props, Preview, Story } from '@storybook/addon-docs/blocks';
import * as stories from './Linking.stories.js';
<Meta title="APIs|Linking" />
# Linking
Linking gives you a general interface for securely opening external URLs from JavaScript.
## Methods
### canOpenURL(url)
Returns a `Promise` that resolves to a boolean indicating whether the app can open the URL.
### getInitialURL()
Returns a `Promise` that resolves to the string of the URL that initially loaded the app.
### openURL(url)
Try to open the given url in a secure fashion. The method returns a `Promise`
object. If the url opens, the promise is resolved. If not, the promise is
rejected.
## Example
<Preview withSource='none'>
<Story name="openURL">{stories.openURL}</Story>
</Preview>

View File

@@ -1,13 +1,9 @@
/**
* @flow
*/
import { Linking, StyleSheet, Text, View } from 'react-native';
import React, { PureComponent } from 'react';
const url = 'https://mathiasbynens.github.io/rel-noopener/malicious.html';
export default class OpenURL extends PureComponent {
class OpenURLExample extends PureComponent {
handlePress() {
Linking.canOpenURL(url).then(supported => {
return Linking.openURL(url);
@@ -33,6 +29,10 @@ export default class OpenURL extends PureComponent {
}
}
export default function OpenURL() {
return <OpenURLExample />;
}
const styles = StyleSheet.create({
text: {
borderRadius: 5,

View File

@@ -0,0 +1,37 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="APIs|PixelRatio" />
# PixelRatio
PixelRatio class gives access to the device pixel density.
## Methods
### get()
Returns the device pixel density as a number.
### getFontScale()
On web this returns the device pixel ratio as a number.
### getPixelSizeForLayoutSize(number)
Converts a layout size (dp) to pixel size (px). Guaranteed to return an integer number.
```js
const image = getImage({
width: PixelRatio.getPixelSizeForLayoutSize(200),
height: PixelRatio.getPixelSizeForLayoutSize(100),
});
<Image source={image} style={{width: 200, height: 100}} />
```
### roundToNearestPixel(number)
Rounds a layout size (dp) to the nearest layout size that corresponds to an
integer number of pixels. For example, on a device with a PixelRatio of 3,
`PixelRatio.roundToNearestPixel(8.4) = 8.33`, which corresponds to exactly
`(8.33 * 3) = 25` pixels.

View File

@@ -0,0 +1,49 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="APIs|Platform" />
# Platform
Detect what is the platform in which the app is running. This piece of
functionality can be useful when only small parts of a component are platform
specific.
## Properties
### OS
`Platform.OS` will be `web` when running in a Web browser.
```js
import { Platform } from 'react-native';
const styles = StyleSheet.create({
height: (Platform.OS === 'web') ? 200 : 100,
});
```
## Methods
### select(config)
`Platform.select` takes an object containing `Platform.OS` as keys and returns
the value for the platform you are currently running on.
```js
import { Platform } from 'react-native';
const containerStyles = {
flex: 1,
...Platform.select({
android: {
backgroundColor: 'blue'
},
ios: {
backgroundColor: 'red'
},
web: {
backgroundColor: 'green'
}
})
});
```

View File

@@ -0,0 +1,83 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="APIs|StyleSheet" />
# StyleSheet
The StyleSheet abstraction converts predefined styles to (vendor-prefixed) CSS
without requiring a compile-time step. Styles that cannot be resolved outside of
the render loop (e.g., dynamic positioning) are usually applied as inline
styles.
## Methods
### compose(style1, style2)
Combines two styles such that the last style overrides properties of the first
style. If either style is falsy, the other one is returned without allocating an
array, saving allocations and maintaining reference equality.
```js
StyleSheet.compose(style1, style2);
```
### create(styles)
Each key of the object passed to `create` must define a style object. The
returned object replaces style objects with IDs.
```js
const styles = StyleSheet.create({
container: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: '#d6d7da',
},
title: {
fontSize: 19,
fontWeight: 'bold',
}
})
```
### flatten(style)
Lookup a style object by ID or flatten an array of styles into a single style
object.
```js
StyleSheet.flatten(styles.listItem);
StyleSheet.flatten([styles.listItem, styles.selectedListItem]);
```
## Properties
### absoluteFill
A very common pattern is to create overlays with position absolute and zero
positioning, so `absoluteFill` can be used for convenience and to reduce
duplication of these repeated styles.
```js
<View style={StyleSheet.absoluteFill} />
```
### absoluteFillObject
Sometimes you may want `absoluteFill` but with a couple tweaks -
`absoluteFillObject` can be used to create a customized entry in a `StyleSheet`
```js
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'transparent',
top: 10
}
});
```
### hairlineWidth
Enables borders of just one physical pixel on retina screens, otherwise it is
equal to a CSS value of 1px.

View File

@@ -0,0 +1,33 @@
import { Meta } from '@storybook/addon-docs/blocks';
<Meta title="APIs|Vibration" />
# Vibration
Vibration is described as a pattern of on-off pulses, which may be of varying
lengths. The pattern may consist of either a single integer, describing the
number of milliseconds to vibrate, or an array of integers describing a pattern
of vibrations and pauses. Vibration is controlled with a single method:
`Vibration.vibrate()`.
The vibration is asynchronous so this method will return immediately. There will
be no effect on devices that do not support vibration.
## Methods
### cancel()
Stop the vibration
### vibrate(pattern)
Start the vibration pattern
```js
// Vibrate once for 200ms
Vibration.vibrate(200);
Vibration.vibrate([200]);
// Vibrate for 200ms, pause for 100ms, vibrate for 200ms:
Vibration.vibrate([200, 100, 200]);
```

View File

@@ -0,0 +1,29 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ViewPropTypes': PropTypes.any,
animating: PropTypes.bool,
color: PropTypes.string,
hidesWhenStopped: PropTypes.bool,
size: PropTypes.oneOfType([PropTypes.oneOf(['small', 'large']), PropTypes.number])
};
ofProps.defaultProps = {
animating: true,
color: '#1976D2',
hidesWhenStopped: true,
size: 'small'
};
export default {
title: 'Components|ActivityIndicator',
includeStories: []
};
export { ofProps };
export { default as animating } from './examples/Animating';
export { default as color } from './examples/Color';
export { default as hidesWhenStopped } from './examples/HidesWhenStopped';
export { default as size } from './examples/Size';

View File

@@ -0,0 +1,45 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './ActivityIndicator.stories.js';
<Meta title="Components|ActivityIndicator" />
# ActivityIndicator
Displays a customizable activity indicator.
## Props
<Props of={stories.ofProps} />
### animating
Controls whether to show the indicator or hide it.
<Preview withSource='none'>
<Story name="animating">{stories.animating}</Story>
</Preview>
### color
Customize the foreground color of the indicator.
<Preview withSource='none'>
<Story name="color">{stories.color}</Story>
</Preview>
### hidesWhenStopped
Control whether to show the indicator when it is not animating.
<Preview withSource='none'>
<Story name="hidesWhenStopped">{stories.hidesWhenStopped}</Story>
</Preview>
### size
Customize the size of the indicator. `small` has a height of `20px`, large has a
height of `36px`. Scale transforms can also be used.
<Preview withSource='none'>
<Story name="size">{stories.size}</Story>
</Preview>

View File

@@ -0,0 +1,18 @@
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import React from 'react';
export default function Animating() {
return (
<View style={styles.horizontal}>
<ActivityIndicator />
<ActivityIndicator animating={false} />
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
}
});

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
export default function Color() {
return (
<View style={styles.horizontal}>
<ActivityIndicator color="#1DA1F2" style={styles.item} />
<ActivityIndicator color="#17BF63" style={styles.item} />
<ActivityIndicator color="#F45D22" style={styles.item} />
<ActivityIndicator color="#794BC4" style={styles.item} />
<ActivityIndicator color="#E0245E" style={styles.item} />
<ActivityIndicator color="#FFAD1F" style={styles.item} />
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
item: {
paddingRight: 10
}
});

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { bool } from 'prop-types';
import React, { PureComponent } from 'react';
@@ -43,26 +39,21 @@ class ToggleAnimatingActivityIndicator extends PureComponent {
}
}
const ActivityIndicatorHidesWhenStoppedExample = () => (
<View style={[styles.horizontal]}>
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} style={styles.rightPadding} />
<ToggleAnimatingActivityIndicator />
</View>
);
export default function HidesWhenStoppedExample() {
return (
<View style={[styles.horizontal]}>
<ToggleAnimatingActivityIndicator hidesWhenStopped={false} style={styles.item} />
<ToggleAnimatingActivityIndicator />
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
rightPadding: {
item: {
paddingRight: 10
}
});
ActivityIndicatorHidesWhenStoppedExample.metadata = {
id: 'ActivityIndicator.props.hidesWhenStopped',
description: ''
};
export default ActivityIndicatorHidesWhenStoppedExample;

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
const sizes = [20, 'small', 36, 'large', 60];
export default function Size() {
return (
<View style={styles.horizontal}>
{sizes.map((size, i) => (
<ActivityIndicator key={i} size={size} style={styles.item} />
))}
<ActivityIndicator size="large" style={styles.large} />
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
alignItems: 'center',
flexDirection: 'row'
},
item: {
paddingRight: 10
},
large: {
marginLeft: 20,
transform: [{ scale: 1.75 }]
}
});

View File

@@ -0,0 +1,27 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
accessibilityLabel: PropTypes.string,
color: PropTypes.string,
disabled: PropTypes.bool,
onPress: PropTypes.func,
testID: PropTypes.string,
title: PropTypes.string
};
ofProps.defaultProps = {
color: '#2196F3',
disabled: false
};
export default {
title: 'Components|Button',
includeStories: []
};
export { ofProps };
export { default as color } from './examples/Color';
export { default as disabled } from './examples/Disabled';
export { default as onPress } from './examples/OnPress';

View File

@@ -0,0 +1,45 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './Button.stories.js';
<Meta title="Components|Button" />
# Button
A basic button component. Supports a minimal level of customization. You can
build your own custom button using `TouchableOpacity` or `TouchableHighlight`.
## Props
<Props of={stories.ofProps} />
### color
Customize the background color of the button.
<Preview withSource='none'>
<Story name="color">{stories.color}</Story>
</Preview>
### disabled
Prevent all interactions with the button.
<Preview withSource='none'>
<Story name="disabled">{stories.disabled}</Story>
</Preview>
### onPress
Called when the button is pressed by a pointer or keyboard.
<Preview withSource='none'>
<Story name="onPress">{stories.onPress}</Story>
</Preview>
### title
Text to display inside the button.
```js
<Button title="Text content" />
```

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { DividerVertical } from '../helpers';
import { Button, View } from 'react-native';
const emptyFunction = () => {};
export default function Color() {
return (
<View>
<Button color="#17BF63" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#F45D22" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#794BC4" onPress={emptyFunction} title="Press me" />
<DividerVertical />
<Button color="#E0245E" onPress={emptyFunction} title="Press me" />
</View>
);
}

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { Button } from 'react-native';
export default function Disabled() {
const onPress = () => {
console.error('Disabled button should not trigger onPress!');
};
return <Button disabled onPress={onPress} title="Disabled button" />;
}

View File

@@ -0,0 +1,25 @@
import React from 'react';
import { DividerHorizontal } from '../helpers';
import { Button, StyleSheet, View } from 'react-native';
const emptyFunction = () => {};
export default function OnPress() {
return (
<View style={styles.horizontal}>
<Button
accessibilityLabel="This sounds great!"
onPress={emptyFunction}
title="This looks great!"
/>
<DividerHorizontal />
<Button color="#841584" onPress={emptyFunction} title="Ok!" />
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
flexDirection: 'row'
}
});

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import React from 'react';
import { StyleSheet, View } from 'react-native';

View File

@@ -0,0 +1,29 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ViewPropTypes': PropTypes.any,
color: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func,
onValueChange: PropTypes.func,
value: PropTypes.bool
};
ofProps.defaultProps = {
disabled: false,
value: false
};
export default {
title: 'Components|CheckBox',
includeStories: []
};
export { ofProps };
export { default as color } from './examples/Color';
export { default as disabled } from './examples/Disabled';
export { default as onValueChange } from './examples/OnValueChange';
export { default as value } from './examples/Value';
export { default as customSize } from './examples/CustomSize';

View File

@@ -0,0 +1,56 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './CheckBox.stories.js';
<Meta title="Components|CheckBox" />
# CheckBox
This is a controlled component that requires an `onValueChange` callback that
updates the value prop in order for the component to reflect user actions. If
the `value` prop is not updated, the component will continue to render the
supplied `value` prop instead of the expected result of any user actions.
## Props
<Props of={stories.ofProps} />
### color
Customize the color of the active checkbox.
<Preview withSource='none'>
<Story name="color">{stories.color}</Story>
</Preview>
### disabled
Prevent interaction with the checkbox.
<Preview withSource='none'>
<Story name="disabled">{stories.disabled}</Story>
</Preview>
### onValueChange
Called with the event when the value changes.
<Preview withSource='none'>
<Story name="onValueChange">{stories.onValueChange}</Story>
</Preview>
### value
Controls whether the checkbox is active or not.
<Preview withSource='none'>
<Story name="value">{stories.value}</Story>
</Preview>
## Notes
The checkbox size can be controlled by changing the `height` and `width` style
properties.
<Preview withSource='none'>
<Story name="customSize">{stories.customSize}</Story>
</Preview>

View File

@@ -0,0 +1,16 @@
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
export default function Color() {
return (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox color="#1DA1F2" value />
</View>
<View style={styles.marginRight}>
<CheckBox color="#F45D22" value />
</View>
</View>
);
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
export default function CustomSize() {
return (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox style={{ height: 20, width: 20 }} value />
</View>
<View style={styles.marginRight}>
<CheckBox style={{ height: 32, width: 32 }} value />
</View>
</View>
);
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
export default function Disabled() {
return (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox disabled value={false} />
</View>
<View style={styles.marginRight}>
<CheckBox disabled value />
</View>
</View>
);
}

View File

@@ -1,12 +1,8 @@
/**
* @flow
*/
import styles from './styles';
import React, { PureComponent } from 'react';
import { CheckBox, Text, View } from 'react-native';
class CheckBoxOnValueChangeExample extends PureComponent {
class OnValueChangeExample extends PureComponent {
state = {
eventSwitchIsOn: false,
eventSwitchRegressionIsOn: true
@@ -56,4 +52,6 @@ class CheckBoxOnValueChangeExample extends PureComponent {
};
}
export default CheckBoxOnValueChangeExample;
export default function OnValueChange() {
return <OnValueChangeExample />;
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
import styles from './styles';
import { CheckBox, View } from 'react-native';
export default function Value() {
return (
<View style={styles.row}>
<View style={styles.marginRight}>
<CheckBox value={false} />
</View>
<View style={styles.marginRight}>
<CheckBox value />
</View>
</View>
);
}

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import React from 'react';
import { StyleSheet, View } from 'react-native';

View File

@@ -0,0 +1,7 @@
export default {
title: 'Components|FlatList',
includeStories: []
};
export { default as singleColumn } from './examples/SingleColumn';
export { default as multiColumn } from './examples/MultiColumn';

View File

@@ -0,0 +1,26 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './FlatList.stories.js';
<Meta title="Components|FlatList" />
# FlatList
An interface for rendering basic, flat lists. Please refer to the React Native
[documentation for FlatList](https://facebook.github.io/react-native/docs/flatlist).
Note that `FlatList` is not optimized for the web. You may wish to consider
alternatives such as
[recyclerlistview](https://github.com/Flipkart/recyclerlistview) and
[FastList](https://gist.github.com/vishnevskiy/f4ba74adf5cf1d269b860fab86e8feef).
### Single column
<Preview withSource='none'>
<Story name="singleColumn">{stories.singleColumn}</Story>
</Preview>
### Multiple columns
<Preview withSource='none'>
<Story name="multiColumn">{stories.multiColumn}</Story>
</Preview>

View File

@@ -4,25 +4,14 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @providesModule MultiColumnExample
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
FlatList,
StyleSheet,
Text,
View,
} = ReactNative;
import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';
const RNTesterPage = require('./RNTesterPage');
const infoLog = require('infoLog');
const {
import {
FooterComponent,
HeaderComponent,
ItemComponent,
@@ -31,10 +20,10 @@ const {
genItemData,
getItemLayout,
pressItem,
renderSmallSwitchOption,
} = require('./ListExampleShared');
renderSmallSwitchOption
} from './shared';
class MultiColumnExample extends React.PureComponent<$FlowFixMeProps, $FlowFixMeState> {
class MultiColumnExample extends React.PureComponent {
static title = '<FlatList> - MultiColumn';
static description = 'Performant, scrollable grid of data.';
@@ -44,23 +33,20 @@ class MultiColumnExample extends React.PureComponent<$FlowFixMeProps, $FlowFixMe
fixedHeight: true,
logViewable: false,
numColumns: 2,
virtualized: true,
virtualized: true
};
_onChangeFilterText = (filterText) => {
this.setState(() => ({filterText}));
_onChangeFilterText = filterText => {
this.setState(() => ({ filterText }));
};
_onChangeNumColumns = (numColumns) => {
this.setState(() => ({numColumns: Number(numColumns)}));
_onChangeNumColumns = numColumns => {
this.setState(() => ({ numColumns: Number(numColumns) }));
};
render() {
const filterRegex = new RegExp(String(this.state.filterText), 'i');
const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title));
const filter = item => filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = this.state.data.filter(filter);
return (
<RNTesterPage
title={this.props.navigator ? null : '<FlatList> - MultiColumn'}
noSpacer={true}
noScroll={true}>
<View style={styles.container}>
<View style={styles.searchRow}>
<View style={styles.row}>
<PlainInput
@@ -68,7 +54,7 @@ class MultiColumnExample extends React.PureComponent<$FlowFixMeProps, $FlowFixMe
placeholder="Search..."
value={this.state.filterText}
/>
<Text> numColumns: </Text>
<Text> numColumns: </Text>
<PlainInput
clearButtonMode="never"
onChangeText={this._onChangeNumColumns}
@@ -83,46 +69,45 @@ class MultiColumnExample extends React.PureComponent<$FlowFixMeProps, $FlowFixMe
</View>
<SeparatorComponent />
<FlatList
data={filteredData}
disableVirtualization={!this.state.virtualized}
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
key={this.state.numColumns + (this.state.fixedHeight ? 'f' : 'v')}
ListFooterComponent={FooterComponent}
ListHeaderComponent={HeaderComponent}
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
data={filteredData}
key={this.state.numColumns + (this.state.fixedHeight ? 'f' : 'v')}
numColumns={this.state.numColumns || 1}
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
onRefresh={() => console.log('onRefresh: nothing to refresh :P')}
onViewableItemsChanged={this._onViewableItemsChanged}
refreshing={false}
renderItem={this._renderItemComponent}
disableVirtualization={!this.state.virtualized}
onViewableItemsChanged={this._onViewableItemsChanged}
legacyImplementation={false}
/>
</RNTesterPage>
</View>
);
}
_getItemLayout(data: any, index: number): {length: number, offset: number, index: number} {
_getItemLayout(data: any, index: number): { length: number, offset: number, index: number } {
const length = getItemLayout(data, index).length + 2 * (CARD_MARGIN + BORDER_WIDTH);
return {length, offset: length * index, index};
return { length, offset: length * index, index };
}
_renderItemComponent = ({item}) => {
_renderItemComponent = ({ item }) => {
return (
<View style={styles.card}>
<ItemComponent
item={item}
fixedHeight={this.state.fixedHeight}
onPress={this._pressItem}
/>
<ItemComponent fixedHeight={this.state.fixedHeight} item={item} onPress={this._pressItem} />
</View>
);
};
// This is called when items change viewability by scrolling into or out of the viewable area.
_onViewableItemsChanged = (info: {
changed: Array<{
key: string, isViewable: boolean, item: {columns: Array<*>}, index: ?number, section?: any
}>},
) => {
key: string,
isViewable: boolean,
item: { columns: Array<*> },
index: ?number,
section?: any
}>
}) => {
// Impressions can be logged here
if (this.state.logViewable) {
infoLog('onViewableItemsChanged: ', info.changed.map((v) => ({...v, item: '...'})));
console.log('onViewableItemsChanged: ', info.changed.map(v => ({ ...v, item: '...' })));
}
};
_pressItem = (key: string) => {
@@ -134,21 +119,31 @@ const CARD_MARGIN = 4;
const BORDER_WIDTH = 1;
const styles = StyleSheet.create({
container: {
backgroundColor: 'rgb(239, 239, 244)',
flex: 1
},
card: {
margin: CARD_MARGIN,
borderRadius: 10,
flex: 1,
overflow: 'hidden',
borderColor: 'lightgray',
borderWidth: BORDER_WIDTH,
borderWidth: BORDER_WIDTH
},
row: {
flexDirection: 'row',
alignItems: 'center',
alignItems: 'center'
},
searchRow: {
padding: 10,
},
padding: 10
}
});
module.exports = MultiColumnExample;
export default function() {
return (
<View style={{ height: 300 }}>
<MultiColumnExample />
</View>
);
}

View File

@@ -0,0 +1,197 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
import React from 'react';
import { Animated, FlatList, StyleSheet, View } from 'react-native';
import {
FooterComponent,
HeaderComponent,
ItemComponent,
ItemSeparatorComponent,
PlainInput,
SeparatorComponent,
Spindicator,
genItemData,
getItemLayout,
pressItem,
renderSmallSwitchOption
} from './shared';
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const VIEWABILITY_CONFIG = {
minimumViewTime: 3000,
viewAreaCoveragePercentThreshold: 100,
waitForInteraction: true
};
class SingleColumnExample extends React.PureComponent {
static title = '<FlatList>';
static description = 'Performant, scrollable list of data.';
state = {
data: genItemData(100),
debug: false,
horizontal: false,
inverted: false,
filterText: '',
fixedHeight: true,
logViewable: false,
virtualized: true
};
_onChangeFilterText = filterText => {
this.setState({ filterText });
};
_onChangeScrollToIndex = text => {
this._listRef.getNode().scrollToIndex({ viewPosition: 0.5, index: Number(text) });
};
_scrollPos = new Animated.Value(0);
_scrollSinkX = Animated.event([{ nativeEvent: { contentOffset: { x: this._scrollPos } } }], {
useNativeDriver: true
});
_scrollSinkY = Animated.event([{ nativeEvent: { contentOffset: { y: this._scrollPos } } }], {
useNativeDriver: true
});
componentDidUpdate() {
this._listRef.getNode().recordInteraction(); // e.g. flipping logViewable switch
}
render() {
const filterRegex = new RegExp(String(this.state.filterText), 'i');
const filter = item => filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = this.state.data.filter(filter);
return (
<View style={styles.container}>
<View style={styles.searchRow}>
<View style={styles.options}>
<PlainInput
onChangeText={this._onChangeFilterText}
placeholder="Search..."
value={this.state.filterText}
/>
<PlainInput onChangeText={this._onChangeScrollToIndex} placeholder="scrollToIndex..." />
</View>
<View style={styles.options}>
{renderSmallSwitchOption(this, 'virtualized')}
{renderSmallSwitchOption(this, 'horizontal')}
{renderSmallSwitchOption(this, 'fixedHeight')}
{renderSmallSwitchOption(this, 'logViewable')}
{renderSmallSwitchOption(this, 'inverted')}
{renderSmallSwitchOption(this, 'debug')}
<Spindicator value={this._scrollPos} />
</View>
</View>
<SeparatorComponent />
<AnimatedFlatList
contentContainerStyle={styles.list}
data={filteredData}
debug={this.state.debug}
disableVirtualization={!this.state.virtualized}
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
horizontal={this.state.horizontal}
inverted={this.state.inverted}
ItemSeparatorComponent={ItemSeparatorComponent}
key={(this.state.horizontal ? 'h' : 'v') + (this.state.fixedHeight ? 'f' : 'd')}
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="always"
legacyImplementation={false}
ListFooterComponent={FooterComponent}
ListHeaderComponent={<HeaderComponent />}
numColumns={1}
onEndReached={this._onEndReached}
onRefresh={this._onRefresh}
onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY}
onViewableItemsChanged={this._onViewableItemsChanged}
ref={this._captureRef}
refreshing={false}
renderItem={this._renderItemComponent}
viewabilityConfig={VIEWABILITY_CONFIG}
/>
</View>
);
}
_captureRef = ref => {
this._listRef = ref;
};
_getItemLayout = (data: any, index: number) => {
return getItemLayout(data, index, this.state.horizontal);
};
_onEndReached = () => {
if (this.state.data.length >= 1000) {
return;
}
this.setState(state => ({
data: state.data.concat(genItemData(100, state.data.length))
}));
};
_onRefresh = () => console.log('onRefresh: nothing to refresh :P');
_renderItemComponent = ({ item, separators }) => {
return (
<ItemComponent
fixedHeight={this.state.fixedHeight}
horizontal={this.state.horizontal}
item={item}
onHideUnderlay={separators.unhighlight}
onPress={this._pressItem}
onShowUnderlay={separators.highlight}
/>
);
};
// This is called when items change viewability by scrolling into or out of
// the viewable area.
_onViewableItemsChanged = (info: {
changed: Array<{
key: string,
isViewable: boolean,
item: any,
index: ?number,
section?: any
}>
}) => {
// Impressions can be logged here
if (this.state.logViewable) {
console.log('onViewableItemsChanged: ', info.changed.map(v => ({ ...v, item: '...' })));
}
};
_pressItem = (key: string) => {
this._listRef.getNode().recordInteraction();
pressItem(this, key);
};
_listRef: AnimatedFlatList;
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'rgb(239, 239, 244)',
flex: 1
},
list: {
backgroundColor: 'white'
},
options: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center'
},
searchRow: {
paddingHorizontal: 10
}
});
export default function() {
return (
<View style={{ height: 300 }}>
<SingleColumnExample />
</View>
);
}

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -3,15 +3,11 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @providesModule ListExampleShared
*/
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {
import * as React from 'react';
import {
Animated,
Image,
Platform,
@@ -20,10 +16,10 @@ const {
Switch,
Text,
TextInput,
View,
} = ReactNative;
View
} from 'react-native';
type Item = {title: string, text: string, key: string, pressed: boolean, noImage?: ?boolean};
type Item = { title: string, text: string, key: string, pressed: boolean, noImage?: ?boolean };
function genItemData(count: number, start: number = 0): Array<Item> {
const dataBlob = [];
@@ -31,9 +27,9 @@ function genItemData(count: number, start: number = 0): Array<Item> {
const itemHash = Math.abs(hashCode('Item ' + ii));
dataBlob.push({
title: 'Item ' + ii,
text: LOREM_IPSUM.substr(0, itemHash % 301 + 20),
text: LOREM_IPSUM.substr(0, (itemHash % 301) + 20),
key: String(ii),
pressed: false,
pressed: false
});
}
return dataBlob;
@@ -48,30 +44,34 @@ class ItemComponent extends React.PureComponent<{
item: Item,
onPress: (key: string) => void,
onShowUnderlay?: () => void,
onHideUnderlay?: () => void,
onHideUnderlay?: () => void
}> {
_onPress = () => {
this.props.onPress(this.props.item.key);
};
render() {
const {fixedHeight, horizontal, item} = this.props;
const { fixedHeight, horizontal, item } = this.props;
const itemHash = Math.abs(hashCode(item.title));
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
return (
<TouchableHighlight
onHideUnderlay={this.props.onHideUnderlay}
onPress={this._onPress}
onShowUnderlay={this.props.onShowUnderlay}
onHideUnderlay={this.props.onHideUnderlay}
style={horizontal ? styles.horizItem : styles.item}
tvParallaxProperties={{
pressMagnification: 1.1,
pressMagnification: 1.1
}}
style={horizontal ? styles.horizItem : styles.item}>
<View style={[
styles.row, horizontal && {width: HORIZ_WIDTH}, fixedHeight && {height: ITEM_HEIGHT}]}>
{!item.noImage && <Image style={styles.thumb} source={imgSource} />}
<Text
style={styles.text}
numberOfLines={(horizontal || fixedHeight) ? 3 : undefined}>
>
<View
style={[
styles.row,
horizontal && { width: HORIZ_WIDTH },
fixedHeight && { height: ITEM_HEIGHT }
]}
>
{!item.noImage && <Image source={imgSource} style={styles.thumb} />}
<Text numberOfLines={horizontal || fixedHeight ? 3 : undefined} style={styles.text}>
{item.title} - {item.text}
</Text>
</View>
@@ -80,13 +80,15 @@ class ItemComponent extends React.PureComponent<{
}
}
const renderStackedItem = ({item}: {item: Item}) => {
const renderStackedItem = ({ item }: { item: Item }) => {
const itemHash = Math.abs(hashCode(item.title));
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
return (
<View style={styles.stacked}>
<Text style={styles.stackedText}>{item.title} - {item.text}</Text>
<Image style={styles.thumb} source={imgSource} />
<Text style={styles.stackedText}>
{item.title} - {item.text}
</Text>
<Image source={imgSource} style={styles.thumb} />
</View>
);
};
@@ -123,27 +125,34 @@ class SeparatorComponent extends React.PureComponent<{}> {
}
}
class ItemSeparatorComponent extends React.PureComponent<$FlowFixMeProps> {
class ItemSeparatorComponent extends React.PureComponent<{}> {
render() {
const style = this.props.highlighted
? [styles.itemSeparator, {marginLeft: 0, backgroundColor: 'rgb(217, 217, 217)'}]
? [styles.itemSeparator, { marginLeft: 0, backgroundColor: 'rgb(217, 217, 217)' }]
: styles.itemSeparator;
return <View style={style} />;
}
}
class Spindicator extends React.PureComponent<$FlowFixMeProps> {
class Spindicator extends React.PureComponent<{}> {
render() {
return (
<Animated.View style={[styles.spindicator, {
transform: [
{rotate: this.props.value.interpolate({
inputRange: [0, 5000],
outputRange: ['0deg', '360deg'],
extrapolate: 'extend',
})}
]
}]} />
<Animated.View
style={[
styles.spindicator,
{
transform: [
{
rotate: this.props.value.interpolate({
inputRange: [0, 5000],
outputRange: ['0deg', '360deg'],
extrapolate: 'extend'
})
}
]
}
]}
/>
);
}
}
@@ -160,44 +169,46 @@ const THUMB_URLS = [
require('./Thumbnails/party.png'),
require('./Thumbnails/poke.png'),
require('./Thumbnails/superlike.png'),
require('./Thumbnails/victory.png'),
require('./Thumbnails/victory.png')
];
const LOREM_IPSUM = 'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix \
civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id \
integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem \
vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud \
modus, putant invidunt reprehendunt ne qui.';
const LOREM_IPSUM =
'Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix ' +
'civibus corrumpit referrentur. Te nam case ludus inciderint, te mea facilisi adipiscing. Sea id ' +
'integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei. Eu paulo sapientem ' +
'vulputate est, vel an accusam intellegam interesset. Nam eu stet pericula reprimique, ea vim illud ' +
'modus, putant invidunt reprehendunt ne qui.';
/* eslint no-bitwise: 0 */
function hashCode(str: string): number {
let hash = 15;
for (let ii = str.length - 1; ii >= 0; ii--) {
hash = ((hash << 5) - hash) + str.charCodeAt(ii);
hash = (hash << 5) - hash + str.charCodeAt(ii);
}
return hash;
}
const HEADER = {height: 30, width: 100};
const HEADER = { height: 30, width: 100 };
const SEPARATOR_HEIGHT = StyleSheet.hairlineWidth;
function getItemLayout(data: any, index: number, horizontal?: boolean) {
const [length, separator, header] = horizontal ?
[HORIZ_WIDTH, 0, HEADER.width] : [ITEM_HEIGHT, SEPARATOR_HEIGHT, HEADER.height];
return {length, offset: (length + separator) * index + header, index};
const [length, separator, header] = horizontal
? [HORIZ_WIDTH, 0, HEADER.width]
: [ITEM_HEIGHT, SEPARATOR_HEIGHT, HEADER.height];
return { length, offset: (length + separator) * index + header, index };
}
function pressItem(context: Object, key: string) {
const index = Number(key);
const pressed = !context.state.data[index].pressed;
context.setState((state) => {
context.setState(state => {
const newData = [...state.data];
newData[index] = {
...state.data[index],
pressed,
title: 'Item ' + key + (pressed ? ' (pressed)' : ''),
title: 'Item ' + key + (pressed ? ' (pressed)' : '')
};
return {data: newData};
return { data: newData };
});
}
@@ -209,9 +220,9 @@ function renderSmallSwitchOption(context: Object, key: string) {
<View style={styles.option}>
<Text>{key}:</Text>
<Switch
onValueChange={value => context.setState({ [key]: value })}
style={styles.smallSwitch}
value={context.state[key]}
onValueChange={(value) => context.setState({[key]: value})}
/>
</View>
);
@@ -223,8 +234,8 @@ function PlainInput(props: Object) {
autoCapitalize="none"
autoCorrect={false}
clearButtonMode="always"
underlineColorAndroid="transparent"
style={styles.searchTextInput}
underlineColorAndroid="transparent"
{...props}
/>
);
@@ -235,31 +246,31 @@ const styles = StyleSheet.create({
...HEADER,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
justifyContent: 'center'
},
headerFooterContainer: {
backgroundColor: 'rgb(239, 239, 244)',
backgroundColor: 'rgb(239, 239, 244)'
},
horizItem: {
alignSelf: 'flex-start', // Necessary for touch highlight
alignSelf: 'flex-start' // Necessary for touch highlight
},
item: {
flex: 1,
flex: 1
},
itemSeparator: {
height: SEPARATOR_HEIGHT,
backgroundColor: 'rgb(200, 199, 204)',
marginLeft: 60,
marginLeft: 60
},
option: {
flexDirection: 'row',
padding: 8,
paddingRight: 0,
paddingRight: 0
},
row: {
flexDirection: 'row',
padding: 10,
backgroundColor: 'white',
backgroundColor: 'white'
},
searchTextInput: {
backgroundColor: 'white',
@@ -270,51 +281,51 @@ const styles = StyleSheet.create({
paddingVertical: 0,
height: 26,
fontSize: 14,
flexGrow: 1,
flexGrow: 1
},
separator: {
height: SEPARATOR_HEIGHT,
backgroundColor: 'rgb(200, 199, 204)',
backgroundColor: 'rgb(200, 199, 204)'
},
smallSwitch: Platform.select({
android: {
top: 1,
margin: -6,
transform: [{scale: 0.7}],
transform: [{ scale: 0.7 }]
},
ios: {
top: 4,
margin: -10,
transform: [{scale: 0.5}],
},
transform: [{ scale: 0.5 }]
}
}),
stacked: {
alignItems: 'center',
backgroundColor: 'white',
padding: 10,
padding: 10
},
thumb: {
width: 50,
height: 50,
left: -5,
left: -5
},
spindicator: {
marginLeft: 'auto',
marginTop: 8,
width: 2,
height: 16,
backgroundColor: 'darkgray',
backgroundColor: 'darkgray'
},
stackedText: {
padding: 4,
fontSize: 18,
fontSize: 18
},
text: {
flex: 1,
},
flex: 1
}
});
module.exports = {
export {
FooterComponent,
HeaderComponent,
ItemComponent,
@@ -326,5 +337,5 @@ module.exports = {
getItemLayout,
pressItem,
renderSmallSwitchOption,
renderStackedItem,
renderStackedItem
};

View File

@@ -0,0 +1,48 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ViewPropTypes': PropTypes.any,
blurRadius: PropTypes.number,
defaultSource: PropTypes.object, // ImageSourcePropType,
draggable: PropTypes.bool,
onError: PropTypes.func,
onLoad: PropTypes.func,
onLoadEnd: PropTypes.func,
onLoadStart: PropTypes.func,
resizeMode: PropTypes.oneOf(['contain', 'cover']),
source: PropTypes.object, ///ImageSourcePropType,
style: PropTypes.any
};
ofProps.defaultProps = {};
const ofStatics = () => {};
// hack to display instance methods
ofStatics.propTypes = {
getSize: PropTypes.func,
prefetch: PropTypes.func,
queryCache: PropTypes.func
};
export default {
title: 'Components|Image',
includeStories: []
};
export { ofProps, ofStatics };
export { default as defaultSource } from './examples/DefaultSource';
export { default as draggable } from './examples/Draggable';
export { default as onError } from './examples/OnError';
export { default as onLoad } from './examples/OnLoad';
export { default as onLoadEnd } from './examples/OnLoadEnd';
export { default as onLoadStart } from './examples/OnLoadStart';
export { default as resizeMode } from './examples/ResizeMode';
export { default as source } from './examples/Source';
export { default as styleBoxShadow } from './examples/StyleBoxShadow';
export { default as styleTintColor } from './examples/StyleTintColor';
export { default as getSize } from './examples/StaticGetSize';
export { default as prefetch } from './examples/StaticPrefetch';

View File

@@ -0,0 +1,173 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './Image.stories.js';
<Meta title="Components|Image" />
# Image
An accessible and responsive image component.
## Props
<Props of={stories.ofProps} />
### blurRadius
The blur radius of the blur filter added to the image.
### defaultSource
The source `uri` is a string representing a placeholder to display while the main image is being loaded.
```js
type Source = { uri: string, width: number, height: number }
```
<Preview withSource='none'>
<Story name="defaultSource">{stories.defaultSource}</Story>
</Preview>
### draggable
This is a web-only prop that indicates whether the image can be dragged with
native browser behavior. The default is `false`.
<Preview withSource='none'>
<Story name="draggable">{stories.draggable}</Story>
</Preview>
### onError
Called on load error with ``{nativeEvent: {error}}``.
<Preview withSource='none'>
<Story name="onError">{stories.onError}</Story>
</Preview>
### onLoad
Called when load completes successfully.
<Preview withSource='none'>
<Story name="onLoad">{stories.onLoad}</Story>
</Preview>
### resizeMode
Determines how to resize the image when the frame doesn't match the raw image dimensions.
```js
type ResizeMode = 'center' | 'contain' | 'cover' | 'none' | 'repeat' | 'stretch';
```
<Preview withSource='none'>
<Story name="resizeMode">{stories.resizeMode}</Story>
</Preview>
### source
The source `uri` is a string representing the resource identifier for the image, which could be an http address or a base64 encoded image.
```js
type Source = { uri: string, width: number, height: number }
```
<Preview withSource='none'>
<Story name="source">{stories.source}</Story>
</Preview>
### style
```js
{
...ViewProps.style
resizeMode: ResizeMode
tintColor: Color
}
```
#### shadows
Image shadows are derived exactly from the pixels.
<Preview withSource='none'>
<Story name="styleBoxShadow">{stories.styleBoxShadow}</Story>
</Preview>
#### tintColor
Tint color is applied to all opaque pixels.
<Preview withSource='none'>
<Story name="styleTintColor">{stories.styleTintColor}</Story>
</Preview>
## Static methods
<Props of={stories.ofStatics} />
### getSize(url, callback)
```js
type GetSize = (url: string, success: (width, height) => {}, failure: function) => void
```
Retrieve the width and height (in pixels) of an image prior to displaying it.
This method can fail if the image cannot be found, or fails to download.
(In order to retrieve the image dimensions, the image may first need to be
loaded or downloaded, after which it will be cached. This means that in
principle you could use this method to preload images, however, it is not
optimized for that purpose, and may in future be implemented in a way that does
not fully load/download the image data.)
```js
const uri = 'https://www.domain.com/img-1.jpg';
Image.getSize(uri, ((width, height) => {
// image size is available
});
```
<Preview withSource='none'>
<Story name="getSize">{stories.getSize}</Story>
</Preview>
### prefetch(url)
```js
type Prefetch = (url: string) => Promise
```
Prefetches a remote image for later use by downloading it. Once an image has been
prefetched it is assumed to be in native browser caches and available for immediate
rendering.
```js
const uri = 'https://www.domain.com/img-1.jpg';
Image.prefetch(uri).then(() => {
// image is now prefetched
});
```
<Preview withSource='none'>
<Story name="prefetch">{stories.prefetch}</Story>
</Preview>
### queryCache(list)
Performs cache interrogation. Returns a mapping from URL to cache status:
"disk", "memory", "disk/memory". If a requested URL is not in the mapping, it
means it's not in the cache.
```js
Image.queryCache([
'https://www.domain.com/img-1.jpg',
'https://www.domain.com/img-2.missing',
'https://www.domain.com/img-3.jpg'
]).then(res => {
expect(res).toEqual({
'https://www.domain.com/img-1.jpg': 'disk/memory',
'https://www.domain.com/img-3.jpg': 'disk/memory'
});
});
```

View File

@@ -0,0 +1,8 @@
import * as helpers from '../helpers';
import sources from '../sources';
import React from 'react';
import { Image } from 'react-native';
export default function Source() {
return <Image defaultSource={sources.placeholder} style={helpers.styles.base} />;
}

View File

@@ -1,16 +1,14 @@
/**
* @flow
*/
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, View } from 'react-native';
const ImageDraggableExample = () => (
<View style={styles.container}>
<Image draggable={true} source={sources.large} style={styles.image} />
</View>
);
export default function Draggable() {
return (
<View style={styles.container}>
<Image draggable={true} source={sources.large} style={styles.image} />
</View>
);
}
const styles = StyleSheet.create({
container: {
@@ -23,5 +21,3 @@ const styles = StyleSheet.create({
marginRight: 10
}
});
export default ImageDraggableExample;

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
/*
import React, { PureComponent } from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import * as helpers from '../helpers';
import { oneOf } from 'prop-types';
import sources from '../sources';

View File

@@ -0,0 +1,7 @@
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
export default function OnError() {
return <NetworkImage logMethod="onError" source={sources.broken} />;
}

View File

@@ -0,0 +1,8 @@
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
export default function OnLoad() {
return <NetworkImage logMethod="onLoad" source={createUncachedURI(sources.largeAlt)} />;
}

View File

@@ -0,0 +1,8 @@
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
export default function OnLoadEnd() {
return <NetworkImage logMethod="onLoadEnd" source={createUncachedURI(sources.largeAlt)} />;
}

View File

@@ -0,0 +1,8 @@
import { createUncachedURI } from '../helpers';
import NetworkImage from './NetworkImage';
import React from 'react';
import sources from '../sources';
export default function OnLoadStart() {
return <NetworkImage logMethod="onLoadStart" source={createUncachedURI(sources.largeAlt)} />;
}

View File

@@ -0,0 +1,58 @@
import React from 'react';
import sources from '../sources';
import { Image, StyleSheet, Text, View } from 'react-native';
export default function ResizeMode() {
return (
<View>
{[sources.small].map((source, i) => {
return (
<View key={i}>
<View style={styles.horizontal}>
<View>
<Text style={[styles.resizeModeText]}>Center</Text>
<Image resizeMode="center" source={source} style={styles.resizeMode} />
</View>
<View>
<Text style={[styles.resizeModeText]}>Contain</Text>
<Image resizeMode="contain" source={source} style={styles.resizeMode} />
</View>
<View>
<Text style={[styles.resizeModeText]}>Cover</Text>
<Image resizeMode="cover" source={source} style={styles.resizeMode} />
</View>
<View>
<Text style={[styles.resizeModeText]}>Repeat</Text>
<Image resizeMode="repeat" source={source} style={styles.resizeMode} />
</View>
<View>
<Text style={[styles.resizeModeText]}>Stretch</Text>
<Image resizeMode="stretch" source={source} style={styles.resizeMode} />
</View>
</View>
</View>
);
})}
</View>
);
}
const styles = StyleSheet.create({
horizontal: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
resizeMode: {
borderColor: 'black',
borderWidth: 0.5,
height: 120,
width: 120
},
resizeModeText: {
marginBottom: '0.5rem'
},
leftMargin: {
marginLeft: 10
}
});

View File

@@ -0,0 +1,60 @@
import sources from '../sources';
import React from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
export default function Source() {
return (
<React.Fragment>
<View style={styles.row}>
<View style={styles.column}>
<Text style={styles.text}>Static image</Text>
<Image source={sources.static} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Progressive JPEG</Text>
<Image source={sources.pjpeg} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>Animated GIF</Text>
<Image source={sources.animatedGif} style={styles.image} />
</View>
</View>
<View style={styles.row}>
<View style={styles.column}>
<Text style={styles.text}>PNG (base64)</Text>
<Image source={sources.dataBase64Png} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>SVG (base64)</Text>
<Image source={sources.dataBase64Svg} style={styles.image} />
</View>
<View style={styles.column}>
<Text style={styles.text}>SVG (inline data)</Text>
<Image source={sources.dataSvg} style={styles.image} />
</View>
</View>
</React.Fragment>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between'
},
column: {
alignItems: 'flex-start',
marginBottom: '1rem'
},
text: {
marginBottom: '0.5rem'
},
image: {
borderColor: 'black',
borderWidth: 0.5,
height: 120,
width: 120,
resizeMode: 'cover'
}
});

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import sources from '../sources';
import React, { PureComponent } from 'react';

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import { createUncachedURI } from '../helpers';
import sources from '../sources';
import React, { PureComponent } from 'react';

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
import React from 'react';
import { Image, StyleSheet } from 'react-native';
import source from '../sources/hawk.png';
export default function StyleBoxShadow() {
return <Image source={source} style={styles.box} />;
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
overflow: 'visible',
shadowOpacity: 0.5,
shadowRadius: 3,
shadowOffset: { width: 2, height: 2 }
}
});

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
import React from 'react';
import { Image, StyleSheet } from 'react-native';
import source from '../sources/hawk.png';
export default function StyleBoxShadow() {
return <Image source={source} style={styles.box} />;
}
const styles = StyleSheet.create({
box: {
width: 100,
height: 100,
borderWidth: 2,
tintColor: 'red'
}
});

View File

@@ -1,8 +1,3 @@
/**
* @flow
*/
// import React from 'react';
import { StyleSheet } from 'react-native';
const createUncachedURI = source => {

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -1,4 +1,5 @@
import placeholder from './placeholder.jpg';
import smallflower from './smallflower.jpg';
import staticImage from './ladybug.jpg';
const dataBase64Png =
@@ -14,10 +15,7 @@ const sources = {
'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'
},
broken: { uri: 'http://TYPO_ERROR.github.io/image.png' },
small: {
uri:
'https://images.unsplash.com/photo-1488584585634-35fc98ccb808?dpr=1&auto=format&fit=crop&w=100&h=66&q=60&cs=tinysrgb'
},
small: smallflower,
smallAlt: {
uri:
'https://images.unsplash.com/photo-1481595357459-84468f6eeaac?dpr=1&auto=format&fit=crop&w=100&h=66&q=60&cs=tinysrgb'

View File

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,19 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ImagePropTypes': PropTypes.any,
children: PropTypes.number,
imageStyle: PropTypes.any
};
ofProps.defaultProps = {};
export default {
title: 'Components|ImageBackground',
includeStories: []
};
export { ofProps };
export { default as children } from './examples/Children';

View File

@@ -0,0 +1,20 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './ImageBackground.stories.js';
<Meta title="Components|ImageBackground" />
# ImageBackground
A image component with support for child content.
## Props
<Props of={stories.ofProps} />
### children
Content to display over the image.
<Preview withSource='none'>
<Story name="children">{stories.children}</Story>
</Preview>

View File

@@ -1,16 +1,14 @@
/**
* @flow
*/
import sources from '../../Image/sources';
import React from 'react';
import { ImageBackground, StyleSheet, Text } from 'react-native';
const ImageChildrenExample = () => (
<ImageBackground source={sources.large} style={styles.image}>
<Text style={styles.text}>Child content</Text>
</ImageBackground>
);
export default function Children() {
return (
<ImageBackground source={sources.large} style={styles.image}>
<Text style={styles.text}>Child content</Text>
</ImageBackground>
);
}
const styles = StyleSheet.create({
image: {
@@ -29,5 +27,3 @@ const styles = StyleSheet.create({
top: 50
}
});
export default ImageChildrenExample;

View File

@@ -0,0 +1,42 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ViewPropTypes': PropTypes.any,
children: PropTypes.any,
enabled: PropTypes.bool,
onValueChange: PropTypes.func,
selectedValue: PropTypes.string,
style: PropTypes.any
};
ofProps.defaultProps = {};
const ofPropsItem = () => {};
ofPropsItem.propTypes = {
color: PropTypes.any,
label: PropTypes.string,
testID: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
export default {
title: 'Components|ImageBackground',
includeStories: []
};
export { ofProps, ofPropsItem };
/*
<PickerExample enabled={false} />
<PickerExample
onValueChange={(itemValue, itemPosition) => {
window.alert(`itemValue: ${itemValue}, itemPosition: ${itemPosition}`);
}}
/>
<PickerExample selectedValue="book-3" />
*/

View File

@@ -0,0 +1,71 @@
import { Meta, Props, Story, Preview } from '@storybook/addon-docs/blocks';
import * as stories from './Picker.stories.js';
<Meta title="Components|Picker" />
# Picker
Renders the native `<select>` component.
## Props
<Props of={stories.ofProps} />
### children
The items to display in the picker must be of type `Picker.Item`.
```js
<Picker>
<Picker.Item label="Goblet of Fire" />
<Picker.Item label="Order of the Phoenix" />
</Picker>
```
### enabled
If set to `false`, the picker will be disabled, i.e., the user will not be able
to make a selection.
### onValueChange
Callback for when an item is selected. This is called with the value and index
prop of the item that was selected: `(itemValue, itemIndex) => void`.
### selectedValue
Select the item with the matching value.
### style
```js
{
...ViewProps.style,
color: color
}
```
# Picker.Item
Individual selectable item in a `Picker`.
## Props
<Props of={stories.ofPropsItem} />
### color
Color of the item label. (Limited by browser support.)
### label
Text to display for this item.
### testID
Used to locate this view in end-to-end tests.
### value
The value to be passed to the picker's `onValueChange` callback when this item
is selected.

View File

@@ -1,7 +1,3 @@
/**
* @flow
*/
import React from 'react';
import { Picker, StyleSheet, View } from 'react-native';

View File

@@ -0,0 +1,32 @@
import PropTypes from 'prop-types';
const ofProps = () => {};
ofProps.propTypes = {
'...ViewPropTypes': PropTypes.any,
color: PropTypes.any,
indeterminate: PropTypes.bool,
progress: PropTypes.number,
trackColor: PropTypes.any
};
ofProps.defaultProps = {
color: '#1976D2',
indeterminate: false,
progress: 0,
trackColor: 'transparent'
};
export default {
title: 'Components|ProgressBar',
includeStories: []
};
export { ofProps };
export { default as color } from './examples/Color';
export { default as indeterminate } from './examples/Indeterminate';
export { default as progress } from './examples/Progress';
export { default as trackColor } from './examples/TrackColor';
export { default as customSize } from './examples/CustomSize';

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