Rewrite interactive documentation
Consolidate all docs within the latest storybook Ref #1172
@@ -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,
|
||||
|
||||
7
.github/CONTRIBUTING.md
vendored
@@ -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
|
||||
|
||||
10
package.json
@@ -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": {
|
||||
|
||||
20
packages/docs/.storybook/_webpack.config.js
Normal 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;
|
||||
};
|
||||
1
packages/docs/.storybook/addons.js
Normal file
@@ -0,0 +1 @@
|
||||
// import '@storybook/addon-options/register';
|
||||
48
packages/docs/.storybook/config.js
Normal 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);
|
||||
@@ -3,9 +3,7 @@ import { StyleSheet, View } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
minHeight: '100vh',
|
||||
maxWidth: 680,
|
||||
marginHorizontal: 'auto'
|
||||
maxWidth: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
10
packages/docs/.storybook/presets.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = [
|
||||
{
|
||||
name: '@storybook/addon-docs/preset',
|
||||
options: {
|
||||
configureJSX: true,
|
||||
babelOptions: {},
|
||||
sourceLoaderOptions: null
|
||||
}
|
||||
}
|
||||
];
|
||||
23
packages/docs/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
107
packages/docs/src/apis/AppRegistry/AppRegistry.stories.mdx
Normal 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`.
|
||||
6
packages/docs/src/apis/AppState/AppState.stories.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
title: 'APIs|AppState',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as stateChanges } from './examples/StateChanges';
|
||||
44
packages/docs/src/apis/AppState/AppState.stories.mdx
Normal 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>
|
||||
32
packages/docs/src/apis/AppState/examples/StateChanges.js
Normal 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>
|
||||
);
|
||||
}
|
||||
6
packages/docs/src/apis/Clipboard/Clipboard.stories.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
title: 'APIs|Clipboard',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as setString } from './examples/SetString';
|
||||
39
packages/docs/src/apis/Clipboard/Clipboard.stories.mdx
Normal 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.
|
||||
35
packages/docs/src/apis/Clipboard/examples/SetString.js
Normal 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
|
||||
}
|
||||
});
|
||||
6
packages/docs/src/apis/Dimensions/Dimensions.stories.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
title: 'APIs|Dimensions',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as stateChanges } from './examples/StateChanges';
|
||||
50
packages/docs/src/apis/Dimensions/Dimensions.stories.mdx
Normal 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>
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
title: 'APIs|I18nManager',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as layoutRTL } from './examples/LayoutRTL';
|
||||
48
packages/docs/src/apis/I18nManager/I18nManager.stories.mdx
Normal 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>
|
||||
67
packages/docs/src/apis/I18nManager/examples/Block.js
Normal 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
|
||||
}
|
||||
});
|
||||
627
packages/docs/src/apis/I18nManager/examples/LayoutRTL.js
Normal 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'
|
||||
}
|
||||
});
|
||||
47
packages/docs/src/apis/I18nManager/examples/Page.js
Normal 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
|
||||
}
|
||||
});
|
||||
29
packages/docs/src/apis/I18nManager/examples/Title.js
Normal 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'
|
||||
}
|
||||
});
|
||||
6
packages/docs/src/apis/Linking/Linking.stories.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
title: 'APIs|Linking',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as openURL } from './examples/OpenURL';
|
||||
30
packages/docs/src/apis/Linking/Linking.stories.mdx
Normal 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>
|
||||
@@ -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,
|
||||
37
packages/docs/src/apis/PixelRatio/PixelRatio.stories.mdx
Normal 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.
|
||||
49
packages/docs/src/apis/Platform/Platform.stories.mdx
Normal 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'
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
83
packages/docs/src/apis/StyleSheet/StyleSheet.stories.mdx
Normal 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.
|
||||
33
packages/docs/src/apis/Vibration/Vibration.stories.mdx
Normal 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]);
|
||||
```
|
||||
@@ -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';
|
||||
@@ -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>
|
||||
@@ -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'
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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 }]
|
||||
}
|
||||
});
|
||||
27
packages/docs/src/components/Button/Button.stories.js
Normal 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';
|
||||
45
packages/docs/src/components/Button/Button.stories.mdx
Normal 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" />
|
||||
```
|
||||
19
packages/docs/src/components/Button/examples/Color.js
Normal 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>
|
||||
);
|
||||
}
|
||||
10
packages/docs/src/components/Button/examples/Disabled.js
Normal 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" />;
|
||||
}
|
||||
25
packages/docs/src/components/Button/examples/OnPress.js
Normal 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'
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
29
packages/docs/src/components/CheckBox/CheckBox.stories.js
Normal 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';
|
||||
56
packages/docs/src/components/CheckBox/CheckBox.stories.mdx
Normal 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>
|
||||
16
packages/docs/src/components/CheckBox/examples/Color.js
Normal 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>
|
||||
);
|
||||
}
|
||||
16
packages/docs/src/components/CheckBox/examples/CustomSize.js
Normal 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>
|
||||
);
|
||||
}
|
||||
16
packages/docs/src/components/CheckBox/examples/Disabled.js
Executable 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>
|
||||
);
|
||||
}
|
||||
@@ -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 />;
|
||||
}
|
||||
16
packages/docs/src/components/CheckBox/examples/Value.js
Executable 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>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export default {
|
||||
title: 'Components|FlatList',
|
||||
includeStories: []
|
||||
};
|
||||
|
||||
export { default as singleColumn } from './examples/SingleColumn';
|
||||
export { default as multiColumn } from './examples/MultiColumn';
|
||||
26
packages/docs/src/components/FlatList/FlatList.stories.mdx
Normal 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>
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
197
packages/docs/src/components/FlatList/examples/SingleColumn.js
Normal 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>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -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
|
||||
};
|
||||
48
packages/docs/src/components/Image/Image.stories.js
Normal 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';
|
||||
173
packages/docs/src/components/Image/Image.stories.mdx
Normal 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'
|
||||
});
|
||||
});
|
||||
```
|
||||
@@ -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} />;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
/*
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Image, StyleSheet, Text, View } from 'react-native';
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as helpers from '../helpers';
|
||||
import { oneOf } from 'prop-types';
|
||||
import sources from '../sources';
|
||||
7
packages/docs/src/components/Image/examples/OnError.js
Normal 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} />;
|
||||
}
|
||||
8
packages/docs/src/components/Image/examples/OnLoad.js
Normal 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)} />;
|
||||
}
|
||||
8
packages/docs/src/components/Image/examples/OnLoadEnd.js
Normal 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)} />;
|
||||
}
|
||||
@@ -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)} />;
|
||||
}
|
||||
58
packages/docs/src/components/Image/examples/ResizeMode.js
Normal 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
|
||||
}
|
||||
});
|
||||
60
packages/docs/src/components/Image/examples/Source.js
Normal 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'
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { createUncachedURI } from '../helpers';
|
||||
import sources from '../sources';
|
||||
import React, { PureComponent } from 'react';
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { createUncachedURI } from '../helpers';
|
||||
import sources from '../sources';
|
||||
import React, { PureComponent } from 'react';
|
||||
@@ -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 }
|
||||
}
|
||||
});
|
||||
@@ -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'
|
||||
}
|
||||
});
|
||||
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const createUncachedURI = source => {
|
||||
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
@@ -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'
|
||||
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
BIN
packages/docs/src/components/Image/sources/smallflower.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
@@ -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';
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
42
packages/docs/src/components/Picker/Picker.stories.js
Normal 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" />
|
||||
*/
|
||||
71
packages/docs/src/components/Picker/Picker.stories.mdx
Normal 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.
|
||||
@@ -1,7 +1,3 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Picker, StyleSheet, View } from 'react-native';
|
||||
|
||||
@@ -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';
|
||||