├── docs └── .gitkeep ├── .gitattributes ├── .babelrc ├── src ├── Icon │ ├── lib │ │ ├── react-native.js │ │ ├── react-native.osx.js │ │ ├── create-icon-set-from-fontello.js │ │ ├── create-icon-set-from-icomoon.js │ │ ├── tab-bar-item-ios.js │ │ ├── generate-icon-set-from-css.js │ │ ├── icon-button.js │ │ ├── toolbar-android.js │ │ └── create-icon-set.js │ ├── index.js │ ├── Weui.js │ └── Icon.js ├── Msg │ ├── index.js │ ├── variable.js │ └── Msg.js ├── Mask │ ├── index.js │ └── Mask.js ├── Badge │ ├── index.js │ └── Badge.js ├── Toast │ ├── index.js │ └── Toast.js ├── Progress │ ├── index.js │ ├── variable.js │ └── Progress.js ├── Toptips │ ├── index.js │ └── Toptips.js ├── LoadMore │ ├── index.js │ └── LoadMore.js ├── SearchBar │ ├── index.js │ └── SearchBar.js ├── Tab │ ├── TabBar.js │ ├── NavBar.js │ └── index.js ├── ActionSheet │ ├── index.js │ └── ActionSheet.js ├── Flex │ ├── index.js │ ├── FlexItem.js │ └── Flex.js ├── Popup │ ├── index.js │ ├── PopupHeader.js │ └── Popup.js ├── Picker │ ├── index.js │ ├── PickerSection.js │ └── Picker.js ├── Dialog │ ├── index.js │ ├── variable.js │ └── Dialog.js ├── Gallery │ ├── index.js │ ├── GalleryDelete.js │ └── Gallery.js ├── Grid │ ├── variable.js │ ├── index.js │ ├── GridIcon.js │ ├── GridLabel.js │ ├── Grids.js │ └── Grid.js ├── Button │ ├── index.js │ ├── ButtonArea.js │ ├── ButtonPreview.js │ ├── variable.js │ ├── ButtonText.js │ └── Button.js ├── Form │ ├── variable.js │ ├── Switch.js │ ├── index.js │ ├── Input.js │ ├── Label.js │ ├── RadioCells.js │ ├── CheckboxCells.js │ ├── TextArea.js │ ├── Slider.js │ ├── Agreement.js │ ├── Select.js │ └── Uploader.js ├── Panel │ ├── index.js │ ├── PanelBody.js │ ├── Panel.js │ ├── PanelHeader.js │ └── PanelFooter.js ├── Article │ ├── index.js │ ├── Section.js │ ├── Article.js │ ├── H3.js │ ├── P.js │ ├── H1.js │ └── H2.js ├── Media │ ├── index.js │ ├── MediaBody.js │ ├── MediaTitle.js │ ├── MediaInfo.js │ ├── MediaDesc.js │ ├── MediaHeader.js │ ├── MediaInfoMeta.js │ └── Media.js ├── Cell │ ├── index.js │ ├── CellText.js │ ├── variable.js │ ├── CellsTitle.js │ ├── CellsTips.js │ ├── Cells.js │ ├── CellHeader.js │ ├── CellBody.js │ ├── CellFooter.js │ └── Cell.js ├── Preview │ ├── index.js │ ├── PreviewValue.js │ ├── PreviewLabel.js │ ├── PreviewFooter.js │ ├── Preview.js │ ├── PreviewBody.js │ ├── PreviewHeader.js │ └── PreviewItem.js ├── patch.js ├── Text │ └── index.js ├── StyleSheet │ └── index.js ├── variable.js └── index.js ├── fonts └── Weui.ttf ├── .npmignore ├── assets ├── back_arrow.png ├── back_arrow@2x.png └── back_arrow@3x.png ├── .travis.yml ├── index.ios.js ├── __tests__ ├── index.ios.js └── index.android.js ├── example ├── page │ ├── Tab.js │ ├── Msg.js │ ├── Progress.js │ ├── Icons.js │ ├── Toast.js │ ├── SearchBar.js │ ├── Button.js │ ├── Dialog.js │ ├── ActionSheet.js │ ├── Grid.js │ ├── Panel.js │ ├── Article.js │ └── Cell.js ├── components │ ├── StyleSheet.js │ └── Page.js ├── App.js └── routes.js ├── .gitignore ├── .eslintrc ├── index.android.js ├── package.json └── README.md /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /src/Icon/lib/react-native.js: -------------------------------------------------------------------------------- 1 | export * from 'react-native'; 2 | -------------------------------------------------------------------------------- /fonts/Weui.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maskzh/rn-weui/HEAD/fonts/Weui.ttf -------------------------------------------------------------------------------- /src/Icon/lib/react-native.osx.js: -------------------------------------------------------------------------------- 1 | export * from 'react-native-desktop'; 2 | -------------------------------------------------------------------------------- /src/Msg/index.js: -------------------------------------------------------------------------------- 1 | import Msg from './Msg' 2 | 3 | export { 4 | Msg 5 | } 6 | -------------------------------------------------------------------------------- /src/Icon/index.js: -------------------------------------------------------------------------------- 1 | import Icon from './Icon' 2 | 3 | export { 4 | Icon 5 | } 6 | -------------------------------------------------------------------------------- /src/Mask/index.js: -------------------------------------------------------------------------------- 1 | import Mask from './Mask' 2 | 3 | export { 4 | Mask, 5 | } 6 | -------------------------------------------------------------------------------- /src/Badge/index.js: -------------------------------------------------------------------------------- 1 | import Badge from './Badge' 2 | 3 | export { 4 | Badge 5 | } 6 | -------------------------------------------------------------------------------- /src/Toast/index.js: -------------------------------------------------------------------------------- 1 | import Toast from './Toast' 2 | 3 | export { 4 | Toast 5 | } 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | android/ 2 | docs/ 3 | example/ 4 | ios/ 5 | .*rc 6 | .*config 7 | .index.*.js 8 | -------------------------------------------------------------------------------- /assets/back_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maskzh/rn-weui/HEAD/assets/back_arrow.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | script: 5 | - npm run lint 6 | -------------------------------------------------------------------------------- /assets/back_arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maskzh/rn-weui/HEAD/assets/back_arrow@2x.png -------------------------------------------------------------------------------- /assets/back_arrow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maskzh/rn-weui/HEAD/assets/back_arrow@3x.png -------------------------------------------------------------------------------- /src/Progress/index.js: -------------------------------------------------------------------------------- 1 | import Progress from './Progress' 2 | 3 | export { 4 | Progress 5 | } 6 | -------------------------------------------------------------------------------- /src/Toptips/index.js: -------------------------------------------------------------------------------- 1 | import Toptips from './Toptips' 2 | 3 | export { 4 | Toptips, 5 | } 6 | -------------------------------------------------------------------------------- /src/LoadMore/index.js: -------------------------------------------------------------------------------- 1 | import LoadMore from './LoadMore' 2 | 3 | export { 4 | LoadMore, 5 | } 6 | -------------------------------------------------------------------------------- /src/SearchBar/index.js: -------------------------------------------------------------------------------- 1 | import SearchBar from './SearchBar' 2 | 3 | export { 4 | SearchBar 5 | } 6 | -------------------------------------------------------------------------------- /src/Tab/TabBar.js: -------------------------------------------------------------------------------- 1 | import TabBar from 'react-native-tab-navigator' 2 | 3 | export default TabBar 4 | -------------------------------------------------------------------------------- /src/Tab/NavBar.js: -------------------------------------------------------------------------------- 1 | import NavBar from 'react-native-scrollable-tab-view' 2 | 3 | export default NavBar 4 | -------------------------------------------------------------------------------- /src/ActionSheet/index.js: -------------------------------------------------------------------------------- 1 | import ActionSheet from './ActionSheet' 2 | 3 | export { 4 | ActionSheet 5 | } 6 | -------------------------------------------------------------------------------- /src/Flex/index.js: -------------------------------------------------------------------------------- 1 | import Flex from './Flex' 2 | import FlexItem from './FlexItem' 3 | 4 | export { 5 | Flex, 6 | FlexItem 7 | } 8 | -------------------------------------------------------------------------------- /src/Tab/index.js: -------------------------------------------------------------------------------- 1 | import TabBar from './TabBar' 2 | import NavBar from './NavBar' 3 | 4 | export { 5 | TabBar, 6 | NavBar 7 | } 8 | -------------------------------------------------------------------------------- /src/Popup/index.js: -------------------------------------------------------------------------------- 1 | import Popup from './Popup' 2 | import PopupHeader from './PopupHeader' 3 | 4 | export { 5 | Popup, 6 | PopupHeader 7 | } 8 | -------------------------------------------------------------------------------- /index.ios.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native' 2 | import App from './example/App' 3 | 4 | AppRegistry.registerComponent('example', () => App) 5 | -------------------------------------------------------------------------------- /src/Picker/index.js: -------------------------------------------------------------------------------- 1 | import Picker from './Picker' 2 | import PickerSection from './PickerSection' 3 | 4 | export { 5 | Picker, 6 | PickerSection 7 | } 8 | -------------------------------------------------------------------------------- /src/Dialog/index.js: -------------------------------------------------------------------------------- 1 | // import Alert from './Alert' 2 | // import Confirm from './Confirm' 3 | import Dialog from './Dialog' 4 | 5 | export { 6 | Dialog 7 | } 8 | -------------------------------------------------------------------------------- /src/Gallery/index.js: -------------------------------------------------------------------------------- 1 | import Gallery from './Gallery' 2 | import GalleryDelete from './GalleryDelete' 3 | 4 | export { 5 | Gallery, 6 | GalleryDelete, 7 | } 8 | -------------------------------------------------------------------------------- /src/Grid/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiGridBorderColor: '#D9D9D9', 3 | weuiGridFontSize: 14, 4 | weuiGridIconSize: 28, 5 | weuiGridColumnCount: 3, 6 | } 7 | -------------------------------------------------------------------------------- /src/Progress/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiProgressBg: '#EBEBEB', 3 | weuiProgressColor: '#09BB07', 4 | weuiProgressHeight: 3, 5 | weuiProgressCloseBg: '#EF4F4F', 6 | weuiProgressActiveBg: '#C13E3E', 7 | } 8 | -------------------------------------------------------------------------------- /src/Button/index.js: -------------------------------------------------------------------------------- 1 | import Button from './Button' 2 | import ButtonArea from './ButtonArea' 3 | import ButtonPreview from './ButtonPreview' 4 | 5 | export { 6 | Button, 7 | ButtonArea, 8 | ButtonPreview 9 | } 10 | -------------------------------------------------------------------------------- /src/Form/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiUploaderBorderColor: '#D9D9D9', 3 | weuiUploaderActiveBorderColor: '#999999', 4 | weuiUploaderFileSpacing: 9, 5 | weuiUploaderSize: 79, 6 | weuiUploaderBorderWidth: 1, 7 | } 8 | -------------------------------------------------------------------------------- /src/Dialog/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiDialogBackgroundColor: '#FFFFFF', 3 | weuiDialogLineColor: '#D5D5D6', 4 | weuiDialogLinkColor: '#3CC51F', 5 | weuiDialogLinkActiveBc: '#EEEEEE', 6 | weuiDialogGapWidth: 1.6 * 16 7 | } 8 | -------------------------------------------------------------------------------- /src/Grid/index.js: -------------------------------------------------------------------------------- 1 | import Grids from './Grids' 2 | import Grid from './Grid' 3 | import GridIcon from './GridIcon' 4 | import GridLabel from './GridLabel' 5 | 6 | export { 7 | Grids, 8 | Grid, 9 | GridIcon, 10 | GridLabel 11 | } 12 | -------------------------------------------------------------------------------- /src/Msg/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiMsgPaddingTop: 36, 3 | weuiMsgIconGap: 30, 4 | weuiMsgTitleGap: 5, 5 | weuiMsgTextGap: 25, 6 | weuiMsgOprGap: 25, 7 | weuiMsgExtraAreaGap: 15, 8 | weuiMsgExtraAreaOfMinHeight: 438, 9 | } 10 | -------------------------------------------------------------------------------- /src/Panel/index.js: -------------------------------------------------------------------------------- 1 | import Panel from './Panel' 2 | import PanelHeader from './PanelHeader' 3 | import PanelBody from './PanelBody' 4 | import PanelFooter from './PanelFooter' 5 | 6 | export { 7 | Panel, 8 | PanelHeader, 9 | PanelBody, 10 | PanelFooter, 11 | } 12 | -------------------------------------------------------------------------------- /src/Article/index.js: -------------------------------------------------------------------------------- 1 | import Article from './Article' 2 | import Section from './Section' 3 | import H1 from './H1' 4 | import H2 from './H2' 5 | import H3 from './H3' 6 | import P from './P' 7 | 8 | export { 9 | Article, 10 | Section, 11 | H1, 12 | H2, 13 | H3, 14 | P, 15 | } 16 | -------------------------------------------------------------------------------- /__tests__/index.ios.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.ios.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /__tests__/index.android.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.android.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /src/Flex/FlexItem.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | 3 | const FlexItem = ({ component, children, style, ...others }) => 4 | {children} 5 | 6 | FlexItem.propTypes = { 7 | component: PropTypes.node.isRequired, 8 | style: PropTypes.any, 9 | children: PropTypes.node, 10 | } 11 | 12 | export default FlexItem 13 | -------------------------------------------------------------------------------- /src/Media/index.js: -------------------------------------------------------------------------------- 1 | import Media from './Media' 2 | import MediaHeader from './MediaHeader' 3 | import MediaBody from './MediaBody' 4 | import MediaTitle from './MediaTitle' 5 | import MediaDescription from './MediaDesc' 6 | import MediaInfo from './MediaInfo' 7 | import MediaInfoMeta from './MediaInfoMeta' 8 | 9 | export { 10 | Media, 11 | MediaHeader, 12 | MediaBody, 13 | MediaTitle, 14 | MediaDescription, 15 | MediaInfo, 16 | MediaInfoMeta, 17 | } 18 | -------------------------------------------------------------------------------- /src/Article/Section.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | section: { 6 | marginBottom: 22.5 7 | } 8 | }) 9 | 10 | const Section = ({ style, children }) => 11 | {children} 12 | 13 | Section.propTypes = { 14 | style: View.propTypes.style, 15 | children: PropTypes.node 16 | } 17 | 18 | export default Section 19 | -------------------------------------------------------------------------------- /src/Cell/index.js: -------------------------------------------------------------------------------- 1 | import Cells from './Cells' 2 | import CellsTitle from './CellsTitle' 3 | import CellsTips from './CellsTips' 4 | import Cell from './Cell' 5 | import CellHeader from './CellHeader' 6 | import CellBody from './CellBody' 7 | import CellFooter from './CellFooter' 8 | import CellText from './CellText' 9 | 10 | export { 11 | Cells, 12 | CellsTitle, 13 | CellsTips, 14 | Cell, 15 | CellHeader, 16 | CellBody, 17 | CellFooter, 18 | CellText, 19 | } 20 | -------------------------------------------------------------------------------- /src/Preview/index.js: -------------------------------------------------------------------------------- 1 | import Preview from './Preview' 2 | import PreviewHeader from './PreviewHeader' 3 | import PreviewBody from './PreviewBody' 4 | import PreviewFooter from './PreviewFooter' 5 | import PreviewItem from './PreviewItem' 6 | import PreviewLabel from './PreviewLabel' 7 | import PreviewValue from './PreviewValue' 8 | 9 | export { 10 | Preview, 11 | PreviewHeader, 12 | PreviewBody, 13 | PreviewFooter, 14 | PreviewItem, 15 | PreviewLabel, 16 | PreviewValue, 17 | } 18 | -------------------------------------------------------------------------------- /src/Icon/lib/create-icon-set-from-fontello.js: -------------------------------------------------------------------------------- 1 | import createIconSet from './create-icon-set'; 2 | 3 | export default function createIconSetFromFontello(config, fontFamilyArg, fontFile) { 4 | let glyphMap = {}; 5 | config.glyphs.forEach(glyph => { 6 | glyphMap[glyph.css] = glyph.code; 7 | }); 8 | 9 | const fontFamily = fontFamilyArg || config.name || 'fontello'; 10 | 11 | return createIconSet( 12 | glyphMap, 13 | fontFamily, 14 | fontFile || fontFamily + '.ttf' 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/Media/MediaBody.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | mediaBody: { 6 | flex: 1 7 | } 8 | }) 9 | 10 | const MediaBody = ({ style, children, ...others }) => 11 | 12 | {children} 13 | 14 | 15 | MediaBody.propTypes = { 16 | style: View.propTypes.style, 17 | children: PropTypes.node, 18 | } 19 | 20 | export default MediaBody 21 | -------------------------------------------------------------------------------- /src/patch.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, StyleSheet } from 'react-native' 3 | import wrap from 'lodash/wrap' 4 | 5 | const styles = StyleSheet.create({ 6 | defaultFont: { 7 | fontFamily: '' 8 | } 9 | }) 10 | 11 | Text.prototype.render = wrap(Text.prototype.render, (func, ...args) => { 12 | const originText = func.apply(this, args) 13 | return React.cloneElement(originText, { 14 | style: [ 15 | originText.props.style, 16 | styles.defaultFont 17 | ] 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /src/Icon/lib/create-icon-set-from-icomoon.js: -------------------------------------------------------------------------------- 1 | import createIconSet from './create-icon-set'; 2 | 3 | export default function createIconSetFromIcoMoon(config, fontFamilyArg, fontFile) { 4 | let glyphMap = {}; 5 | config.icons.forEach(icon => { 6 | glyphMap[icon.properties.name] = icon.properties.code; 7 | }); 8 | 9 | const fontFamily = fontFamilyArg || config.preferences.fontPref.metadata.fontFamily; 10 | 11 | return createIconSet( 12 | glyphMap, 13 | fontFamily, 14 | fontFile || fontFamily + '.ttf' 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/Form/Switch.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Switch as RNSwitch } from 'react-native' 3 | 4 | const Switch = ({ value, onChange, disabled, style, ...others }) => 5 | 12 | 13 | Switch.propTypes = { 14 | value: PropTypes.bool, 15 | onChange: PropTypes.func, 16 | disabled: PropTypes.bool, 17 | style: RNSwitch.propTypes.style, 18 | } 19 | 20 | export default Switch 21 | -------------------------------------------------------------------------------- /src/Form/index.js: -------------------------------------------------------------------------------- 1 | import TextArea from './TextArea' 2 | import Input from './Input' 3 | import Switch from './Switch' 4 | import RadioCells from './RadioCells' 5 | import CheckboxCells from './CheckboxCells' 6 | import Uploader from './Uploader' 7 | import Label from './Label' 8 | import Select from './Select' 9 | import Slider from './Slider' 10 | import Agreement from './Agreement' 11 | 12 | export { 13 | TextArea, 14 | Input, 15 | Switch, 16 | RadioCells, 17 | CheckboxCells, 18 | Uploader, 19 | Label, 20 | Select, 21 | Slider, 22 | Agreement, 23 | } 24 | -------------------------------------------------------------------------------- /src/Flex/Flex.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View } from 'react-native' 3 | 4 | const Flex = ({ direction = 'row', wrap = 'wrap', style, children, ...others }) => 5 | {children} 12 | 13 | Flex.propTypes = { 14 | direction: PropTypes.oneOf(['row', 'column']), 15 | wrap: PropTypes.oneOf(['wrap', 'nowrap']), 16 | style: View.propTypes.style, 17 | children: PropTypes.node, 18 | } 19 | 20 | export default Flex 21 | -------------------------------------------------------------------------------- /src/Preview/PreviewValue.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, Text } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | formPreviewValue: { 6 | flex: 1, 7 | textAlign: 'right', 8 | fontStyle: 'italic', 9 | }, 10 | }) 11 | 12 | const PreviewValue = ({ style, children, ...others }) => 13 | {children} 14 | 15 | PreviewValue.propTypes = { 16 | style: Text.propTypes.style, 17 | children: PropTypes.node, 18 | } 19 | 20 | export default PreviewValue 21 | -------------------------------------------------------------------------------- /src/Article/Article.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { ScrollView, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | article: { 6 | paddingTop: 20, 7 | paddingRight: 15, 8 | paddingBottom: 20, 9 | paddingLeft: 15, 10 | }, 11 | }) 12 | 13 | const Article = ({ style, children }) => 14 | 15 | {children} 16 | 17 | 18 | Article.propTypes = { 19 | style: ScrollView.propTypes.style, 20 | children: PropTypes.node 21 | } 22 | 23 | export default Article 24 | -------------------------------------------------------------------------------- /src/Media/MediaTitle.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, Text } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | mediaTitle: { 6 | fontSize: 17, 7 | fontWeight: '400', 8 | marginBottom: 5, 9 | } 10 | }) 11 | 12 | const MediaTitle = ({ style, children, ...others }) => 13 | 14 | {children} 15 | 16 | 17 | MediaTitle.propTypes = { 18 | style: Text.propTypes.style, 19 | children: PropTypes.node, 20 | } 21 | 22 | export default MediaTitle 23 | -------------------------------------------------------------------------------- /src/Grid/GridIcon.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | gridIcon: { 7 | width: V.weuiGridIconSize, 8 | height: V.weuiGridIconSize, 9 | alignSelf: 'center' 10 | } 11 | }) 12 | 13 | const GridIcon = ({ children, style, ...others }) => 14 | {children} 15 | 16 | GridIcon.propTypes = { 17 | children: PropTypes.node, 18 | style: View.propTypes.style, 19 | } 20 | 21 | export default GridIcon 22 | -------------------------------------------------------------------------------- /src/Preview/PreviewLabel.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | formPreviewLabel: { 7 | textAlign: 'justify', 8 | color: V.weuiTextColorGray, 9 | }, 10 | }) 11 | 12 | const PreviewLabel = ({ style, children, ...others }) => 13 | {children} 14 | 15 | PreviewLabel.propTypes = { 16 | style: Text.propTypes.style, 17 | children: PropTypes.node, 18 | } 19 | 20 | export default PreviewLabel 21 | -------------------------------------------------------------------------------- /src/Media/MediaInfo.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | mediaInfo: { 6 | flexDirection: 'row', 7 | flexWrap: 'wrap', 8 | marginTop: 15, 9 | paddingBottom: 5, 10 | overflow: 'hidden', 11 | } 12 | }) 13 | 14 | const MediaInfo = ({ style, children, ...others }) => 15 | 16 | {children} 17 | 18 | 19 | MediaInfo.propTypes = { 20 | style: View.propTypes.style, 21 | children: PropTypes.node, 22 | } 23 | 24 | export default MediaInfo 25 | -------------------------------------------------------------------------------- /src/Grid/GridLabel.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | gridLabel: { 7 | textAlign: 'center', 8 | fontSize: V.weuiGridFontSize, 9 | color: V.weuiTextColorTitle, 10 | marginTop: 5, 11 | } 12 | }) 13 | 14 | const GridLabel = ({ children, style, ...others }) => 15 | {children} 16 | 17 | GridLabel.propTypes = { 18 | children: PropTypes.node, 19 | style: Text.propTypes.style, 20 | } 21 | 22 | export default GridLabel 23 | -------------------------------------------------------------------------------- /src/Panel/PanelBody.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View } from 'react-native' 3 | 4 | const PanelBody = ({ children, style, ...others }) => { 5 | const childrenWithProps = React.Children.map(children, (child, idx) => { 6 | if (idx === 0) { 7 | return React.cloneElement(child, { first: true }) 8 | } 9 | return child 10 | }) 11 | 12 | return ( 13 | 14 | {childrenWithProps} 15 | 16 | ) 17 | } 18 | 19 | PanelBody.propTypes = { 20 | children: PropTypes.node, 21 | style: View.propTypes.style, 22 | } 23 | 24 | export default PanelBody 25 | -------------------------------------------------------------------------------- /example/page/Tab.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text, ScrollView } from 'react-native' 3 | import { Tab } from '../../src' 4 | 5 | const TabScene = () => 6 | 7 | My 8 | favorite 9 | project 10 | favorite 11 | project 12 | project 13 | project 14 | project 15 | 16 | 17 | export default TabScene 18 | -------------------------------------------------------------------------------- /example/components/StyleSheet.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Platform } from 'react-native' 2 | 3 | export function create(styles) { 4 | const platformStyles = {} 5 | Object.keys(styles).forEach((name) => { 6 | const { ios, android } = { ...styles[name] } 7 | let { ...style } = { ...styles[name] } 8 | if (ios && Platform.OS === 'ios') { 9 | style = { ...style, ...ios } 10 | } 11 | if (android && Platform.OS === 'android') { 12 | style = { ...style, ...android } 13 | } 14 | platformStyles[name] = style 15 | }) 16 | return StyleSheet.create(platformStyles) 17 | } 18 | 19 | export default {...StyleSheet, create } 20 | -------------------------------------------------------------------------------- /src/Text/index.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, Text as RNText } from 'react-native' 3 | import V from '../variable' 4 | 5 | const Text = ({ style, children, ...others, }) => { 6 | const styleObj = StyleSheet.flatten(style) 7 | const fontSize = styleObj.fontSize || V.baseFontSize 8 | const lineHeight = styleObj.lineHeight || fontSize * V.baseLineHeight 9 | 10 | return ( 11 | {children} 12 | ) 13 | } 14 | 15 | Text.propTypes = { 16 | style: RNText.propTypes.style, 17 | children: PropTypes.node 18 | } 19 | 20 | export default Text 21 | -------------------------------------------------------------------------------- /src/Preview/PreviewFooter.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | previewFooter: { 7 | flexDirection: 'row', 8 | borderTopWidth: StyleSheet.hairlineWidth, 9 | borderColor: V.weuiDialogLineColor, 10 | }, 11 | }) 12 | 13 | const PreviewFooter = ({ style, children, ...other }) => 14 | {children} 15 | 16 | PreviewFooter.propTypes = { 17 | style: View.propTypes.style, 18 | children: PropTypes.node, 19 | } 20 | 21 | export default PreviewFooter 22 | -------------------------------------------------------------------------------- /src/Article/H3.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | h3: { 8 | fontSize: 15, 9 | fontWeight: '400', 10 | lineHeight: 15 * V.baseLineHeight, 11 | android: { 12 | lineHeight: Math.round(15 * V.baseLineHeight), 13 | }, 14 | } 15 | }) 16 | 17 | const H3 = ({ style, children }) => 18 | 19 | {children} 20 | 21 | 22 | H3.propTypes = { 23 | style: Text.propTypes.style, 24 | children: PropTypes.node 25 | } 26 | 27 | export default H3 28 | -------------------------------------------------------------------------------- /src/Article/P.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | p: { 8 | fontSize: 15, 9 | marginBottom: 15 * 0.8, 10 | lineHeight: 15 * V.baseLineHeight, 11 | android: { 12 | lineHeight: Math.round(15 * V.baseLineHeight), 13 | }, 14 | } 15 | }) 16 | 17 | const P = ({ style, children }) => 18 | 19 | {children} 20 | 21 | 22 | P.propTypes = { 23 | style: Text.propTypes.style, 24 | children: PropTypes.node 25 | } 26 | 27 | export default P 28 | -------------------------------------------------------------------------------- /src/Cell/CellText.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cellText: { 7 | fontSize: V.weuiCellFontSize, 8 | marginTop: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 9 | marginBottom: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 10 | } 11 | }) 12 | 13 | const CellText = ({ children, style, ...others }) => 14 | {children} 15 | 16 | CellText.propTypes = { 17 | children: PropTypes.node, 18 | style: Text.propTypes.style, 19 | } 20 | 21 | export default CellText 22 | -------------------------------------------------------------------------------- /src/Preview/Preview.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | preview: { 7 | backgroundColor: '#FFFFFF', 8 | borderTopWidth: StyleSheet.hairlineWidth, 9 | borderBottomWidth: StyleSheet.hairlineWidth, 10 | borderColor: V.weuiCellBorderColor 11 | }, 12 | }) 13 | 14 | const Preview = ({ style, children, ...other }) => 15 | {children} 16 | 17 | Preview.propTypes = { 18 | style: View.propTypes.style, 19 | children: PropTypes.node, 20 | } 21 | 22 | export default Preview 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | android/app/libs 42 | *.keystore 43 | 44 | # Custom 45 | /android 46 | /ios 47 | /lib 48 | .*config 49 | -------------------------------------------------------------------------------- /src/Gallery/GalleryDelete.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TouchableWithoutFeedback, View, StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | 5 | const styles = StyleSheet.create({ 6 | galleryDelete: { 7 | flex: 1, 8 | height: 60, 9 | alignItems: 'center', 10 | justifyContent: 'center' 11 | } 12 | }) 13 | 14 | const GalleryDelete = ({ style, ...others }) => 15 | 16 | 17 | 18 | 19 | GalleryDelete.propTypes = { 20 | style: View.propTypes.style 21 | } 22 | 23 | export default GalleryDelete 24 | -------------------------------------------------------------------------------- /src/Article/H1.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | h1: { 8 | fontSize: 18, 9 | fontWeight: '400', 10 | marginBottom: 18 * 0.9, 11 | lineHeight: 18 * V.baseLineHeight, 12 | android: { 13 | lineHeight: Math.round(18 * V.baseLineHeight), 14 | }, 15 | } 16 | }) 17 | 18 | const H1 = ({ style, children }) => 19 | 20 | {children} 21 | 22 | 23 | H1.propTypes = { 24 | style: Text.propTypes.style, 25 | children: PropTypes.node 26 | } 27 | 28 | export default H1 29 | -------------------------------------------------------------------------------- /src/Article/H2.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | h2: { 8 | fontSize: 16, 9 | fontWeight: '400', 10 | marginBottom: 16 * 0.34, 11 | lineHeight: 16 * V.baseLineHeight, 12 | android: { 13 | lineHeight: Math.round(16 * V.baseLineHeight), 14 | }, 15 | } 16 | }) 17 | 18 | const H2 = ({ style, children }) => 19 | 20 | {children} 21 | 22 | 23 | H2.propTypes = { 24 | style: Text.propTypes.style, 25 | children: PropTypes.node 26 | } 27 | 28 | export default H2 29 | -------------------------------------------------------------------------------- /src/Media/MediaDesc.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | mediaDesc: { 8 | fontSize: 13, 9 | color: V.weuiTextColorGray, 10 | lineHeight: 13 * 1.2, 11 | android: { 12 | lineHeight: Math.round(13 * 1.2), 13 | }, 14 | } 15 | }) 16 | 17 | const MediaDesc = ({ style, children, ...others }) => 18 | 19 | {children} 20 | 21 | 22 | MediaDesc.propTypes = { 23 | style: Text.propTypes.style, 24 | children: PropTypes.node, 25 | } 26 | 27 | export default MediaDesc 28 | -------------------------------------------------------------------------------- /src/Icon/Weui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Icon icon set component. 3 | * Usage: 4 | */ 5 | import createIconSet from './lib/create-icon-set' 6 | 7 | const glyphMap = { 8 | circle: 59905, 9 | download: 59906, 10 | info: 59907, 11 | safe_success: 59908, 12 | safe_warn: 59909, 13 | success: 59910, 14 | success_circle: 59911, 15 | success_no_circle: 59912, 16 | waiting: 59913, 17 | waiting_circle: 59914, 18 | warn: 59915, 19 | info_circle: 59916, 20 | cancel: 59917, 21 | search: 59918, 22 | clear: 59919, 23 | back: 59920, 24 | delete: 59921, 25 | } 26 | 27 | const Weui = createIconSet(glyphMap, 'Weui', 'Weui.ttf') 28 | 29 | module.exports = Weui 30 | module.exports.glyphMap = glyphMap 31 | -------------------------------------------------------------------------------- /src/Form/Input.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { TextInput, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | input: { 7 | fontSize: V.weuiCellFontSize, 8 | height: V.weuiCellLineHeight, 9 | }, 10 | }) 11 | 12 | const Input = ({ value, onChange, disabled = false, style, ...others }) => 13 | 20 | 21 | Input.propTypes = { 22 | value: PropTypes.string, 23 | onChange: PropTypes.func, 24 | disabled: PropTypes.bool, 25 | style: TextInput.propTypes.style, 26 | } 27 | 28 | export default Input 29 | -------------------------------------------------------------------------------- /example/page/Msg.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | StyleSheet, 4 | View, 5 | } from 'react-native' 6 | import { Msg } from '../../src' 7 | 8 | const styles = StyleSheet.create({ 9 | wrapper: { 10 | flex: 1, 11 | paddingTop: 64, 12 | backgroundColor: '#fbf9fe' 13 | } 14 | }) 15 | 16 | const MsgScene = () => 17 | 18 | {} 26 | }, { 27 | type: 'default', 28 | label: '取消', 29 | onPress: () => {} 30 | }]} 31 | /> 32 | 33 | 34 | export default MsgScene 35 | -------------------------------------------------------------------------------- /src/Form/Label.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | label: { 7 | width: V.weuiCellLabelWidth, 8 | fontSize: V.weuiCellFontSize, 9 | marginTop: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 10 | marginBottom: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 11 | }, 12 | }) 13 | 14 | const Label = ({ style, children, ...others }) => 15 | 20 | {children} 21 | 22 | 23 | Label.propTypes = { 24 | style: Text.propTypes.style, 25 | children: PropTypes.node, 26 | } 27 | 28 | export default Label 29 | -------------------------------------------------------------------------------- /src/Panel/Panel.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | panel: { 7 | backgroundColor: '#fff', 8 | marginTop: 10, 9 | overflow: 'hidden', 10 | borderTopWidth: StyleSheet.hairlineWidth, 11 | borderBottomWidth: StyleSheet.hairlineWidth, 12 | borderStyle: 'solid', 13 | borderColor: V.weuiLineColorLight, 14 | } 15 | }) 16 | 17 | const Panel = ({ children, style, ...others }) => 18 | 19 | {children} 20 | 21 | 22 | Panel.propTypes = { 23 | children: PropTypes.node, 24 | style: View.propTypes.style, 25 | } 26 | 27 | export default Panel 28 | -------------------------------------------------------------------------------- /src/StyleSheet/index.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Platform } from 'react-native' 2 | 3 | export function create(styles) { 4 | const platformStyles = {} 5 | Object.keys(styles).forEach((name) => { 6 | const { ios, android } = { ...styles[name] } 7 | /* eslint-disable no-param-reassign */ 8 | delete styles[name].ios 9 | delete styles[name].android 10 | /* eslint-enable no-param-reassign */ 11 | let { ...style } = { ...styles[name] } 12 | if (ios && Platform.OS === 'ios') { 13 | style = { ...style, ...ios } 14 | } 15 | if (android && Platform.OS === 'android') { 16 | style = { ...style, ...android } 17 | } 18 | platformStyles[name] = style 19 | }) 20 | return StyleSheet.create(platformStyles) 21 | } 22 | 23 | export default { create } 24 | -------------------------------------------------------------------------------- /example/components/Page.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | page: { 6 | paddingTop: 64, 7 | backgroundColor: '#fbf9fe' 8 | }, 9 | spacing: { 10 | paddingLeft: 15, 11 | paddingRight: 15, 12 | } 13 | }) 14 | 15 | const Page = (props) => { 16 | const { 17 | spacing, 18 | style, 19 | children 20 | } = props 21 | 22 | return ( 23 | {children} 24 | ) 25 | } 26 | 27 | Page.propTypes = { 28 | spacing: PropTypes.bool, 29 | style: PropTypes.oneOfType([PropTypes.array, PropTypes.object, PropTypes.number]), 30 | children: PropTypes.node, 31 | } 32 | 33 | export default Page 34 | -------------------------------------------------------------------------------- /src/Cell/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiCellBg: '#FFFFFF', 3 | weuiCellBorderColor: '#D9D9D9', 4 | weuiCellGapV: 10, 5 | weuiCellGapH: 15, 6 | weuiCellInnerGapH: 17 * 0.35, // '.35em' 7 | weuiCellHeight: 44, 8 | weuiCellFontSize: 17, 9 | weuiCellTipsFontSize: 14, 10 | weuiCellLabelWidth: 105, 11 | 12 | // unit((weuiCellHeight - 2 * weuiCellGapV) / weuiCellFontSize) 13 | // 高度为44,减去上下padding的行高 14 | weuiCellLineHeight: 44 - 20, 15 | // unit(20 / @weuiCellFontSize, em), 16 | weuiCellsMarginTop: 20, 17 | 18 | // weui switch 19 | weuiSwitchHeight: 32, 20 | 21 | // weui uploader 22 | weuiUploaderBorderColor: '#D9D9D9', 23 | weuiUploaderActiveBorderColor: '#999999', 24 | weuiUploaderFileSpacing: '9px', 25 | weuiUploaderSize: '79px', 26 | weuiUploaderBorderWidth: '1px', 27 | } 28 | -------------------------------------------------------------------------------- /src/Mask/Mask.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, TouchableWithoutFeedback, View } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | mask: { 6 | flex: 1, 7 | zIndex: 1000, 8 | } 9 | }) 10 | 11 | const Mask = ({ transparent = false, style, onPress, children }) => 12 | 13 | 16 | {children} 17 | 18 | 19 | 20 | Mask.propTypes = { 21 | transparent: PropTypes.bool, 22 | style: View.propTypes.style, 23 | children: PropTypes.node, 24 | onPress: PropTypes.func 25 | } 26 | 27 | export default Mask 28 | -------------------------------------------------------------------------------- /src/Cell/CellsTitle.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cellsTitle: { 7 | marginTop: (V.weuiCellTipsFontSize * 0.77) + (((14 * V.baseLineHeight) - 14) * 0.5), 8 | marginBottom: (V.weuiCellTipsFontSize * 0.3) + (((14 * V.baseLineHeight) - 14) * 0.5), 9 | paddingLeft: V.weuiCellGapH, 10 | paddingRight: V.weuiCellGapH, 11 | fontSize: V.weuiCellTipsFontSize, 12 | color: V.weuiTextColorGray, 13 | } 14 | }) 15 | 16 | const CellsTitle = ({ children, style, ...others }) => 17 | {children} 18 | 19 | CellsTitle.propTypes = { 20 | children: PropTypes.node, 21 | style: Text.propTypes.style, 22 | } 23 | 24 | export default CellsTitle 25 | -------------------------------------------------------------------------------- /src/Picker/PickerSection.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Picker, StyleSheet } from 'react-native' 3 | 4 | const Item = Picker.Item 5 | 6 | const styles = StyleSheet.create({ 7 | pickerItem: { 8 | flex: 1 9 | } 10 | }) 11 | 12 | const PickerSection = ({ value, onChange, options = [], style, ...others }) => 13 | 19 | {options.map((item, idx) => )} 20 | 21 | 22 | PickerSection.propTypes = { 23 | options: PropTypes.array, 24 | value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 25 | onChange: PropTypes.func, 26 | style: Picker.propTypes.style, 27 | } 28 | 29 | export default PickerSection 30 | -------------------------------------------------------------------------------- /src/Cell/CellsTips.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Text } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | cellsTips: { 8 | paddingLeft: V.weuiCellGapH, 9 | paddingRight: V.weuiCellGapH, 10 | fontSize: V.weuiCellTipsFontSize, 11 | color: V.weuiTextColorGray, 12 | marginTop: V.weuiCellTipsFontSize * 0.3, 13 | lineHeight: V.weuiCellTipsFontSize * V.baseLineHeight, 14 | android: { 15 | lineHeight: Math.round(V.weuiCellTipsFontSize * V.baseLineHeight), 16 | }, 17 | } 18 | }) 19 | 20 | const CellsTips = ({ children, style, ...others }) => 21 | {children} 22 | 23 | CellsTips.propTypes = { 24 | children: PropTypes.node, 25 | style: Text.propTypes.style, 26 | } 27 | 28 | export default CellsTips 29 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Navigator } from 'react-native' 3 | import routes from './routes' 4 | 5 | const App = () => { 6 | const renderScene = (route, navigator) => { 7 | const routeConfig = routes[route.to] 8 | if (!routeConfig) return navigator.pop() 9 | const currentRoute = { ...routeConfig, ...route } 10 | return 11 | } 12 | 13 | const configureScene = (route) => { 14 | const currentRoute = { ...(routes[route.to] || {}), ...route } 15 | const { scene = 'PushFromRight' } = currentRoute 16 | return Navigator.SceneConfigs[scene] 17 | } 18 | 19 | return ( 20 | 26 | ) 27 | } 28 | 29 | export default App 30 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { "es6": true }, 3 | "extends": "airbnb", 4 | "parser": "babel-eslint", 5 | "rules": { 6 | "comma-dangle": 0, # 松散行尾逗号 7 | "semi": [2, "never"], # 不允许分号 8 | "no-underscore-dangle": 0, # 允许 _ 9 | "global-require": 0, # 允许 require 10 | "react/jsx-first-prop-new-line": 0, # 属性不需另起一行 11 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], # js 扩展名 12 | "jsx-a11y/no-static-element-interactions": 0, 13 | "react/forbid-prop-types": 0, 14 | "import/prefer-default-export": 0, 15 | "import/no-unresolved": [2, { ignore: ['\.(png || jpg || gif)$'] }], # 忽略 require 图片的错误 16 | 17 | # "id-length": 0, 18 | # "no-param-reassign": [2, { "props": false }], # 参数不可修改,属性可修改 19 | # "global-require": 0, 20 | # "no-class-assign": 0, 21 | # "react/prop-types": 0, 22 | # "react/prefer-stateless-function": 0, 23 | # "react/jsx-no-bind": 0 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Preview/PreviewBody.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | previewBody: { 7 | paddingTop: V.weuiCellGapV, 8 | paddingBottom: V.weuiCellGapV, 9 | paddingLeft: V.weuiCellGapH, 10 | paddingRight: V.weuiCellGapH, 11 | }, 12 | }) 13 | 14 | const PreviewBody = ({ style, children, ...other }) => { 15 | const childrenWithProps = React.Children.map(children, (child) => { 16 | if (child.type.name === 'PreviewItem') { 17 | return React.cloneElement(child, { preset: 'body' }) 18 | } 19 | return child 20 | }) 21 | 22 | return ( 23 | {childrenWithProps} 24 | ) 25 | } 26 | 27 | PreviewBody.propTypes = { 28 | style: View.propTypes.style, 29 | children: PropTypes.node, 30 | } 31 | 32 | export default PreviewBody 33 | -------------------------------------------------------------------------------- /src/Panel/PanelHeader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | panelHeader: { 7 | paddingTop: 14, 8 | paddingRight: 15, 9 | paddingBottom: 10, 10 | marginLeft: 15, 11 | borderBottomWidth: StyleSheet.hairlineWidth, 12 | borderStyle: 'solid', 13 | borderColor: V.weuiLineColorLight, 14 | }, 15 | panelHeaderText: { 16 | color: V.weuiTextColorGray, 17 | fontSize: 13, 18 | } 19 | }) 20 | 21 | const PanelHeader = ({ children, style, textStyle, ...others }) => 22 | 23 | {children} 24 | 25 | 26 | PanelHeader.propTypes = { 27 | children: PropTypes.node, 28 | style: View.propTypes.style, 29 | textStyle: Text.propTypes.style, 30 | } 31 | 32 | export default PanelHeader 33 | -------------------------------------------------------------------------------- /src/Button/ButtonArea.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | weuiBtnArea: { 7 | marginTop: V.weuiCellsMarginTop, 8 | marginRight: V.weuiBtnDefaultGap, 9 | marginBottom: 16 * 0.3, 10 | marginLeft: V.weuiBtnDefaultGap 11 | }, 12 | weuiBtnAreaInline: { 13 | flexDirection: 'row' 14 | }, 15 | }) 16 | 17 | const ButtonArea = ({ direction = 'vertical', style, children }) => { 18 | const buttonAreaStyle = [styles.weuiBtnArea] 19 | if (direction === 'horizontal') { 20 | buttonAreaStyle.push(styles.weuiBtnAreaInline) 21 | } 22 | 23 | return {children} 24 | } 25 | 26 | ButtonArea.propTypes = { 27 | direction: PropTypes.oneOf(['horizontal', 'vertical']), 28 | style: View.propTypes.style, 29 | children: PropTypes.node, 30 | } 31 | 32 | export default ButtonArea 33 | -------------------------------------------------------------------------------- /src/Grid/Grids.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import Grid from './Grid' 4 | import V from '../variable' 5 | 6 | const styles = StyleSheet.create({ 7 | grids: { 8 | flexDirection: 'row', 9 | flexWrap: 'wrap', 10 | overflow: 'hidden', 11 | borderTopWidth: StyleSheet.hairlineWidth, 12 | borderLeftWidth: StyleSheet.hairlineWidth, 13 | borderColor: V.weuiGridBorderColor, 14 | } 15 | }) 16 | 17 | const renderData = data => 18 | data.map((item, i) => 19 | ) 20 | 21 | const Grids = ({ children, data = [], style, ...others }) => 22 | 23 | {data.length > 0 ? renderData(data) : children} 24 | 25 | 26 | Grids.propTypes = { 27 | data: PropTypes.array, 28 | style: View.propTypes.style, 29 | children: PropTypes.node, 30 | } 31 | 32 | export default Grids 33 | -------------------------------------------------------------------------------- /src/Cell/Cells.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cells: { 7 | marginTop: V.weuiCellssMarginTop, 8 | backgroundColor: V.weuiCellBg, 9 | overflow: 'hidden', 10 | borderTopWidth: StyleSheet.hairlineWidth, 11 | borderBottomWidth: StyleSheet.hairlineWidth, 12 | borderColor: V.weuiCellBorderColor 13 | } 14 | }) 15 | 16 | const Cells = ({ children, style, ...others }) => { 17 | const childrenWithProps = React.Children.map(children, (child, idx) => { 18 | if (idx === 0) { 19 | return React.cloneElement(child, { first: true }) 20 | } 21 | return child 22 | }) 23 | 24 | return ( 25 | 26 | {childrenWithProps} 27 | 28 | ) 29 | } 30 | 31 | Cells.propTypes = { 32 | children: PropTypes.node, 33 | style: View.propTypes.style, 34 | } 35 | 36 | export default Cells 37 | -------------------------------------------------------------------------------- /src/Media/MediaHeader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | mediaHeader: { 7 | marginRight: V.baseFontSize * 0.8, 8 | width: 60, 9 | height: 60, 10 | }, 11 | mediaAppmsgThumb: { 12 | width: 60, 13 | height: 60, 14 | } 15 | }) 16 | 17 | const MediaHeader = ({ style, children, ...others }) => { 18 | const childrenWithProps = React.Children.map(children, (child) => { 19 | if (child.type.displayName === 'Image' && !child.props.style) { 20 | return React.cloneElement(child, { style: [styles.mediaAppmsgThumb, child.props.style] }) 21 | } 22 | return child 23 | }) 24 | 25 | return ( 26 | {childrenWithProps} 27 | ) 28 | } 29 | 30 | MediaHeader.propTypes = { 31 | style: View.propTypes.style, 32 | children: PropTypes.node, 33 | } 34 | 35 | export default MediaHeader 36 | -------------------------------------------------------------------------------- /src/Media/MediaInfoMeta.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text, StyleSheet } from 'react-native' 3 | 4 | const styles = StyleSheet.create({ 5 | mediaInfoMeta: { 6 | paddingRight: 13 * 1, 7 | }, 8 | mediaInfoMetaExtra: { 9 | borderLeftWidth: StyleSheet.hairlineWidth, 10 | borderStyle: 'solid', 11 | borderColor: '#CECECE', 12 | paddingLeft: 13 * 1, 13 | }, 14 | mediaInfoMetaText: { 15 | fontSize: 13, 16 | color: '#CECECE', 17 | } 18 | }) 19 | 20 | const MediaInfoMeta = ({ extra, style, textStyle, children, ...others }) => 21 | 22 | 23 | {children} 24 | 25 | 26 | 27 | MediaInfoMeta.propTypes = { 28 | extra: PropTypes.bool, 29 | style: View.propTypes.style, 30 | textStyle: Text.propTypes.style, 31 | children: PropTypes.node, 32 | } 33 | 34 | export default MediaInfoMeta 35 | -------------------------------------------------------------------------------- /example/routes.js: -------------------------------------------------------------------------------- 1 | import Grid from './page/Grid' 2 | import Button from './page/Button' 3 | import Cell from './page/Cell' 4 | import Toast from './page/Toast' 5 | import Dialog from './page/Dialog' 6 | import Progress from './page/Progress' 7 | import Msg from './page/Msg' 8 | import Article from './page/Article' 9 | import ActionSheet from './page/ActionSheet' 10 | import Icons from './page/Icons' 11 | import Panel from './page/Panel' 12 | import Tab from './page/Tab' 13 | import SearchBar from './page/SearchBar' 14 | 15 | const routes = { 16 | Grid: { component: Grid }, 17 | Button: { component: Button }, 18 | Cell: { component: Cell }, 19 | Toast: { component: Toast }, 20 | Dialog: { component: Dialog }, 21 | Progress: { component: Progress }, 22 | Msg: { component: Msg }, 23 | Article: { component: Article }, 24 | ActionSheet: { component: ActionSheet }, 25 | Icons: { component: Icons }, 26 | Panel: { component: Panel }, 27 | Tab: { component: Tab }, 28 | SearchBar: { component: SearchBar }, 29 | } 30 | 31 | export default routes 32 | -------------------------------------------------------------------------------- /src/Preview/PreviewHeader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | previewHeader: { 7 | paddingTop: V.weuiCellGapV, 8 | paddingBottom: V.weuiCellGapV, 9 | paddingRight: V.weuiCellGapH, 10 | borderBottomWidth: StyleSheet.hairlineWidth, 11 | borderColor: V.weuiCellBorderColor, 12 | marginLeft: V.weuiCellGapH, 13 | }, 14 | }) 15 | 16 | const PreviewHeader = ({ style, children, ...other }) => { 17 | const childrenWithProps = React.Children.map(children, (child) => { 18 | if (child.type.name === 'PreviewItem') { 19 | return React.cloneElement(child, { preset: 'header' }) 20 | } 21 | return child 22 | }) 23 | 24 | return ( 25 | {childrenWithProps} 26 | ) 27 | } 28 | 29 | PreviewHeader.propTypes = { 30 | style: View.propTypes.style, 31 | children: PropTypes.node, 32 | } 33 | 34 | export default PreviewHeader 35 | -------------------------------------------------------------------------------- /src/Cell/CellHeader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cellHeader: { 7 | marginRight: 5, 8 | }, 9 | image: { 10 | width: 24, 11 | height: 24, 12 | }, 13 | error: { 14 | color: V.weuiColorWarn 15 | } 16 | }) 17 | 18 | const CellHeader = ({ error, children, style, ...others }) => { 19 | const childrenWithProps = React.Children.map(children, (child) => { 20 | if (child.type.displayName === 'Image' && !child.props.style) { 21 | return React.cloneElement(child, { style: [styles.image, child.props.style] }) 22 | } 23 | if (error && child.type.name === 'Label') { 24 | return React.cloneElement(child, { style: [child.props.style, styles.error] }) 25 | } 26 | return child 27 | }) 28 | 29 | return {childrenWithProps} 30 | } 31 | 32 | CellHeader.propTypes = { 33 | error: PropTypes.bool, 34 | children: PropTypes.node, 35 | style: View.propTypes.style, 36 | } 37 | 38 | export default CellHeader 39 | -------------------------------------------------------------------------------- /src/variable.js: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/merge' 2 | import _button from './Button/variable' 3 | import _grid from './Grid/variable' 4 | import _cell from './Cell/variable' 5 | import _msg from './Msg/variable' 6 | import _progress from './Progress/variable' 7 | import _dialog from './Dialog/variable' 8 | import _form from './Form/variable' 9 | 10 | const _global = { 11 | // color 12 | weuiColorPrimary: '#1AAD19', 13 | weuiColorWarn: '#E64340', 14 | 15 | // link 16 | weuiLinkColorDefault: '#586C94', 17 | 18 | // background 19 | weuiBgColorDefault: '#EFEFF4', 20 | weuiBgColorActive: '#ECECEC', 21 | 22 | // line 23 | weuiLineColorLight: '#E5E5E5', 24 | weuiLineColorDark: '#BCBAB6', 25 | 26 | // text 27 | weuiTextColorTitle: '#000000', 28 | weuiTextColorTips: '#B2B2B2', 29 | weuiTextColorWarn: '#E64340', 30 | weuiTextColorGray: '#999999', 31 | 32 | weuiActionSheetAndroidBorderRadius: 2, 33 | 34 | // ------------------------- old 35 | baseFontSize: 16, 36 | baseLineHeight: 1.6, 37 | } 38 | 39 | export default merge( 40 | {}, 41 | _global, 42 | _button, 43 | _grid, 44 | _cell, 45 | _msg, 46 | _progress, 47 | _dialog, 48 | _form, 49 | ) 50 | -------------------------------------------------------------------------------- /src/Cell/CellBody.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | import CellText from './CellText' 5 | import V from '../variable' 6 | 7 | const styles = StyleSheet.create({ 8 | cellBody: { 9 | flex: 1, 10 | }, 11 | error: { 12 | flex: 1, 13 | color: V.weuiColorWarn, 14 | }, 15 | }) 16 | 17 | const CellBody = ({ error, children, style, ...others }) => { 18 | const childrenWithProps = React.Children.map(children, (child) => { 19 | if (typeof child === 'string') { 20 | return {child} 21 | } 22 | return React.cloneElement(child, { style: [child.props.style, 23 | error ? styles.error : null 24 | ] }) 25 | }) 26 | 27 | return ( 28 | 29 | {childrenWithProps} 30 | {error ? : false} 31 | 32 | ) 33 | } 34 | 35 | CellBody.propTypes = { 36 | error: PropTypes.bool, 37 | children: PropTypes.node, 38 | style: View.propTypes.style, 39 | } 40 | 41 | export default CellBody 42 | -------------------------------------------------------------------------------- /index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | */ 5 | 6 | import React, { Component } from 'react' 7 | import { 8 | AppRegistry, 9 | StyleSheet, 10 | Text, 11 | View, 12 | } from 'react-native' 13 | 14 | class example extends Component { 15 | render() { 16 | return ( 17 | 18 | 19 | Welcome to React Native! 20 | 21 | 22 | To get started, edit index.android.js 23 | 24 | 25 | Shake or press menu button for dev menu 26 | 27 | 28 | ) 29 | } 30 | } 31 | 32 | const styles = StyleSheet.create({ 33 | container: { 34 | flex: 1, 35 | justifyContent: 'center', 36 | alignItems: 'center', 37 | backgroundColor: '#F5FCFF', 38 | }, 39 | welcome: { 40 | fontSize: 20, 41 | textAlign: 'center', 42 | margin: 10, 43 | }, 44 | instructions: { 45 | textAlign: 'center', 46 | color: '#333333', 47 | marginBottom: 5, 48 | }, 49 | }) 50 | 51 | AppRegistry.registerComponent('example', () => example) 52 | -------------------------------------------------------------------------------- /src/Grid/Grid.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, TouchableHighlight, StyleSheet, Dimensions } from 'react-native' 3 | import GridIcon from './GridIcon' 4 | import GridLabel from './GridLabel' 5 | import V from '../variable' 6 | 7 | const styles = StyleSheet.create({ 8 | grid: { 9 | paddingTop: 20, 10 | paddingRight: 10, 11 | paddingBottom: 20, 12 | paddingLeft: 10, 13 | borderRightWidth: StyleSheet.hairlineWidth, 14 | borderBottomWidth: StyleSheet.hairlineWidth, 15 | borderColor: V.weuiGridBorderColor, 16 | width: (Dimensions.get('window').width - StyleSheet.hairlineWidth) / 3, 17 | height: 87 18 | } 19 | }) 20 | 21 | const Grid = ({ children, icon = false, label = '', style, ...others }) => 22 | 27 | 28 | {icon ? {icon} : false} 29 | {children} 30 | {label ? {label} : false} 31 | 32 | 33 | 34 | Grid.propTypes = { 35 | label: PropTypes.string, 36 | icon: PropTypes.any, 37 | style: TouchableHighlight.propTypes.style, 38 | children: PropTypes.node, 39 | } 40 | 41 | export default Grid 42 | -------------------------------------------------------------------------------- /src/Form/RadioCells.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | import { Cells, Cell, CellBody, CellText } from '../Cell' 5 | import V from '../variable' 6 | 7 | const styles = StyleSheet.create({ 8 | radio: { 9 | fontSize: 16, 10 | paddingLeft: V.weuiCellInnerGapH, 11 | }, 12 | disabled: { 13 | opacity: 0.5 14 | }, 15 | }) 16 | 17 | const RadioCells = ({ value, options, onChange, disabled, style, children, ...others }) => 18 | 19 | {options.map((option, idx) => 20 | !disabled && onChange(option.value)}> 21 | 22 | {option.label || option.value} 23 | 24 | {value === option.value ? ( 25 | 26 | ) : null} 27 | 28 | )} 29 | {children} 30 | 31 | 32 | RadioCells.propTypes = { 33 | value: PropTypes.any, 34 | options: PropTypes.array.isRequired, 35 | onChange: PropTypes.func, 36 | disabled: PropTypes.bool, 37 | style: Icon.propTypes.style, 38 | children: PropTypes.node, 39 | } 40 | 41 | export default RadioCells 42 | -------------------------------------------------------------------------------- /src/Button/ButtonPreview.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, TouchableHighlight, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | previewBtn: { 7 | flex: 1, 8 | alignSelf: 'center', 9 | borderLeftWidth: StyleSheet.hairlineWidth, 10 | borderColor: V.weuiDialogLineColor, 11 | }, 12 | 13 | previewBtnText: { 14 | textAlign: 'center', 15 | color: V.weuiDialogLinkColor, 16 | lineHeight: 50, 17 | fontSize: V.baseFontSize, 18 | }, 19 | previewBtnDefaultText: { 20 | color: V.weuiTextColorGray, 21 | }, 22 | previewBtnPrimaryText: { 23 | color: '#0BB20C', 24 | }, 25 | }) 26 | 27 | const ButtonPreview = ({ primary, style, textStyle, children, ...others }) => 28 | 29 | {children} 35 | 36 | 37 | ButtonPreview.propTypes = { 38 | primary: PropTypes.bool, 39 | style: TouchableHighlight.propTypes.style, 40 | textStyle: Text.propTypes.style, 41 | children: PropTypes.node, 42 | } 43 | 44 | export default ButtonPreview 45 | -------------------------------------------------------------------------------- /src/Media/Media.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { TouchableHighlight, StyleSheet, View } from 'react-native' 3 | import V from '../variable' 4 | 5 | const underlayColor = '#ECECEC' 6 | 7 | const styles = StyleSheet.create({ 8 | media: { 9 | paddingTop: 15, 10 | paddingBottom: 15, 11 | paddingRight: 15, 12 | marginLeft: 15, 13 | borderTopWidth: StyleSheet.hairlineWidth, 14 | borderStyle: 'solid', 15 | borderColor: V.weuiLineColorLight, 16 | }, 17 | 18 | // appmsg 19 | appmsgMedia: { 20 | flexDirection: 'row', 21 | alignItems: 'center', 22 | }, 23 | 24 | // small_appmsg 25 | small_appmsgMedia: { 26 | padding: 0, 27 | marginLeft: 0, 28 | }, 29 | 30 | firstMedia: { 31 | borderTopWidth: 0 32 | } 33 | }) 34 | 35 | const Media = ({ type, style, children, first, ...others }) => 36 | 41 | {children} 44 | 45 | 46 | Media.propTypes = { 47 | type: PropTypes.oneOf(['text', 'appmsg', 'small_appmsg']), 48 | style: View.propTypes.style, 49 | children: PropTypes.node, 50 | first: PropTypes.bool, 51 | } 52 | 53 | export default Media 54 | -------------------------------------------------------------------------------- /src/Button/variable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | weuiBtnHeight: 46, 3 | weuiBtnFontSize: 18, 4 | weuiBtnFontColor: '#FFFFFF', 5 | weuiBtnDisabledFontColor: 'rgba(255,255,255,.6)', 6 | weuiBtnActiveFontColor: 'rgba(255,255,255,.4)', 7 | weuiBtnBorderRadius: 5, 8 | weuiBtnDefaultGap: 15, 9 | 10 | weuiBtnMiniFontSize: 13, 11 | weuiBtnMiniHeight: 2.3, 12 | 13 | weuiBtnDefaultFontColor: '#000000', 14 | weuiBtnDefaultDisabledFontColor: 'rgba(0,0,0,.3)', 15 | weuiBtnDefaultActiveFontColor: 'rgba(0,0,0,.6)', 16 | 17 | weuiBtnDefaultBg: '#F8F8F8', 18 | weuiBtnDefaultActiveBg: '#DEDEDE', 19 | weuiBtnDefaultDisabledBg: '#F7F7F7', 20 | 21 | weuiBtnPrimaryBg: '#1AAD19', 22 | weuiBtnPrimaryActiveBg: '#179B16', 23 | weuiBtnPrimaryDisabledBg: '#9ED99D', 24 | 25 | weuiBtnWarnBg: '#E64340', 26 | weuiBtnWarnActiveBg: '#CE3C39', 27 | weuiBtnWarnDisabledBg: '#EC8B89', 28 | 29 | weuiBtnPlainPrimaryColor: 'rgba(26,173,25,1)', 30 | weuiBtnPlainPrimaryBorderColor: 'rgba(26,173,25,1)', 31 | weuiBtnPlainPrimaryActiveColor: 'rgba(26,173,25,.6)', 32 | weuiBtnPlainPrimaryActiveBorderColor: 'rgba(26,173,25,.6)', 33 | 34 | weuiBtnPlainDefaultColor: 'rgba(53,53,53,1)', 35 | weuiBtnPlainDefaultBorderColor: 'rgba(53,53,53,1)', 36 | weuiBtnPlainDefaultActiveColor: 'rgba(53,53,53,.6)', 37 | weuiBtnPlainDefaultActiveBorderColor: 'rgba(53,53,53,.6)', 38 | } 39 | -------------------------------------------------------------------------------- /src/Badge/Badge.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, View, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | badge: { 7 | color: '#FFFFFF', 8 | fontSize: 12, 9 | paddingLeft: 3.5, 10 | paddingRight: 3.5, 11 | lineHeight: 14, 12 | paddingTop: 1, 13 | textAlign: 'center', 14 | backgroundColor: V.weuiColorWarn, 15 | borderWidth: 1.5, 16 | borderColor: 'transparent', 17 | borderRadius: 8.5, 18 | overflow: 'hidden', 19 | }, 20 | 21 | default: {}, 22 | header: { 23 | position: 'absolute', 24 | top: 0 - (12 * 0.4), 25 | right: 0 - (12 * 0.4), 26 | }, 27 | body: { 28 | marginLeft: 5, 29 | }, 30 | footer: { 31 | marginLeft: 5, 32 | marginRight: 5, 33 | }, 34 | 35 | dot: { 36 | width: 12 * 0.6, 37 | height: 12 * 0.6, 38 | }, 39 | }) 40 | 41 | const Badge = ({ dot = false, preset = 'default', style, children, ...others }) => 42 | 46 | {children} 47 | 48 | 49 | Badge.propTypes = { 50 | dot: PropTypes.bool, 51 | preset: PropTypes.oneOf(['default', 'header', 'body', 'footer']), 52 | style: View.propTypes.style, 53 | children: PropTypes.node, 54 | } 55 | 56 | export default Badge 57 | -------------------------------------------------------------------------------- /src/Popup/PopupHeader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, StyleSheet, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | popupHeader: { 7 | flexDirection: 'row', 8 | alignItems: 'center', 9 | borderBottomWidth: StyleSheet.hairlineWidth, 10 | borderColor: '#E5E5E5', 11 | backgroundColor: '#fbf9fe' 12 | }, 13 | popupActionLeft: { 14 | flex: 1, 15 | color: '#586C94', 16 | textAlign: 'left', 17 | paddingTop: 10, 18 | paddingBottom: 10, 19 | paddingLeft: 15, 20 | fontSize: V.baseFontSize 21 | }, 22 | popupActionRight: { 23 | flex: 1, 24 | color: '#586C94', 25 | textAlign: 'right', 26 | paddingTop: 10, 27 | paddingBottom: 10, 28 | paddingRight: 15, 29 | fontSize: V.baseFontSize 30 | } 31 | }) 32 | 33 | const PopupHeader = ({ style, left = {}, right = {} }) => 34 | 35 | {left.label} 39 | {right.label} 43 | 44 | 45 | PopupHeader.propTypes = { 46 | style: View.propTypes.style, 47 | left: PropTypes.object, 48 | right: PropTypes.object, 49 | } 50 | 51 | export default PopupHeader 52 | -------------------------------------------------------------------------------- /src/Panel/PanelFooter.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Image, Text, View, TouchableHighlight, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | PanelFooter: { 7 | paddingTop: 12, // 10 8 | paddingRight: 15, 9 | paddingBottom: 12, 10 | marginLeft: 15, 11 | borderTopWidth: StyleSheet.hairlineWidth, 12 | borderStyle: 'solid', 13 | borderColor: V.weuiLineColorLight, 14 | position: 'relative', 15 | flexDirection: 'row', 16 | alignItems: 'center', 17 | }, 18 | PanelFooterText: { 19 | flex: 1, 20 | color: V.weuiTextColorGray, 21 | fontSize: 14 22 | } 23 | }) 24 | 25 | const PanelFooter = ({ children, style, textStyle, access, ...others }) => 26 | 27 | 28 | 29 | {children} 30 | 31 | {access ? 32 | : false} 36 | 37 | 38 | 39 | PanelFooter.propTypes = { 40 | access: PropTypes.bool, 41 | children: PropTypes.node, 42 | style: TouchableHighlight.propTypes.style, 43 | textStyle: Text.propTypes.style, 44 | } 45 | 46 | export default PanelFooter 47 | -------------------------------------------------------------------------------- /src/Cell/CellFooter.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Image, Text, View, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cellFooter: { 7 | flexDirection: 'row', 8 | alignItems: 'center', 9 | marginLeft: 5, 10 | }, 11 | cellFooterText: { 12 | textAlign: 'center', 13 | color: V.weuiTextColorGray, 14 | fontSize: V.weuiCellFontSize 15 | }, 16 | vcode: { 17 | width: 100, 18 | height: 44, 19 | } 20 | }) 21 | 22 | const CellFooter = ({ children, style, access, ...others }) => { 23 | const childrenWithProps = React.Children.map(children, (child) => { 24 | if (!child.type) return {child} 25 | if (child.type && child.type.displayName === 'Image' && !child.props.style) { 26 | return React.cloneElement(child, { style: [styles.vcode, child.props.style] }) 27 | } 28 | return child 29 | }) 30 | 31 | return ( 32 | 33 | {childrenWithProps} 34 | {access ? 35 | : false} 39 | 40 | ) 41 | } 42 | 43 | CellFooter.propTypes = { 44 | access: PropTypes.bool, 45 | children: PropTypes.node, 46 | style: Text.propTypes.style, 47 | } 48 | 49 | export default CellFooter 50 | -------------------------------------------------------------------------------- /src/Icon/lib/tab-bar-item-ios.js: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash/isEqual'; 2 | import pick from 'lodash/pick'; 3 | 4 | import React, { 5 | Component, 6 | PropTypes, 7 | } from 'react'; 8 | 9 | import { 10 | TabBarIOS, 11 | } from './react-native'; 12 | 13 | export default function createTabBarItemIOSComponent(IconNamePropType, getImageSource) { 14 | return class TabBarItemIOS extends Component { 15 | static propTypes = { 16 | iconName: IconNamePropType.isRequired, 17 | selectedIconName: IconNamePropType, 18 | iconSize: PropTypes.number, 19 | }; 20 | 21 | static defaultProps = { 22 | iconSize: 30, 23 | }; 24 | 25 | updateIconSources(props) { 26 | if (props.iconName) { 27 | getImageSource(props.iconName, props.iconSize).then(icon => this.setState({ icon })); 28 | } 29 | if (props.selectedIconName) { 30 | getImageSource(props.selectedIconName, props.iconSize).then(selectedIcon => this.setState({ selectedIcon })); 31 | } 32 | } 33 | 34 | componentWillMount() { 35 | this.updateIconSources(this.props); 36 | } 37 | 38 | componentWillReceiveProps(nextProps) { 39 | const keys = Object.keys(TabBarItemIOS.propTypes); 40 | if (!isEqual(pick(nextProps, keys), pick(this.props, keys))) { 41 | this.updateIconSources(nextProps); 42 | } 43 | } 44 | 45 | render() { 46 | return (); 47 | } 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/Form/CheckboxCells.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet } from 'react-native' 3 | import xor from 'lodash/xor' 4 | import { Cells, Cell, CellHeader, CellBody, CellText } from '../Cell' 5 | import { Icon } from '../Icon' 6 | import V from '../variable' 7 | 8 | const styles = StyleSheet.create({ 9 | checkbox: { 10 | fontSize: 23, 11 | paddingRight: V.weuiCellInnerGapH, 12 | }, 13 | disabled: { 14 | opacity: 0.5 15 | }, 16 | }) 17 | 18 | const CheckboxCells = ({ value, options, onChange, disabled, style, children, ...others }) => { 19 | const inArray = v => 20 | value.filter(a => a === v).length 21 | 22 | return ( 23 | 24 | {options.map((option, idx) => 25 | !disabled && onChange(xor(value, [option.value]))}> 26 | 27 | 28 | 29 | 30 | {option.label} 31 | 32 | 33 | )} 34 | {children} 35 | 36 | ) 37 | } 38 | 39 | CheckboxCells.propTypes = { 40 | value: PropTypes.any, 41 | onChange: PropTypes.func, 42 | options: PropTypes.array.isRequired, 43 | disabled: PropTypes.bool, 44 | style: Icon.propTypes.style, 45 | children: PropTypes.node, 46 | } 47 | 48 | export default CheckboxCells 49 | -------------------------------------------------------------------------------- /example/page/Progress.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Progress, Button, ButtonArea } from '../../src' 3 | import Page from '../components/Page' 4 | import $SK from 'react-native-stylekit' 5 | 6 | class ProgressScene extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | value: 0, 11 | timer: null, 12 | isUploading: false, 13 | } 14 | this.start = this.start.bind(this) 15 | this.pause = this.pause.bind(this) 16 | } 17 | componentWillUnmount() { 18 | if (this.state.timer) clearInterval(this.state.timer) 19 | } 20 | start() { 21 | if (this.state.isUploading) return 22 | this.state.isUploading = true 23 | this.upload() 24 | } 25 | pause() { 26 | this.setState({ isUploading: false }) 27 | } 28 | upload() { 29 | if (!this.state.isUploading) return 30 | this.setState({ value: ++this.state.value % 100 }) 31 | this.state.timer = setTimeout(this.upload.bind(this), 20) 32 | } 33 | render() { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ) 44 | } 45 | } 46 | export default ProgressScene 47 | -------------------------------------------------------------------------------- /src/Form/TextArea.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text, TextInput } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const styles = create({ 7 | textarea: { 8 | fontSize: V.weuiCellFontSize, 9 | height: V.weuiCellFontSize * V.baseLineHeight * 3, 10 | lineHeight: V.weuiCellFontSize * V.baseLineHeight, 11 | android: { 12 | lineHeight: Math.round(V.weuiCellFontSize * V.baseLineHeight), 13 | }, 14 | }, 15 | textareaCounter: { 16 | color: V.weuiTextColorTips, 17 | textAlign: 'right', 18 | } 19 | }) 20 | 21 | const TextArea = ({ 22 | value, 23 | onChange, 24 | showCounter = true, 25 | maxLength, 26 | defaultValue, 27 | disabled = false, 28 | style, 29 | ...others 30 | }) => 31 | 32 | 42 | {showCounter ? 43 | {(value || defaultValue || '').length}{maxLength ? `/ ${maxLength}` : false} 46 | : false} 47 | 48 | 49 | TextArea.propTypes = { 50 | value: PropTypes.string, 51 | showCounter: PropTypes.bool, 52 | maxLength: PropTypes.number, 53 | defaultValue: PropTypes.string, 54 | onChange: PropTypes.func, 55 | disabled: PropTypes.bool, 56 | style: TextInput.propTypes.style, 57 | } 58 | 59 | export default TextArea 60 | -------------------------------------------------------------------------------- /src/Form/Slider.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, View, Slider as RNSlider, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | sliderWrapper: { 7 | flexDirection: 'row', 8 | alignItems: 'center' 9 | }, 10 | slider: { 11 | flex: 1, 12 | }, 13 | sliderText: { 14 | marginLeft: 14 * 0.5, 15 | minWidth: 24, 16 | color: '#888888', 17 | textAlign: 'center', 18 | fontSize: 14, 19 | } 20 | }) 21 | 22 | const Slider = ({ 23 | max = 100, 24 | min = 0, 25 | step = 1, 26 | defaultValue = 0, 27 | disabled = false, 28 | showValue = true, 29 | value, 30 | onChange, 31 | style, 32 | sliderStyle, 33 | textStyle, 34 | ...others 35 | }) => 36 | 37 | 49 | {showValue ? {value} : false} 50 | 51 | 52 | Slider.propTypes = { 53 | max: PropTypes.number, 54 | min: PropTypes.number, 55 | step: PropTypes.number, 56 | defaultValue: PropTypes.number, 57 | showValue: PropTypes.bool, 58 | value: PropTypes.number, 59 | onChange: PropTypes.func, 60 | disabled: PropTypes.bool, 61 | style: View.propTypes.style, 62 | sliderStyle: RNSlider.propTypes.style, 63 | textStyle: Text.propTypes.style, 64 | } 65 | 66 | export default Slider 67 | -------------------------------------------------------------------------------- /src/Preview/PreviewItem.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, View } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | formPreviewItem: { 7 | overflow: 'hidden', 8 | flexDirection: 'row', 9 | }, 10 | bodyPreviewLabel: { 11 | marginRight: V.baseFontSize * 0.9, 12 | fontSize: V.baseFontSize * 0.9, 13 | lineHeight: V.baseFontSize * 0.9 * 2, 14 | }, 15 | bodyPreviewValue: { 16 | fontSize: V.baseFontSize * 0.9, 17 | lineHeight: V.baseFontSize * 0.9 * 2, 18 | color: V.weuiTextColorGray, 19 | }, 20 | headerPreviewLabel: { 21 | marginRight: V.baseFontSize, 22 | fontSize: V.baseFontSize, 23 | lineHeight: V.baseFontSize * 2.5, 24 | }, 25 | headerPreviewValue: { 26 | fontSize: V.baseFontSize * 1.6, 27 | lineHeight: V.baseFontSize * 2.5, 28 | fontStyle: 'normal', 29 | }, 30 | }) 31 | 32 | const PreviewItem = ({ style, children, preset = 'body', ...others }) => { 33 | const childrenWithProps = React.Children.map(children, (child) => { 34 | if (child.type.name === 'PreviewLabel') { 35 | return React.cloneElement(child, { style: [child.props.style, styles[`${preset}PreviewLabel`]] }) 36 | } else if (child.type.name === 'PreviewValue') { 37 | return React.cloneElement(child, { style: [child.props.style, styles[`${preset}PreviewValue`]] }) 38 | } 39 | return child 40 | }) 41 | 42 | return ( 43 | {childrenWithProps} 44 | ) 45 | } 46 | 47 | PreviewItem.propTypes = { 48 | style: View.propTypes.style, 49 | children: PropTypes.node, 50 | preset: PropTypes.string, 51 | } 52 | 53 | export default PreviewItem 54 | -------------------------------------------------------------------------------- /example/page/Icons.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | StyleSheet, 4 | ScrollView, 5 | View, 6 | } from 'react-native' 7 | import { Icon } from '../../src' 8 | import SK from 'react-native-stylekit' 9 | 10 | const styles = StyleSheet.create({ 11 | wrapper: { 12 | flex: 1, 13 | paddingTop: 84, 14 | backgroundColor: '#fbf9fe', 15 | }, 16 | }) 17 | 18 | 19 | const SwiperScene = () => 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | export default SwiperScene 49 | -------------------------------------------------------------------------------- /src/Form/Agreement.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { TouchableWithoutFeedback, View, Text, StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | import V from '../variable' 5 | 6 | const styles = StyleSheet.create({ 7 | agreement: { 8 | flexDirection: 'row', 9 | alignItems: 'center', 10 | paddingTop: 13 * 0.5, 11 | paddingBottom: 13 * 0.5, 12 | paddingLeft: 15, 13 | paddingRight: 15, 14 | }, 15 | agreementText: { 16 | fontSize: 13, 17 | color: V.weuiTextColorGray, 18 | marginLeft: 5 19 | }, 20 | checkbox: { 21 | height: 13, 22 | width: 13, 23 | borderRadius: 3, 24 | borderWidth: StyleSheet.hairlineWidth, 25 | borderColor: '#D1D1D1', 26 | alignItems: 'center', 27 | justifyContent: 'center' 28 | }, 29 | disabled: { 30 | backgroundColor: '#E1E1E1', 31 | } 32 | }) 33 | 34 | const Agreement = ({ value = false, onChange, disabled, style, textStyle, children, ...others }) => 35 | onChange(!value) })} {...others}> 36 | 37 | 38 | {value 39 | ? 40 | : false 41 | } 42 | 43 | {children} 44 | 45 | 46 | 47 | Agreement.propTypes = { 48 | value: PropTypes.bool, 49 | disabled: PropTypes.bool, 50 | onChange: PropTypes.func, 51 | style: View.propTypes.style, 52 | textStyle: Text.propTypes.style, 53 | children: PropTypes.node, 54 | } 55 | 56 | export default Agreement 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rn-weui", 3 | "version": "1.0.3", 4 | "description": "weui for react-native", 5 | "main": "./lib", 6 | "scripts": { 7 | "start": "node node_modules/react-native/local-cli/cli.js start", 8 | "clean": "rimraf ./lib", 9 | "build": "npm run lint & npm run clean & babel ./src --out-dir ./lib", 10 | "lint": "eslint ./src/**/*.js", 11 | "test": "jest" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/maskzh/rn-weui.git" 16 | }, 17 | "keywords": [ 18 | "react", 19 | "native", 20 | "weui" 21 | ], 22 | "devDependencies": { 23 | "babel-cli": "^6.18.0", 24 | "babel-eslint": "^7.1.1", 25 | "babel-jest": "18.0.0", 26 | "babel-preset-react-native": "1.9.1", 27 | "eslint": "^3.12.2", 28 | "eslint-config-airbnb": "^13.0.0", 29 | "eslint-plugin-import": "^2.2.0", 30 | "eslint-plugin-jsx-a11y": "^2.2.3", 31 | "eslint-plugin-react": "^6.8.0", 32 | "jest": "18.1.0", 33 | "react-native-blur": "^2.0.0", 34 | "react-native-navbar": "^1.6.0", 35 | "react-native-stylekit": "^0.0.3", 36 | "react-test-renderer": "15.4.1", 37 | "rimraf": "^2.5.4" 38 | }, 39 | "rnpm": { 40 | "assets": [ 41 | "fonts" 42 | ] 43 | }, 44 | "author": "maskzh", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/maskzh/rn-weui/issues" 48 | }, 49 | "homepage": "https://github.com/maskzh/rn-weui#readme", 50 | "dependencies": { 51 | "lodash": "^4.17.4", 52 | "react": "^15.4.2", 53 | "react-native": "^0.40.0", 54 | "react-native-image-picker": "^0.24.1", 55 | "react-native-photo-view": "^1.2.0", 56 | "react-native-tab-navigator": "^0.3.3", 57 | "react-native-scrollable-tab-view": "^0.7.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Icon/lib/generate-icon-set-from-css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | var fs = require('fs'); 4 | 5 | function extractGlyphMapFromCss(files, selectorPattern) { 6 | var styleRulePattern = '(\\.[A-Za-z0-9_.:, \\n\\t-]+)\\{[^}]*content: ?["\\\']\\\\([a-f0-9]+)["\\\'][^}]*\\}'; 7 | var allStyleRules = new RegExp(styleRulePattern, 'g'); 8 | var singleStyleRules = new RegExp(styleRulePattern); 9 | var allSelectors = new RegExp(selectorPattern, 'g'); 10 | var singleSelector = new RegExp(selectorPattern); 11 | 12 | var glyphMap = {}; 13 | if(typeof files === 'string') { 14 | files = [files]; 15 | } 16 | 17 | files.forEach(function(fileName) { 18 | var contents = fs.readFileSync(fileName, { encoding: 'utf8' }); 19 | var rules = contents.match(allStyleRules); 20 | if(rules) { 21 | rules.forEach(function(rule) { 22 | var ruleParts = rule.match(singleStyleRules); 23 | var charCode = parseInt(ruleParts[2], 16); 24 | var selectors = ruleParts[1].match(allSelectors); 25 | if(selectors) { 26 | selectors.forEach(function(selector) { 27 | var name = selector.match(singleSelector)[1]; 28 | glyphMap[name] = charCode; 29 | }); 30 | } 31 | }); 32 | } 33 | }); 34 | return glyphMap; 35 | }; 36 | 37 | function escapeRegExp(str) { 38 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 39 | } 40 | 41 | function generateIconSetFromCss(cssFiles, selectorPrefix, template, data) { 42 | var glyphMap = extractGlyphMapFromCss(cssFiles, escapeRegExp(selectorPrefix) + '([A-Za-z0-9_-]+):before'); 43 | var content = JSON.stringify(glyphMap, null, ' '); 44 | if(template) { 45 | var compiled = _.template(template); 46 | data = data || {}; 47 | data.glyphMap = content; 48 | content = compiled(data); 49 | } 50 | return content; 51 | }; 52 | 53 | module.exports = generateIconSetFromCss; 54 | -------------------------------------------------------------------------------- /src/Cell/Cell.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, TouchableHighlight, StyleSheet } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | cell: { 7 | flexDirection: 'row', 8 | alignItems: 'center', 9 | marginLeft: V.weuiCellGapH, 10 | paddingTop: V.weuiCellGapV, 11 | paddingBottom: V.weuiCellGapV, 12 | paddingRight: V.weuiCellGapH, 13 | borderTopWidth: StyleSheet.hairlineWidth, 14 | borderColor: V.weuiCellBorderColor, 15 | }, 16 | firstCell: { 17 | borderTopWidth: 0, 18 | }, 19 | vcodeCell: { 20 | paddingTop: 0, 21 | paddingBottom: 0, 22 | paddingRight: 0, 23 | }, 24 | disabledCell: { 25 | opacity: 0.5 26 | } 27 | }) 28 | 29 | const Cell = ({ access, vcode, error, first, disabled, children, style, ...others }) => { 30 | const childrenWithProps = React.Children.map(children, (child) => { 31 | if (access && child.type.name === 'CellFooter') { 32 | return React.cloneElement(child, { access: true }) 33 | } 34 | if (error && (child.type.name === 'CellHeader' || child.type.name === 'CellBody')) { 35 | return React.cloneElement(child, { error: true }) 36 | } 37 | return child 38 | }) 39 | 40 | return ( 41 | 42 | {childrenWithProps} 51 | 52 | ) 53 | } 54 | 55 | Cell.propTypes = { 56 | first: PropTypes.bool, 57 | access: PropTypes.bool, 58 | vcode: PropTypes.bool, 59 | error: PropTypes.bool, 60 | disabled: PropTypes.bool, 61 | children: PropTypes.node, 62 | style: View.propTypes.style, 63 | } 64 | 65 | export default Cell 66 | -------------------------------------------------------------------------------- /src/Toast/Toast.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { Modal, View, Text, StyleSheet, ActivityIndicator } from 'react-native' 3 | import { Mask } from '../Mask' 4 | import { Icon } from '../Icon' 5 | import V from '../variable' 6 | 7 | const styles = StyleSheet.create({ 8 | toastWrapper: { 9 | flexDirection: 'row', 10 | alignItems: 'center', 11 | justifyContent: 'center', 12 | }, 13 | toast: { 14 | width: V.baseFontSize * 7.6, 15 | height: V.baseFontSize * 7.6, 16 | backgroundColor: 'rgba(40, 40, 40, 0.75)', 17 | borderRadius: 5, 18 | }, 19 | toastIcon: { 20 | marginTop: 22, 21 | color: '#fff', 22 | fontSize: 55, 23 | textAlign: 'center', 24 | }, 25 | toastContent: { 26 | marginBottom: 15, 27 | color: '#fff', 28 | textAlign: 'center', 29 | }, 30 | toastLoading: { 31 | marginTop: 30, 32 | marginBottom: 15 33 | } 34 | }) 35 | 36 | const Toast = ({ 37 | icon = 'toast', 38 | show = false, 39 | onShow, 40 | onClose, 41 | style, 42 | maskStyle, 43 | textStyle, 44 | children 45 | }) => 46 | 53 | 54 | 55 | {icon === 'loading' 56 | ? 57 | : } 58 | {children} 59 | 60 | 61 | 62 | 63 | Toast.propTypes = { 64 | icon: PropTypes.string, 65 | show: PropTypes.bool, 66 | onShow: PropTypes.func, 67 | onClose: PropTypes.func, 68 | maskStyle: View.propTypes.style, 69 | style: View.propTypes.style, 70 | textStyle: Text.propTypes.style, 71 | children: PropTypes.node 72 | } 73 | 74 | export default Toast 75 | -------------------------------------------------------------------------------- /src/Progress/Progress.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { View, TouchableOpacity, StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | import V from '../variable' 5 | 6 | const styles = StyleSheet.create({ 7 | progress: { 8 | flexDirection: 'row', 9 | alignItems: 'center', 10 | }, 11 | progressBar: { 12 | backgroundColor: V.weuiProgressBg, 13 | height: V.weuiProgressHeight, 14 | flex: 1, 15 | }, 16 | progressInnerBar: { 17 | width: 0, 18 | height: V.weuiProgressHeight, 19 | backgroundColor: V.weuiProgressColor, 20 | }, 21 | progressOpr: { 22 | marginLeft: 15, 23 | } 24 | }) 25 | 26 | class Progress extends Component { 27 | constructor(props) { 28 | super(props) 29 | this.state = { 30 | base: 1 31 | } 32 | this.onLayout = this.onLayout.bind(this) 33 | } 34 | 35 | onLayout() { 36 | this.progressBar.measure((x, y, width) => { 37 | this.setState({ 38 | base: width / 100 39 | }) 40 | }) 41 | } 42 | 43 | _renderOpr = (onCancel) => { 44 | if (!onCancel) return null 45 | return ( 46 | 47 | 48 | 49 | ) 50 | } 51 | 52 | render() { 53 | const { 54 | onCancel, 55 | style 56 | } = this.props 57 | 58 | let { value = 0 } = this.props 59 | if (value < 0) value = 0 60 | if (value > 100) value = 100 61 | 62 | return ( 63 | 64 | { this.progressBar = ref }} 67 | onLayout={this.onLayout} 68 | > 69 | 72 | 73 | {this._renderOpr(onCancel)} 74 | 75 | ) 76 | } 77 | } 78 | 79 | Progress.propTypes = { 80 | value: PropTypes.number, 81 | onCancel: PropTypes.func, 82 | style: View.propTypes.style, 83 | } 84 | 85 | export default Progress 86 | -------------------------------------------------------------------------------- /example/page/Toast.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { View } from 'react-native' 3 | import NavigationBar from 'react-native-navbar' 4 | import { Button, ButtonArea, Toast } from '../../src' 5 | import $SK from 'react-native-stylekit' 6 | 7 | class ToastScene extends Component { 8 | constructor(props) { 9 | super(props) 10 | this.state = { 11 | visibleToast: false, 12 | visibleLoading: false, 13 | toastTimer: null, 14 | loadingTimer: null 15 | } 16 | this.showToast = this.showToast.bind(this) 17 | this.showLoadingToast = this.showLoadingToast.bind(this) 18 | } 19 | 20 | componentWillUnmount() { 21 | if (this.state.toastTimer) clearTimeout(this.state.toastTimer) 22 | if (this.state.loadingTimer) clearTimeout(this.state.loadingTimer) 23 | } 24 | 25 | showToast() { 26 | this.setState({ visibleToast: true }) 27 | this.state.toastTimer = setTimeout(() => { 28 | this.setState({ visibleToast: false }) 29 | }, 2000) 30 | } 31 | 32 | showLoadingToast() { 33 | this.setState({ visibleLoading: true }) 34 | this.state.loadingTimer = setTimeout(() => { 35 | this.setState({ visibleLoading: false }) 36 | }, 2000) 37 | } 38 | 39 | render() { 40 | const _self = this 41 | const NavigationBarProps = { 42 | leftButton: { 43 | title: '返回', 44 | handler() { 45 | _self.props.navigator.pop() 46 | }, 47 | }, 48 | title: { 49 | title: 'Toast', 50 | } 51 | } 52 | return ( 53 | 54 | 55 | 56 | 57 | 61 | 62 | 加载成功 63 | 加载中... 64 | 65 | ) 66 | } 67 | } 68 | 69 | export default ToastScene 70 | -------------------------------------------------------------------------------- /example/page/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { 3 | Image, 4 | Text 5 | } from 'react-native' 6 | import Page from '../components/Page' 7 | import { 8 | SearchBar, 9 | Panel, 10 | PanelHeader, 11 | PanelBody, 12 | PanelFooter, 13 | Media, 14 | MediaHeader, 15 | MediaBody, 16 | MediaTitle, 17 | MediaDescription, 18 | } from '../../src' 19 | import SampleData from './nameDB' 20 | 21 | const appMsgIcon = 22 | 23 | 24 | class SearchBarScene extends Component { 25 | constructor(props) { 26 | super(props) 27 | this.state = { 28 | searchText: '', 29 | results: [], 30 | } 31 | this.handleChange = this.handleChange.bind(this) 32 | } 33 | 34 | handleChange(text) { 35 | const keywords = [text] 36 | let results = SampleData.filter(/./.test.bind(new RegExp(keywords.join('|'), 'i'))) 37 | 38 | if (results.length > 3) results = results.slice(0, 3) 39 | this.setState({ 40 | results, 41 | searchText: text, 42 | }) 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 | 49 | {!this.state.searchText ? null : 50 | 51 | 英文名字 52 | 53 | {this.state.results.length > 0 ? 54 | this.state.results.map((item, i) => 55 | 56 | {appMsgIcon} 57 | 58 | {item} 59 | 60 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 61 | 62 | 63 | 64 | ) : 找不到了! 65 | } 66 | 67 | 查看更多 68 | 69 | } 70 | 71 | ) 72 | } 73 | } 74 | 75 | 76 | export default SearchBarScene 77 | -------------------------------------------------------------------------------- /example/page/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, ScrollView } from 'react-native' 3 | import NavigationBar from 'react-native-navbar' 4 | import { ButtonArea, Button } from '../../src' 5 | import S from 'react-native-stylekit' 6 | 7 | const ButtonScene = ({ navigator }) => { 8 | const NavigationBarProps = { 9 | leftButton: { 10 | title: '返回', 11 | handler() { 12 | navigator.pop() 13 | }, 14 | }, 15 | title: { 16 | title: 'Button', 17 | } 18 | } 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 | export default ButtonScene 55 | -------------------------------------------------------------------------------- /src/LoadMore/LoadMore.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text, ActivityIndicator, Dimensions, StyleSheet } from 'react-native' 3 | import { create } from '../StyleSheet' 4 | import V from '../variable' 5 | 6 | const { width } = Dimensions.get('window') 7 | const styles = create({ 8 | loadMore: { 9 | width: width * 0.65, 10 | marginTop: 14 * 1.5, 11 | marginBottom: 14 * 1.5, 12 | marginLeft: width * 0.175, 13 | marginRight: width * 0.175, 14 | flexDirection: 'row', 15 | alignItems: 'center', 16 | justifyContent: 'center' 17 | }, 18 | loadMoreLine: { 19 | borderTopWidth: StyleSheet.hairlineWidth, 20 | borderColor: V.weuiLineColorLight, 21 | marginTop: 14 * 2.4, 22 | }, 23 | loadMoreTips: { 24 | fontSize: 14, 25 | textAlign: 'center', 26 | lineHeight: 14 * 1.6, 27 | android: { 28 | lineHeight: Math.round(14 * 1.6), 29 | }, 30 | }, 31 | loadMoreLineTips: { 32 | position: 'relative', 33 | top: 0 - (14 * 0.9), 34 | paddingLeft: 14 * 0.55, 35 | paddingRight: 14 * 0.55, 36 | color: V.weuiTextColorGray, 37 | }, 38 | loadMoreDotTips: { 39 | paddingLeft: 14 * 0.16, 40 | paddingRight: 14 * 0.16, 41 | top: 0 - (14 * 1.2), 42 | }, 43 | dot: { 44 | width: 4, 45 | height: 4, 46 | backgroundColor: V.weuiLineColorLight, 47 | borderRadius: 12 48 | } 49 | }) 50 | 51 | const LoadMore = ({ loading, showLine, showDot, style, textStyle, children, ...others }) => 52 | 61 | {loading ? : false} 62 | 70 | {showDot ? : false} 71 | {children} 72 | 73 | 74 | 75 | LoadMore.propTypes = { 76 | loading: PropTypes.bool, 77 | showLine: PropTypes.bool, 78 | showDot: PropTypes.bool, 79 | style: View.propTypes.style, 80 | textStyle: Text.propTypes.style, 81 | children: PropTypes.node 82 | } 83 | 84 | export default LoadMore 85 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import StyleSheet from './StyleSheet' 2 | 3 | // 0.4.x 4 | import { Grids, Grid, GridIcon, GridLabel } from './Grid' 5 | import { Button, ButtonArea, ButtonPreview } from './Button' 6 | import { 7 | Cells, CellsTitle, CellsTips, Cell, CellHeader, CellBody, CellFooter, CellText, 8 | } from './Cell' 9 | import { 10 | TextArea, Input, Switch, RadioCells, CheckboxCells, Uploader, Label, Select, Slider, Agreement 11 | } from './Form' 12 | import { Icon } from './Icon' 13 | import { Toast } from './Toast' 14 | import { Msg } from './Msg' 15 | import { Article, Section, H1, H2, H3, P } from './Article' 16 | import { 17 | Panel, PanelHeader, PanelBody, PanelFooter, 18 | } from './Panel' 19 | import { 20 | Media, MediaHeader, MediaBody, MediaTitle, MediaDescription, MediaInfo, MediaInfoMeta, 21 | } from './Media' 22 | import { Progress } from './Progress' 23 | import { TabBar, NavBar } from './Tab' 24 | import { Dialog } from './Dialog' 25 | import { ActionSheet } from './ActionSheet' 26 | import { SearchBar } from './SearchBar' 27 | 28 | // 1.0 29 | import { Mask } from './Mask' 30 | import { Badge } from './Badge' 31 | import { Flex, FlexItem } from './Flex' 32 | import { LoadMore } from './LoadMore' 33 | import { Popup, PopupHeader } from './Popup' 34 | import { Picker, PickerSection } from './Picker' 35 | import { Gallery, GalleryDelete } from './Gallery' 36 | import { Preview, PreviewHeader, PreviewBody, PreviewFooter, PreviewItem, PreviewLabel, PreviewValue } from './Preview' 37 | import { Toptips } from './Toptips' 38 | 39 | export { 40 | StyleSheet, 41 | Button, ButtonArea, ButtonPreview, 42 | Grids, Grid, GridIcon, GridLabel, 43 | Cells, CellsTitle, CellsTips, Cell, CellHeader, CellBody, CellFooter, CellText, 44 | TextArea, Input, Switch, RadioCells, CheckboxCells, Uploader, Label, Select, Slider, Agreement, 45 | Icon, 46 | Toast, 47 | Msg, 48 | Article, Section, H1, H2, H3, P, 49 | Media, MediaHeader, MediaBody, MediaTitle, MediaDescription, MediaInfo, MediaInfoMeta, 50 | Panel, PanelHeader, PanelBody, PanelFooter, 51 | Progress, 52 | TabBar, NavBar, 53 | Dialog, 54 | ActionSheet, 55 | SearchBar, 56 | Mask, 57 | Badge, 58 | Flex, FlexItem, 59 | LoadMore, 60 | Popup, PopupHeader, 61 | Picker, PickerSection, 62 | Gallery, GalleryDelete, 63 | Preview, PreviewHeader, PreviewBody, PreviewFooter, PreviewItem, PreviewLabel, 64 | PreviewValue, 65 | Toptips 66 | } 67 | -------------------------------------------------------------------------------- /src/Button/ButtonText.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, Text } from 'react-native' 3 | import V from '../variable' 4 | 5 | const styles = StyleSheet.create({ 6 | text: { 7 | fontSize: V.weuiBtnFontSize, 8 | textAlign: 'center', 9 | marginTop: (V.weuiBtnHeight - V.weuiBtnFontSize) / 2, 10 | marginBottom: (V.weuiBtnHeight - V.weuiBtnFontSize) / 2, 11 | }, 12 | 13 | miniText: { 14 | fontSize: V.weuiBtnMiniFontSize, 15 | marginTop: ((V.weuiBtnMiniHeight * V.weuiBtnMiniFontSize) - V.weuiBtnMiniFontSize) / 2, 16 | marginBottom: ((V.weuiBtnMiniHeight * V.weuiBtnMiniFontSize) - V.weuiBtnMiniFontSize) / 2, 17 | }, 18 | 19 | // primary 20 | primaryText: { 21 | color: V.weuiBtnFontColor, 22 | }, 23 | 24 | // warn 25 | warnText: { 26 | color: V.weuiBtnFontColor, 27 | }, 28 | 29 | // default 30 | defaultText: { 31 | color: V.weuiBtnDefaultFontColor, 32 | }, 33 | 34 | // primaryPlain 35 | primaryPlainText: { 36 | color: V.weuiBtnPlainPrimaryColor, 37 | }, 38 | 39 | // defaultPlain 40 | defaultPlainText: { 41 | color: V.weuiBtnPlainDefaultColor, 42 | }, 43 | 44 | // disabled 45 | disabledText: { 46 | color: V.weuiBtnDisabledFontColor 47 | }, 48 | 49 | defaultDisabledText: { 50 | color: V.weuiBtnDefaultDisabledFontColor 51 | }, 52 | 53 | plainDisabledText: { 54 | color: 'rgba(0,0,0,.2)' 55 | } 56 | }) 57 | 58 | const getTextStyles = ({ type, plain, size, disabled }) => { 59 | const config = [styles[`${type}Text`]] 60 | if (plain) config.push(styles[`${type}PlainText`]) 61 | if (size === 'small') config.push(styles.miniText) 62 | if (disabled) { 63 | if (type === 'default') { 64 | config.push(styles.defaultDisabledText) 65 | } else { 66 | config.push(styles.disabledText) 67 | } 68 | } 69 | if (disabled && plain) config.push(styles.plainDisabledText) 70 | return config 71 | } 72 | 73 | const ButtonText = ({ disabled = false, type = 'default', size, plain = false, style, children, }) => { 74 | const textStyles = getTextStyles({ type, plain, size, disabled }) 75 | 76 | return {children} 77 | } 78 | 79 | ButtonText.propTypes = { 80 | type: PropTypes.oneOf(['default', 'primary', 'warn']), 81 | size: PropTypes.oneOf(['small']), 82 | plain: PropTypes.bool, 83 | disabled: PropTypes.bool, 84 | style: Text.propTypes.style, 85 | children: PropTypes.node, 86 | } 87 | 88 | export default ButtonText 89 | -------------------------------------------------------------------------------- /src/Popup/Popup.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Modal, View, StyleSheet, Dimensions, Animated, Easing } from 'react-native' 3 | import { Mask } from '../Mask' 4 | 5 | const { width, height } = Dimensions.get('window') 6 | const styles = StyleSheet.create({ 7 | popup: { 8 | position: 'absolute', 9 | bottom: 0, 10 | left: 0, 11 | width, 12 | backgroundColor: '#EFEFF4', 13 | } 14 | }) 15 | 16 | class Popup extends Component { 17 | constructor(props) { 18 | super(props) 19 | this.state = { visible: false, translateY: new Animated.Value(height) } 20 | this.handleLayout = this.handleLayout.bind(this) 21 | } 22 | 23 | componentWillReceiveProps(nextProp) { 24 | if (this.props.visible !== nextProp.visible) { 25 | if (nextProp.visible) { 26 | this.setState({ visible: true }) 27 | return 28 | } 29 | Animated.timing(this.state.translateY, { 30 | toValue: this.height, 31 | duration: 300, 32 | easing: Easing.easeInOut, 33 | }).start(() => this.setState({ visible: false })) 34 | } 35 | } 36 | 37 | handleLayout() { 38 | this.popup.measure((x, y, w, h) => { 39 | this.height = h 40 | this.setState({ translateY: new Animated.Value(h) }) 41 | Animated.timing(this.state.translateY, { 42 | toValue: 0, 43 | duration: 300, 44 | easing: Easing.easeInOut, 45 | }).start() 46 | }) 47 | } 48 | 49 | render() { 50 | const { 51 | style, 52 | maskStyle, 53 | onShow, 54 | onClose, 55 | children 56 | } = this.props 57 | 58 | return ( 59 | 65 | 66 | 71 | { this.popup = ref }} 73 | onLayout={this.handleLayout} 74 | >{children} 75 | 76 | 77 | 78 | ) 79 | } 80 | } 81 | 82 | Popup.propTypes = { 83 | visible: PropTypes.bool, 84 | onShow: PropTypes.func, 85 | onClose: PropTypes.func, 86 | style: View.propTypes.style, 87 | maskStyle: View.propTypes.style, 88 | children: PropTypes.node, 89 | } 90 | 91 | export default Popup 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rn-weui [![Build Status](https://travis-ci.org/maskzh/rn-weui.svg?branch=master)](https://travis-ci.org/maskzh/rn-weui) [![npm version](https://img.shields.io/npm/v/rn-weui.svg)](https://www.npmjs.org/package/rn-weui) 2 | 一套基于 [Weui](https://github.com/weui/weui) 的 React Native 组件库 3 | 4 | ### 目录结构 5 | 6 | ``` 7 | rn-weui 8 | ├── README.md 9 | ├── assets # 资源 10 | ├── docs # 文档说明 11 | ├── fonts # 字体 12 | ├── example # 示例代码 13 | ├── package.json # package.json 14 | ├── src # rn-weui 组件源码 15 | └── test # 测试文件 16 | ``` 17 | 18 | ### 安装 19 | ```shell 20 | # install 21 | npm i rn-weui --save 22 | 23 | # link 24 | rnpm link 25 | rnpm link react-native-image-picker 26 | ``` 27 | For iOS 10+, Add the NSPhotoLibraryUsageDescription, NSCameraUsageDescription, and NSMicrophoneUsageDescription (if allowing video) keys to your Info.plist with strings describing why your app needs these permissions 28 | 29 | ### 使用 30 | ```js 31 | import { Button } from 'rn-weui' 32 | export default () => 33 | 34 | ``` 35 | 36 | 50 | 51 | ### TODO 52 | - [x] Grid 53 | - [x] Button 54 | - [x] Toast 55 | - [x] Msg 56 | - [x] Article 57 | - [x] Icons 58 | - [x] Panel 59 | - [x] Progress 60 | - [x] Tab 61 | - [x] ActionSheet 62 | - [x] Dialog 63 | - [x] SearchBar 64 | - [x] Cell 65 | - [x] Slider 66 | - [x] Preview 67 | - [x] Mask 68 | - [x] Popup 69 | - [x] LoadMore 70 | - [x] Gallery 71 | - [x] Flex 72 | - [x] Badge 73 | - [x] Agreement 74 | - [x] Picker 75 | - [x] Select 76 | - [x] Toptips 77 | - [ ] example 78 | 79 | ### License 80 | The MIT License(http://opensource.org/licenses/MIT) 81 | -------------------------------------------------------------------------------- /src/Msg/Msg.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text } from 'react-native' 3 | import { Button } from '../Button' 4 | import { Icon } from '../Icon' 5 | import { create } from '../StyleSheet' 6 | import V from '../variable' 7 | 8 | const styles = create({ 9 | msg: { 10 | paddingTop: V.weuiMsgPaddingTop, 11 | }, 12 | iconArea: { 13 | marginBottom: V.weuiMsgIconGap, 14 | }, 15 | textArea: { 16 | marginBottom: V.weuiMsgTextGap, 17 | paddingLeft: 20, 18 | paddingRight: 20, 19 | }, 20 | msgTitle: { 21 | marginBottom: V.weuiMsgTitleGap, 22 | fontWeight: '400', 23 | fontSize: 20, 24 | textAlign: 'center', 25 | }, 26 | msgDesc: { 27 | fontSize: 14, 28 | color: V.weuiTextColorGray, 29 | textAlign: 'center', 30 | lineHeight: 14 * V.baseLineHeight, 31 | android: { 32 | lineHeight: Math.round(14 * V.baseLineHeight), 33 | }, 34 | }, 35 | oprArea: { 36 | marginTop: 0 - V.weuiBtnDefaultGap, 37 | marginBottom: V.weuiMsgOprGap, 38 | paddingLeft: V.weuiBtnDefaultGap, 39 | paddingRight: V.weuiBtnDefaultGap, 40 | }, 41 | oprAreaButton: { 42 | marginTop: V.weuiBtnDefaultGap, 43 | }, 44 | extraArea: { 45 | marginBottom: V.weuiMsgExtraAreaGap, 46 | fontSize: 14, 47 | color: V.weuiTextColorGray, 48 | textAlign: 'center', 49 | lineHeight: 14 * V.baseLineHeight, 50 | android: { 51 | lineHeight: Math.round(14 * V.baseLineHeight), 52 | }, 53 | }, 54 | }) 55 | 56 | const _renderButtons = buttons => 57 | buttons.map((button, idx) => { 58 | const { type, label, style, ...others } = button 59 | return ( 60 | 66 | ) 67 | }) 68 | 69 | const Msg = ({ type = 'success', buttons = [], title, description, extraText }) => 70 | 71 | 72 | 73 | 74 | 75 | {title} 76 | {description} 77 | 78 | 79 | {_renderButtons(buttons)} 80 | 81 | {extraText} 82 | 83 | 84 | Msg.propTypes = { 85 | type: PropTypes.string, 86 | buttons: PropTypes.array, 87 | title: PropTypes.string, 88 | description: PropTypes.string, 89 | extraText: PropTypes.node, 90 | } 91 | 92 | export default Msg 93 | -------------------------------------------------------------------------------- /src/Icon/lib/icon-button.js: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash/isEqual'; 2 | import isString from 'lodash/isString'; 3 | import omit from 'lodash/omit'; 4 | import pick from 'lodash/pick'; 5 | 6 | import React, { 7 | Component, 8 | PropTypes, 9 | } from 'react'; 10 | 11 | import { 12 | StyleSheet, 13 | Text, 14 | TouchableHighlight, 15 | View, 16 | } from './react-native'; 17 | 18 | const styles = StyleSheet.create({ 19 | container: { 20 | flexDirection: 'row', 21 | justifyContent: 'flex-start', 22 | alignItems: 'center', 23 | padding: 8, 24 | }, 25 | touchable: { 26 | overflow: 'hidden', 27 | }, 28 | icon: { 29 | marginRight: 10, 30 | }, 31 | text: { 32 | fontWeight: '600', 33 | backgroundColor: 'transparent', 34 | }, 35 | }); 36 | 37 | const IOS7_BLUE = '#007AFF'; 38 | 39 | export default function createIconButtonComponent(Icon) { 40 | return class IconButton extends Component { 41 | static propTypes = { 42 | backgroundColor: PropTypes.string, 43 | borderRadius: PropTypes.number, 44 | color: PropTypes.string, 45 | size: PropTypes.number, 46 | }; 47 | 48 | static defaultProps = { 49 | backgroundColor: IOS7_BLUE, 50 | borderRadius: 5, 51 | color: 'white', 52 | size: 20, 53 | }; 54 | 55 | render() { 56 | let { 57 | style, 58 | iconStyle, 59 | children, 60 | ...props, 61 | } = this.props; 62 | 63 | let iconProps = pick(props, Object.keys(Text.propTypes), 'style', 'name', 'size', 'color'); 64 | const touchableProps = pick(props, Object.keys(TouchableHighlight.propTypes)); 65 | props = omit( 66 | props, 67 | Object.keys(iconProps), 68 | Object.keys(touchableProps), 69 | 'iconStyle', 70 | 'borderRadius', 71 | 'backgroundColor' 72 | ); 73 | iconProps.style = (this.props.iconStyle ? [styles.icon, this.props.iconStyle] : styles.icon); 74 | 75 | const colorStyle = pick(this.props, 'color'); 76 | const blockStyle = pick(this.props, 'backgroundColor', 'borderRadius'); 77 | 78 | if (isString(children)) { 79 | children = ({children}); 80 | } 81 | 82 | return ( 83 | 84 | 88 | 89 | {children} 90 | 91 | 92 | ); 93 | } 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /src/Icon/Icon.js: -------------------------------------------------------------------------------- 1 | // Usage: 2 | 3 | import React, { PropTypes } from 'react' 4 | import { Text, StyleSheet } from 'react-native' 5 | import WeuiIcon from './Weui' 6 | 7 | const styles = StyleSheet.create({ 8 | icon: { 9 | textAlign: 'center', 10 | alignSelf: 'center' 11 | } 12 | }) 13 | 14 | const glyphMap = { 15 | success: { 16 | size: 23, 17 | color: '#09BB07' 18 | }, 19 | waiting: { 20 | size: 23, 21 | color: '#10AEFF' 22 | }, 23 | warn: { 24 | size: 23, 25 | color: '#F43530' 26 | }, 27 | info: { 28 | size: 23, 29 | color: '#10AEFF' 30 | }, 31 | 32 | success_circle: { 33 | size: 23, 34 | color: '#09BB07' 35 | }, 36 | success_no_circle: { 37 | size: 23, 38 | color: '#09BB07' 39 | }, 40 | waiting_circle: { 41 | size: 23, 42 | color: '#10AEFF' 43 | }, 44 | circle: { 45 | size: 23, 46 | color: '#C9C9C9' 47 | }, 48 | download: { 49 | size: 23, 50 | color: '#09BB07' 51 | }, 52 | 53 | info_circle: { 54 | size: 23, 55 | color: '#09BB07' 56 | }, 57 | 58 | safe_success: { 59 | color: '#09BB07' 60 | }, 61 | safe_warn: { 62 | color: '#FFBE00' 63 | }, 64 | cancel: { 65 | size: 22, 66 | color: '#F43530' 67 | }, 68 | back: { 69 | size: 22, 70 | color: '#fff' 71 | }, 72 | delete: { 73 | size: 22, 74 | color: '#fff' 75 | }, 76 | 77 | search: { 78 | size: 14, 79 | color: '#B2B2B2' 80 | }, 81 | 82 | clear: { 83 | size: 14, 84 | color: '#B2B2B2' 85 | }, 86 | msg: { 87 | size: 93, 88 | warn: { 89 | color: '#F76260' 90 | }, 91 | }, 92 | safe: { 93 | size: 93, 94 | }, 95 | } 96 | 97 | const Icon = ({ 98 | name = 'success', 99 | msg = false, 100 | size = msg ? glyphMap.msg.size : glyphMap[name].size, 101 | color = glyphMap[name].color, 102 | style, 103 | ...others 104 | }) => 105 | 112 | 113 | Icon.propTypes = { 114 | msg: PropTypes.bool, 115 | name: PropTypes.oneOf(['success', 'waiting', 'warn', 'info', 'success_circle', 116 | 'success_no_circle', 'waiting_circle', 'circle', 'download', 117 | 'info_circle', 'safe_success', 'safe_warn', 'cancel', 'back', 'delete', 118 | 'search', 'clear' 119 | ]), 120 | size: PropTypes.number, 121 | color: PropTypes.string, 122 | style: Text.propTypes.style, 123 | } 124 | 125 | export default Icon 126 | -------------------------------------------------------------------------------- /src/Gallery/Gallery.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Modal, View, StyleSheet, Dimensions, StatusBar, ActivityIndicator } from 'react-native' 3 | import PhotoView from 'react-native-photo-view' 4 | import { Mask } from '../Mask' 5 | 6 | const { width, height } = Dimensions.get('window') 7 | const styles = StyleSheet.create({ 8 | galleryWrapper: { 9 | backgroundColor: '#000' 10 | }, 11 | gallery: { 12 | width, 13 | height, 14 | resizeMode: 'contain' 15 | }, 16 | loading: { 17 | position: 'absolute', 18 | width, 19 | height, 20 | alignItems: 'center', 21 | justifyContent: 'center' 22 | }, 23 | operation: { 24 | position: 'absolute', 25 | bottom: 0, 26 | left: 0, 27 | right: 0, 28 | backgroundColor: '#0D0D0D', 29 | }, 30 | }) 31 | 32 | class Gallery extends Component { 33 | constructor(props) { 34 | super(props) 35 | this.state = { laoding: false } 36 | } 37 | 38 | render() { 39 | const { 40 | visible, 41 | source, 42 | onShow, 43 | onClose, 44 | onPress, 45 | maskStyle, 46 | style, 47 | oprStyle, 48 | minimumZoomScale = 0.5, 49 | children, 50 | ...others 51 | } = this.props 52 | 53 | return ( 54 | 60 | 81 | ) 82 | } 83 | } 84 | 85 | Gallery.propTypes = { 86 | visible: PropTypes.bool, 87 | onShow: PropTypes.func, 88 | onClose: PropTypes.func, 89 | onPress: PropTypes.func, 90 | maskStyle: View.propTypes.style, 91 | style: View.propTypes.style, 92 | oprStyle: View.propTypes.style, 93 | source: PropTypes.object, 94 | minimumZoomScale: PropTypes.number, 95 | children: PropTypes.node, 96 | } 97 | 98 | export default Gallery 99 | -------------------------------------------------------------------------------- /src/Icon/lib/toolbar-android.js: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash/isEqual'; 2 | import pick from 'lodash/pick'; 3 | 4 | import React, { 5 | Component, 6 | PropTypes, 7 | } from 'react'; 8 | 9 | import { 10 | ToolbarAndroid, 11 | } from './react-native'; 12 | 13 | export default function createToolbarAndroidComponent(IconNamePropType, getImageSource) { 14 | return class IconToolbarAndroid extends Component { 15 | static propTypes = { 16 | navIconName: IconNamePropType, 17 | overflowIconName: IconNamePropType, 18 | actions: PropTypes.arrayOf(PropTypes.shape({ 19 | title: PropTypes.string.isRequired, 20 | iconName: IconNamePropType, 21 | iconSize: PropTypes.number, 22 | iconColor: PropTypes.string, 23 | show: PropTypes.oneOf(['always', 'ifRoom', 'never']), 24 | showWithText: PropTypes.bool, 25 | })), 26 | iconSize: PropTypes.number, 27 | iconColor: PropTypes.string, 28 | }; 29 | 30 | static defaultProps = { 31 | iconSize: 24, 32 | }; 33 | 34 | updateIconSources(props) { 35 | const size = props.iconSize; 36 | const color = props.iconColor || props.titleColor; 37 | if (props.navIconName) { 38 | getImageSource(props.navIconName, size, color).then(navIcon => this.setState({ navIcon })); 39 | } 40 | if (props.overflowIconName) { 41 | getImageSource(props.overflowIconName, size, color).then(overflowIcon => this.setState({ overflowIcon })); 42 | } 43 | 44 | Promise.all((props.actions || []).map(action => { 45 | if (action.iconName) { 46 | return getImageSource(action.iconName, action.iconSize || size, action.iconColor || color).then(icon => ({ 47 | ...action, 48 | icon, 49 | })); 50 | } 51 | return Promise.resolve(action); 52 | })).then(actions => this.setState({ actions })); 53 | } 54 | 55 | componentWillMount() { 56 | this.updateIconSources(this.props); 57 | } 58 | 59 | componentWillReceiveProps(nextProps) { 60 | const keys = Object.keys(IconToolbarAndroid.propTypes); 61 | if (!isEqual(pick(nextProps, keys), pick(this.props, keys))) { 62 | let stateToEvict = {}; 63 | if (!nextProps.navIconName) { 64 | stateToEvict.navIcon = undefined; 65 | } 66 | if (!nextProps.iconName) { 67 | stateToEvict.icon = undefined; 68 | } 69 | if (this.state && Object.keys(stateToEvict).length) { 70 | this.setState(stateToEvict, () => this.updateIconSources(nextProps)); 71 | } else { 72 | this.updateIconSources(nextProps); 73 | } 74 | } 75 | } 76 | 77 | render() { 78 | return ; 79 | } 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /example/page/Dialog.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { 3 | StyleSheet, 4 | View, 5 | Text, 6 | } from 'react-native' 7 | import { ButtonArea, Button, Dialog } from '../../src' 8 | import S from 'react-native-stylekit' 9 | 10 | const styles = StyleSheet.create({ 11 | wrapper: { 12 | flex: 1, 13 | paddingTop: 204, 14 | backgroundColor: '#fbf9fe', 15 | }, 16 | }) 17 | 18 | class DialogScene extends Component { 19 | constructor(props) { 20 | super(props) 21 | this.state = { 22 | visible1: false, 23 | visible2: false, 24 | } 25 | this.showDialog1 = this.showDialog1.bind(this) 26 | this.showDialog2 = this.showDialog2.bind(this) 27 | this.hideDialog1 = this.hideDialog1.bind(this) 28 | this.hideDialog2 = this.hideDialog2.bind(this) 29 | this.doneDialog2 = this.doneDialog2.bind(this) 30 | } 31 | showDialog1() { 32 | this.setState({ 33 | visible1: true, 34 | }) 35 | } 36 | showDialog2() { 37 | this.setState({ 38 | visible2: true, 39 | }) 40 | } 41 | hideDialog1() { 42 | this.setState({ 43 | visible1: false, 44 | }) 45 | } 46 | hideDialog2() { 47 | this.setState({ 48 | visible2: false, 49 | }) 50 | } 51 | doneDialog2() { 52 | this.setState({ 53 | visible2: false, 54 | }) 55 | console.log('1') 56 | } 57 | render() { 58 | return ( 59 | 60 | 61 | 65 | 66 | 67 | 警告内容 79 | 呵呵 97 | 98 | ) 99 | } 100 | } 101 | 102 | export default DialogScene 103 | -------------------------------------------------------------------------------- /src/Form/Select.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { Text, View, StyleSheet } from 'react-native' 3 | import { Picker } from '../Picker' 4 | import V from '../variable' 5 | 6 | const styles = StyleSheet.create({ 7 | text: { 8 | fontSize: V.weuiCellFontSize, 9 | marginTop: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 10 | marginBottom: (V.weuiCellLineHeight - V.weuiCellFontSize) / 2, 11 | }, 12 | placeholder: { 13 | color: V.weuiTextColorGray 14 | } 15 | }) 16 | 17 | const valueToOptions = (value, options) => { 18 | let tmp = options 19 | return value.map((val, idx) => { 20 | if (idx === 0) return options 21 | tmp = (tmp.filter(option => option.value === value[idx - 1])[0] || tmp[0] || {}).children || [] 22 | return tmp 23 | }) 24 | } 25 | 26 | const getLabel = (value, options) => { 27 | const cascade = options.filter(item => item.children).length 28 | const _options = cascade ? valueToOptions(value, options) : options 29 | 30 | if (cascade) { 31 | return value.map((val, idx) => 32 | (_options[idx].filter(option => option.value === val)[0] || {}).label 33 | ).join(' ') 34 | } 35 | return value.join(' ') 36 | } 37 | 38 | const initLabel = (arg) => { 39 | let _value = arg.value 40 | 41 | if (!Array.isArray(arg.value)) _value = [arg.value] 42 | 43 | if (_value.length && _value.every(item => item)) return getLabel(_value, arg.options) 44 | 45 | return {arg.placeholder} 46 | } 47 | 48 | class Select extends Component { 49 | constructor(props) { 50 | super(props) 51 | this.state = { 52 | visible: false, 53 | label: initLabel(props) 54 | } 55 | this.onChange = this.onChange.bind(this) 56 | this.onClose = this.onClose.bind(this) 57 | } 58 | 59 | componentWillReceiveProps(nextProps) { 60 | this.setState({ label: initLabel(nextProps) }) 61 | } 62 | 63 | onChange(value) { 64 | if (this.props.onChange) this.props.onChange(value) 65 | this.onClose() 66 | } 67 | 68 | onClose() { 69 | this.setState({ visible: false }) 70 | } 71 | 72 | render() { 73 | const { value, options, pickerStyle, style, ...others } = this.props 74 | 75 | return ( 76 | 77 | this.setState({ visible: true })} 80 | >{this.state.label} 81 | 90 | 91 | ) 92 | } 93 | } 94 | 95 | Select.propTypes = { 96 | value: PropTypes.any, 97 | options: PropTypes.array, 98 | onChange: PropTypes.func, 99 | style: Text.propTypes.style, 100 | pickerStyle: View.propTypes.style, 101 | placeholder: PropTypes.string, 102 | } 103 | 104 | export default Select 105 | -------------------------------------------------------------------------------- /src/Toptips/Toptips.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { StyleSheet, Modal, Dimensions, Animated, View, Text, StatusBar, Easing } from 'react-native' 3 | import V from '../variable' 4 | 5 | const { width, height } = Dimensions.get('window') 6 | const styles = StyleSheet.create({ 7 | toptips: { 8 | position: 'absolute', 9 | top: 0, 10 | left: 0, 11 | width, 12 | padding: 5, 13 | zIndex: 5000, 14 | }, 15 | primaryToptips: { 16 | backgroundColor: V.weuiColorPrimary, 17 | }, 18 | warnToptips: { 19 | backgroundColor: V.weuiColorWarn, 20 | }, 21 | infoToptips: { 22 | backgroundColor: V.weuiLinkColorDefault, 23 | }, 24 | toptipsText: { 25 | fontSize: 14, 26 | textAlign: 'center', 27 | color: '#FFFFFF', 28 | } 29 | }) 30 | 31 | class Toptips extends Component { 32 | constructor(props) { 33 | super(props) 34 | this.state = { visible: false, translateY: new Animated.Value(-height) } 35 | this.handleLayout = this.handleLayout.bind(this) 36 | } 37 | 38 | componentWillReceiveProps(nextProp) { 39 | if (this.props.visible !== nextProp.visible) { 40 | if (nextProp.visible) { 41 | this.setState({ visible: true }) 42 | return 43 | } 44 | Animated.timing(this.state.translateY, { 45 | toValue: -this.height, 46 | duration: 150, 47 | easing: Easing.easeInOut, 48 | }).start(() => this.setState({ visible: false })) 49 | } 50 | } 51 | 52 | handleLayout() { 53 | this.toptips.measure((x, y, w, h) => { 54 | this.height = h 55 | this.setState({ translateY: new Animated.Value(-h) }) 56 | Animated.timing(this.state.translateY, { 57 | toValue: 0, 58 | duration: 150, 59 | easing: Easing.easeInOut, 60 | }).start() 61 | }) 62 | } 63 | 64 | render() { 65 | const { type = 'primary', onShow, onClose, style, textStyle, children } = this.props 66 | 67 | return ( 68 | 74 | 88 | ) 89 | } 90 | } 91 | 92 | Toptips.propTypes = { 93 | type: PropTypes.oneOf(['primary', 'warn', 'info']), 94 | visible: PropTypes.bool, 95 | onShow: PropTypes.func, 96 | onClose: PropTypes.func, 97 | style: View.propTypes.style, 98 | textStyle: Text.propTypes.style, 99 | children: PropTypes.node, 100 | } 101 | 102 | export default Toptips 103 | -------------------------------------------------------------------------------- /example/page/ActionSheet.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { 3 | StyleSheet, 4 | View, 5 | } from 'react-native' 6 | import { ButtonArea, Button, ActionSheet, Popup, PopupHeader, Gallery, GalleryDelete } from '../../src' 7 | 8 | const styles = StyleSheet.create({ 9 | wrapper: { 10 | flex: 1, 11 | paddingTop: 204, 12 | backgroundColor: '#fbf9fe', 13 | }, 14 | }) 15 | 16 | class ActionSheetScene extends Component { 17 | constructor(props) { 18 | super(props) 19 | this.state = { 20 | visible: false, 21 | popupVisible: false, 22 | galleryVisible: false, 23 | } 24 | this.showActionSheet = this.showActionSheet.bind(this) 25 | this.hideActionSheet = this.hideActionSheet.bind(this) 26 | } 27 | showActionSheet() { 28 | this.setState({ 29 | visible: true, 30 | }) 31 | } 32 | hideActionSheet() { 33 | this.setState({ 34 | visible: false, 35 | }) 36 | } 37 | render() { 38 | return ( 39 | 40 | 41 | 45 | 46 | 47 | 51 | 52 | 53 | 57 | 58 | 82 | this.setState({ popupVisible: false })} 85 | > 86 | 87 | 88 | 92 | 93 | 94 | this.setState({ galleryVisible: false })} 98 | > console.log('1')} /> 99 | 100 | ) 101 | } 102 | } 103 | 104 | export default ActionSheetScene 105 | -------------------------------------------------------------------------------- /src/Picker/Picker.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { View, StyleSheet } from 'react-native' 3 | import { Popup, PopupHeader } from '../Popup' 4 | import PickerSection from './PickerSection' 5 | 6 | /* 7 | * value: ['1', '2', '3'] 8 | * options: 9 | * [ 10 | * { label: '北京', value: '1', children: [ 11 | * { label: '上海', value: '2', children: [ 12 | * { label: '天津', value: '3', children: [] } 13 | * ] } 14 | * ] }, 15 | * ] 16 | */ 17 | const valueToOptions = (value, options) => { 18 | let tmp = options 19 | return value.map((val, idx) => { 20 | if (idx === 0) return options 21 | tmp = (tmp.filter(option => option.value === value[idx - 1])[0] || tmp[0] || {}).children || [] 22 | return tmp 23 | }) 24 | } 25 | 26 | const styles = StyleSheet.create({ 27 | picker: { 28 | flex: 1, 29 | flexDirection: 'row', 30 | alignItems: 'center', 31 | } 32 | }) 33 | 34 | class Picker extends Component { 35 | constructor(props) { 36 | super(props) 37 | this.state = { value: props.value } 38 | } 39 | 40 | componentWillReceiveProps(nextProps) { 41 | this.setState({ value: nextProps.value }) 42 | } 43 | 44 | render() { 45 | const { 46 | visible = false, 47 | options = [], 48 | onChange, 49 | onClose, 50 | style, 51 | ...others 52 | } = this.props 53 | let { value } = this.state 54 | 55 | const cascade = options.filter(item => item.children).length 56 | 57 | const _options = cascade ? valueToOptions(value, options) : options 58 | 59 | const _onChange = (val, idx) => { 60 | let result = value.map((item, _idx) => { 61 | if (idx === _idx) return val 62 | return item 63 | }) 64 | 65 | if (cascade) { 66 | const __options = valueToOptions(result, options) 67 | result = value.map((item, _idx) => { 68 | if (_idx < idx) return item 69 | else if (_idx === idx) return val 70 | return __options[_idx][0] ? __options[_idx][0].value : '' 71 | }) 72 | } 73 | 74 | this.setState({ value: result }) 75 | } 76 | 77 | // HACK: 设置初始值 78 | if (!value[0]) { 79 | value = _options.map(item => (item[0] ? item[0].value : '')) 80 | } 81 | 82 | const headerProps = { 83 | left: { 84 | label: '取消', 85 | onPress() { 86 | onClose() 87 | } 88 | }, 89 | right: { 90 | label: '完成', 91 | onPress() { 92 | onChange(value) 93 | } 94 | } 95 | } 96 | 97 | return ( 98 | 103 | 104 | 105 | {_options.map((item, idx) => 106 | _onChange(val, idx)} 111 | /> 112 | )} 113 | 114 | 115 | ) 116 | } 117 | } 118 | 119 | Picker.propTypes = { 120 | visible: PropTypes.bool, 121 | value: PropTypes.arrayOf(PropTypes.string).isRequired, 122 | options: PropTypes.array, 123 | onChange: PropTypes.func, 124 | onClose: PropTypes.func, 125 | style: View.propTypes.style, 126 | } 127 | 128 | export default Picker 129 | -------------------------------------------------------------------------------- /example/page/Grid.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Image } from 'react-native' 3 | import NavigationBar from 'react-native-navbar' 4 | import { Grids } from '../../src' 5 | 6 | const GridScene = ({ navigator }) => { 7 | const grids = [{ 8 | icon: , 9 | label: 'Button', 10 | onPress() { return navigator.push({ to: 'Button', title: 'Button' }) } 11 | }, { 12 | icon: , 13 | label: 'Cell', 14 | onPress() { return navigator.push({ to: 'Cell', title: 'Cell' }) } 15 | }, { 16 | icon: , 17 | label: 'Toast', 18 | onPress() { return navigator.push({ to: 'Toast', title: 'Toast' }) } 19 | }, { 20 | icon: , 21 | label: 'Dialog', 22 | onPress() { return navigator.push({ to: 'Dialog', title: 'Dialog' }) } 23 | }, { 24 | icon: , 25 | label: 'Progress', 26 | onPress() { return navigator.push({ to: 'Progress', title: 'Progress' }) } 27 | }, { 28 | icon: , 29 | label: 'Msg', 30 | onPress() { return navigator.push({ to: 'Msg', title: 'Msg' }) } 31 | }, { 32 | icon: , 33 | label: 'Article', 34 | onPress() { return navigator.push({ to: 'Article', title: 'Article' }) } 35 | }, { 36 | icon: , 37 | label: 'ActionSheet', 38 | onPress() { return navigator.push({ to: 'ActionSheet', title: 'ActionSheet' }) } 39 | }, { 40 | icon: , 41 | label: 'Icons', 42 | onPress() { return navigator.push({ to: 'Icons', title: 'Icons' }) } 43 | }, { 44 | icon: , 45 | label: 'Panel', 46 | onPress() { return navigator.push({ to: 'Panel', title: 'Panel' }) } 47 | }, { 48 | icon: , 49 | label: 'Tab', 50 | onPress() { return navigator.push({ to: 'Tab', title: 'Tab' }) } 51 | }, { 52 | icon: , 53 | label: 'SearchBar', 54 | onPress() { return navigator.push({ to: 'SearchBar', title: 'SearchBar' }) } 55 | }, { 56 | icon: , 57 | label: 'Badge', 58 | onPress() { return navigator.push({ to: 'Badge', title: 'Badge' }) } 59 | }] 60 | 61 | const NavigationBarProps = { 62 | title: { 63 | title: 'Grid', 64 | } 65 | } 66 | 67 | return ( 68 | 69 | 70 | 71 | 72 | ) 73 | } 74 | 75 | GridScene.propTypes = { 76 | navigator: PropTypes.object, 77 | } 78 | 79 | export default GridScene 80 | -------------------------------------------------------------------------------- /src/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { StyleSheet, View, TouchableHighlight } from 'react-native' 3 | import ButtonText from './ButtonText' 4 | import V from '../variable' 5 | 6 | const styles = StyleSheet.create({ 7 | // global 8 | button: { 9 | borderRadius: V.weuiBtnBorderRadius, 10 | borderWidth: StyleSheet.hairlineWidth, 11 | paddingLeft: 14, 12 | paddingRight: 14, 13 | borderColor: 'rgba(0,0,0,0.2)', 14 | overflow: 'hidden', 15 | }, 16 | 17 | // mini 18 | mini: { 19 | paddingLeft: V.weuiBtnMiniFontSize * 0.75, 20 | paddingRight: V.weuiBtnMiniFontSize * 0.75, 21 | }, 22 | 23 | // primary 24 | primary: { 25 | backgroundColor: V.weuiBtnPrimaryBg, 26 | }, 27 | 28 | // warn 29 | warn: { 30 | backgroundColor: V.weuiBtnWarnBg, 31 | }, 32 | 33 | // default 34 | default: { 35 | backgroundColor: V.weuiBtnDefaultBg, 36 | }, 37 | 38 | // primaryPlain 39 | primaryPlain: { 40 | borderWidth: 1, 41 | borderStyle: 'solid', 42 | borderColor: V.weuiBtnPlainPrimaryBorderColor, 43 | backgroundColor: 'transparent' 44 | }, 45 | 46 | // defaultPlain 47 | defaultPlain: { 48 | borderWidth: 1, 49 | borderStyle: 'solid', 50 | borderColor: V.weuiBtnPlainDefaultBorderColor, 51 | backgroundColor: 'transparent' 52 | }, 53 | 54 | // disabled 55 | primaryDisabled: { 56 | backgroundColor: V.weuiBtnPrimaryDisabledBg 57 | }, 58 | warnDisabled: { 59 | backgroundColor: V.weuiBtnWarnDisabledBg 60 | }, 61 | defaultDisabled: { 62 | backgroundColor: V.weuiBtnDefaultDisabledBg 63 | }, 64 | plainDisabled: { 65 | borderColor: 'rgba(0,0,0,.2)', 66 | backgroundColor: 'transparent' 67 | } 68 | }) 69 | 70 | const underlayColors = { 71 | primaryActive: V.weuiBtnPrimaryActiveBg, 72 | warnActive: V.weuiBtnWarnActiveBg, 73 | defaultActive: V.weuiBtnDefaultActiveBg, 74 | primaryPlainActive: 'transparent', 75 | defaultPlainActive: 'transparent', 76 | } 77 | 78 | const getButtonStyles = ({ type, plain, size, disabled }) => { 79 | const config = [styles[type]] 80 | if (plain) config.push(styles[`${type}Plain`]) 81 | if (size === 'small') config.push(styles.mini) 82 | if (disabled) config.push(styles[`${type}Disabled`]) 83 | if (disabled && plain) config.push(styles.plainDisabled) 84 | return config 85 | } 86 | 87 | const getUnderlayColor = ({ type, plain }) => { 88 | if (plain) return underlayColors[`${type}PlainActive`] 89 | return underlayColors[`${type}Active`] 90 | } 91 | 92 | const Button = ({ 93 | disabled = false, 94 | type = 'default', 95 | size, 96 | plain = false, 97 | style, 98 | children, 99 | ...others 100 | }) => { 101 | const buttonStyles = getButtonStyles({ type, plain, size, disabled }) 102 | const underlayColor = getUnderlayColor({ type, plain }) 103 | 104 | let touchableProps = {} 105 | if (!disabled) touchableProps = others 106 | 107 | return ( 108 | 113 | 114 | {children} 115 | 116 | 117 | ) 118 | } 119 | 120 | Button.propTypes = { 121 | type: PropTypes.oneOf(['default', 'primary', 'warn']), 122 | size: PropTypes.oneOf(['small']), 123 | plain: PropTypes.bool, 124 | disabled: PropTypes.bool, 125 | onPress: PropTypes.func, 126 | onPressIn: PropTypes.func, 127 | onPressOut: PropTypes.func, 128 | onLongPress: PropTypes.func, 129 | style: View.propTypes.style, 130 | children: PropTypes.node, 131 | } 132 | 133 | export default Button 134 | -------------------------------------------------------------------------------- /example/page/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | StyleSheet, 4 | ScrollView, 5 | Image, 6 | } from 'react-native' 7 | import { 8 | Panel, 9 | PanelHeader, 10 | PanelBody, 11 | PanelFooter, 12 | Media, 13 | MediaHeader, 14 | MediaBody, 15 | MediaTitle, 16 | MediaDescription, 17 | MediaInfo, 18 | MediaInfoMeta, 19 | Cell, 20 | CellHeader, 21 | CellBody, 22 | CellFooter, 23 | CellText, 24 | Badge 25 | } from '../../src' 26 | 27 | const styles = StyleSheet.create({ 28 | wrapper: { 29 | flex: 1, 30 | marginTop: 64, 31 | backgroundColor: '#fbf9fe', 32 | } 33 | }) 34 | 35 | 36 | const PanelScene = () => 37 | 38 | 39 | 图文组合列表 40 | 41 | 42 | 43 | 44 | 8 45 | 46 | 47 | 标题一 48 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 标题一 57 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 58 | 59 | 60 | 61 | {}} access>查看更多 62 | 63 | 64 | 65 | 文字组合列表 66 | 67 | {}}> 68 | 标题一 69 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 70 | 71 | {}}> 72 | 标题一 73 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 74 | 75 | 76 | {}} access>查看更多 77 | 78 | 79 | 80 | 小图文组合列表 81 | 82 | {}}> 83 | 84 | 85 | 86 | 标题文字 87 | 88 | 89 | {}}> 90 | 91 | 92 | 93 | 标题文字 94 | 95 | 96 | 97 | 98 | 99 | 100 | 文字列表附来源 101 | 102 | 103 | 标题一 104 | 由各种物质组成的巨型球状天体,叫做星球。星球有一定的形状,有自己的运行轨道。 105 | 106 | 文字来源 107 | 时间 108 | 其他信息 109 | 110 | 111 | 112 | 113 | 114 | 115 | export default PanelScene 116 | -------------------------------------------------------------------------------- /example/page/Article.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | StyleSheet, 4 | View, 5 | } from 'react-native' 6 | import { 7 | Article, 8 | Section, 9 | H1, 10 | H2, 11 | H3, 12 | P, 13 | // Text 14 | LoadMore, 15 | Preview, 16 | PreviewHeader, 17 | PreviewBody, 18 | PreviewFooter, 19 | PreviewItem, 20 | PreviewLabel, 21 | PreviewValue, 22 | ButtonPreview, 23 | } from '../../src' 24 | 25 | 26 | const styles = StyleSheet.create({ 27 | wrapper: { 28 | flex: 1, 29 | backgroundColor: '#fbf9fe', 30 | }, 31 | }) 32 | 33 | 34 | const ArticleScene = () => 35 | 36 |
37 |

大标题

38 |
39 |

章标题

40 |
41 |

小标题

42 |

43 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, 44 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 45 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris 46 | nisi ut aliquip ex ea commodo consequat. Duis aute 47 |

48 |

49 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, 50 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 51 | Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris 52 | nisi ut aliquip ex ea commodo consequat. Duis aute 53 |

54 |
55 |
56 | Loading 57 | No Data 58 | 59 |
60 | 61 | 62 | 63 | Total 64 | $44.50 65 | 66 | 67 | 68 | 69 | Product 70 | Name 71 | 72 | 73 | Description 74 | Cras sit amet nibh libero, in gravida 75 | 76 | 77 | Details 78 | 79 | Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. 80 | 81 | 82 | 83 | 84 | Action 85 | 86 | 87 | 88 | 89 | 90 | Total 91 | $44.50 92 | 93 | 94 | 95 | 96 | Product 97 | Name 98 | 99 | 100 | Description 101 | Cras sit amet nibh libero, in gravida 102 | 103 | 104 | Details 105 | 106 | Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. 107 | 108 | 109 | 110 | 111 | Action 112 | Action 113 | 114 | 115 |
116 | 117 | export default ArticleScene 118 | -------------------------------------------------------------------------------- /src/Icon/lib/create-icon-set.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | PropTypes, 4 | } from 'react'; 5 | 6 | import { 7 | NativeModules, 8 | Platform, 9 | PixelRatio, 10 | processColor, 11 | requireNativeComponent, 12 | Text, 13 | } from './react-native'; 14 | 15 | const NativeIconAPI = NativeModules && (NativeModules.RNVectorIconsManager || NativeModules.RNVectorIconsModule); 16 | 17 | const DEFAULT_ICON_SIZE = 12; 18 | const DEFAULT_ICON_COLOR = 'black'; 19 | 20 | import createIconButtonComponent from './icon-button'; 21 | import createTabBarItemIOSComponent from './tab-bar-item-ios'; 22 | import createToolbarAndroidComponent from './toolbar-android'; 23 | 24 | export default function createIconSet(glyphMap, fontFamily, fontFile) { 25 | let fontReference = fontFamily; 26 | // Android doesn't care about actual fontFamily name, it will only look in fonts folder. 27 | if (Platform.OS === 'android' && fontFile) { 28 | fontReference = fontFile.replace(/\.(otf|ttf)$/, ''); 29 | } 30 | 31 | const IconNamePropType = PropTypes.oneOf(Object.keys(glyphMap)); 32 | 33 | class Icon extends Component { 34 | static propTypes = { 35 | name: IconNamePropType.isRequired, 36 | size: PropTypes.number, 37 | color: PropTypes.string, 38 | }; 39 | 40 | static defaultProps = { 41 | size: DEFAULT_ICON_SIZE, 42 | allowFontScaling: false, 43 | }; 44 | 45 | setNativeProps(nativeProps) { 46 | if (this._root) { 47 | this._root.setNativeProps(nativeProps); 48 | } 49 | } 50 | 51 | render() { 52 | const { name, size, color, style, ...props } = this.props; 53 | 54 | let glyph = glyphMap[name] || '?'; 55 | if (typeof glyph === 'number') { 56 | glyph = String.fromCharCode(glyph); 57 | } 58 | 59 | const styleDefaults = { 60 | fontSize: size, 61 | fontWeight: 'normal', 62 | fontStyle: 'normal', 63 | color, 64 | }; 65 | 66 | props.style = [styleDefaults, style]; 67 | props.ref = (component) => { 68 | this._root = component; 69 | }; 70 | 71 | styleDefaults.fontFamily = fontReference; 72 | 73 | return ({glyph}{this.props.children}); 74 | } 75 | } 76 | 77 | let imageSourceCache = {}; 78 | 79 | function getImageSource(name, size = DEFAULT_ICON_SIZE, color = DEFAULT_ICON_COLOR) { 80 | if (!NativeIconAPI) { 81 | if (Platform.OS === 'android') { 82 | throw new Error('RNVectorIconsModule not available, did you properly integrate the module?'); 83 | } 84 | throw new Error('RNVectorIconsManager not available, did you add the library to your project and link with libRNVectorIcons.a?'); 85 | } 86 | 87 | let glyph = glyphMap[name] || '?'; 88 | if (typeof glyph === 'number') { 89 | glyph = String.fromCharCode(glyph); 90 | } 91 | 92 | const proessedColor = processColor(color); 93 | const cacheKey = glyph + ':' + size + ':' + proessedColor; 94 | const scale = PixelRatio.get(); 95 | 96 | return new Promise((resolve, reject) => { 97 | const cached = imageSourceCache[cacheKey]; 98 | if (typeof cached !== 'undefined') { 99 | if (!cached || cached instanceof Error ) { reject(cached); } 100 | resolve({ uri: cached, scale }); 101 | } else { 102 | NativeIconAPI.getImageForFont(fontReference, glyph, size, proessedColor, (err, image) => { 103 | const error = (typeof err === 'string' ? new Error(err) : err); 104 | imageSourceCache[cacheKey] = image || error || false; 105 | if (!error && image) { 106 | resolve({ uri: image, scale }); 107 | } else { 108 | reject(error); 109 | } 110 | }); 111 | } 112 | }); 113 | } 114 | 115 | Icon.Button = createIconButtonComponent(Icon); 116 | Icon.TabBarItem = Icon.TabBarItemIOS = createTabBarItemIOSComponent(IconNamePropType, getImageSource); 117 | Icon.ToolbarAndroid = createToolbarAndroidComponent(IconNamePropType, getImageSource); 118 | Icon.getImageSource = getImageSource; 119 | 120 | return Icon; 121 | } 122 | -------------------------------------------------------------------------------- /src/SearchBar/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native' 3 | import { Icon } from '../Icon' 4 | 5 | const styles = StyleSheet.create({ 6 | searchBar: { 7 | position: 'relative', 8 | paddingTop: 8, 9 | paddingRight: 10, 10 | paddingBottom: 8, 11 | paddingLeft: 10, 12 | flexDirection: 'row', 13 | alignItems: 'center', 14 | backgroundColor: '#EFEFF4', 15 | borderTopWidth: StyleSheet.hairlineWidth, 16 | borderBottomWidth: StyleSheet.hairlineWidth, 17 | borderStyle: 'solid', 18 | borderColor: '#C7C7C7', 19 | }, 20 | searchOuter: { 21 | position: 'relative', 22 | flex: 1, 23 | backgroundColor: '#FFFFFF', 24 | borderWidth: StyleSheet.hairlineWidth, 25 | borderColor: '#E6E6EA', 26 | borderRadius: 5, 27 | }, 28 | searchInner: { 29 | position: 'relative', 30 | paddingLeft: 10, 31 | paddingRight: 10, 32 | paddingTop: 4, 33 | paddingBottom: 4, 34 | flexDirection: 'row', 35 | alignItems: 'center', 36 | }, 37 | searchInput: { 38 | marginLeft: 5, 39 | height: 20, 40 | fontSize: 14, 41 | flex: 1, 42 | }, 43 | searchCover: { 44 | position: 'absolute', 45 | top: 1, 46 | right: 1, 47 | bottom: 1, 48 | left: 1, 49 | height: 26, 50 | borderRadius: 3, 51 | backgroundColor: '#FFFFFF', 52 | flexDirection: 'row', 53 | alignItems: 'center', 54 | justifyContent: 'center', 55 | }, 56 | searchCoverText: { 57 | textAlign: 'center', 58 | color: '#9B9B9B', 59 | marginLeft: 5, 60 | }, 61 | searchCancel: { 62 | marginLeft: 10, 63 | color: '#09BB07', 64 | } 65 | }) 66 | 67 | class SearchBar extends Component { 68 | static defaultProps = { 69 | placeholder: '搜索', 70 | onChange: undefined, 71 | onClear: undefined, 72 | onCancel: undefined, 73 | lang: { 74 | cancel: '取消' 75 | } 76 | } 77 | 78 | constructor(props) { 79 | super(props) 80 | this.state = { 81 | focus: false, 82 | text: '', 83 | } 84 | this.cancelHandle = this.cancelHandle.bind(this) 85 | this.changeHandle = this.changeHandle.bind(this) 86 | this.clearHandle = this.clearHandle.bind(this) 87 | this.handleFocus = this.handleFocus.bind(this) 88 | this.handleBlur = this.handleBlur.bind(this) 89 | this.focus = this.focus.bind(this) 90 | } 91 | 92 | changeHandle(text) { 93 | this.setState({ text }) 94 | if (this.props.onChange) this.props.onChange(text) 95 | } 96 | 97 | cancelHandle(e) { 98 | this.setState({ focus: false }) 99 | this.blur() 100 | if (this.props.onCancel) this.props.onCancel(e) 101 | } 102 | 103 | clearHandle(e) { 104 | this.setState({ text: '' }) 105 | if (this.props.onClear) this.props.onClear(e) 106 | if (this.props.onChange) this.props.onChange('') 107 | } 108 | 109 | handleFocus() { 110 | this.setState({ focus: true }) 111 | } 112 | 113 | handleBlur() { 114 | this.setState({ focus: false }) 115 | } 116 | 117 | focus() { 118 | this.searchInput.focus() 119 | } 120 | 121 | blur() { 122 | this.searchInput.blur() 123 | } 124 | 125 | render() { 126 | const { 127 | placeholder, 128 | lang, 129 | style, 130 | } = this.props 131 | const { focus, text } = this.state 132 | 133 | return ( 134 | 135 | 136 | 137 | 138 | { this.searchInput = ref }} 140 | style={styles.searchInput} 141 | placeholder={placeholder} 142 | onFocus={this.handleFocus} 143 | onBlur={this.handleBlur} 144 | onChangeText={this.changeHandle} 145 | value={text} 146 | autoCorrect={false} 147 | blurOnSubmit={!false} 148 | returnKeyType="search" 149 | /> 150 | {text ? ( 151 | 152 | 153 | 154 | ) : null} 155 | 156 | {(focus || text) ? null : 157 | 158 | 159 | {placeholder} 160 | 161 | } 162 | 163 | {!focus ? null : 164 | {lang.cancel}} 165 | 166 | ) 167 | } 168 | } 169 | 170 | SearchBar.propTypes = { 171 | onChange: PropTypes.func, 172 | onClear: PropTypes.func, 173 | onCancel: PropTypes.func, 174 | lang: PropTypes.object, 175 | style: View.propTypes.style, 176 | placeholder: PropTypes.string, 177 | } 178 | 179 | export default SearchBar 180 | -------------------------------------------------------------------------------- /src/ActionSheet/ActionSheet.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { 3 | Modal, 4 | View, 5 | Text, 6 | TouchableHighlight, 7 | StyleSheet, 8 | Platform, 9 | } from 'react-native' 10 | import { Mask } from '../Mask' 11 | import { Popup } from '../Popup' 12 | import V from '../variable' 13 | 14 | const styles = StyleSheet.create({ 15 | iosActionsheet: { 16 | backgroundColor: V.weuiBgColorDefault, 17 | }, 18 | androidActionsheetWrapper: { 19 | flexDirection: 'row', 20 | alignItems: 'center', 21 | justifyContent: 'center', 22 | }, 23 | androidActionsheet: { 24 | width: 274, 25 | backgroundColor: V.weuiBgColorDefault, 26 | borderRadius: V.weuiActionSheetAndroidBorderRadius, 27 | }, 28 | actionsheetMenu: { 29 | backgroundColor: '#fff', 30 | }, 31 | actionsheetAction: { 32 | marginTop: 6, 33 | backgroundColor: '#fff', 34 | }, 35 | actionsheetCell: { 36 | borderTopWidth: StyleSheet.hairlineWidth, 37 | borderColor: V.weuiCellBorderColor, 38 | borderStyle: 'solid' 39 | }, 40 | iosActionsheetCell: { 41 | paddingTop: 10, 42 | paddingBottom: 10, 43 | }, 44 | androidActionsheetCell: { 45 | paddingTop: 13, 46 | paddingBottom: 13, 47 | paddingLeft: 24, 48 | paddingRight: 24, 49 | }, 50 | firstActionsheetCell: { 51 | borderTopWidth: 0, 52 | }, 53 | iosActionsheetCellText: { 54 | textAlign: 'center', 55 | fontSize: 18, 56 | marginTop: ((18 * V.baseLineHeight) - 18) / 2, 57 | marginBottom: ((18 * V.baseLineHeight) - 18) / 2, 58 | }, 59 | androidActionsheetCellText: { 60 | textAlign: 'left', 61 | fontSize: 16, 62 | marginTop: ((16 * 1.4) - 16) / 2, 63 | marginBottom: ((16 * 1.4) - 16) / 2, 64 | }, 65 | defaultActionsheetCellText: { 66 | color: '#000', 67 | }, 68 | primaryActionsheetCellText: { 69 | color: '#0BB20C', 70 | }, 71 | warnActionsheetCellText: { 72 | color: V.weuiColorWarn, 73 | } 74 | }) 75 | 76 | const underlayColor = V.weuiBgColorActive 77 | 78 | const ActionSheet = ({ 79 | visible, 80 | style, 81 | maskStyle, 82 | onShow, 83 | onClose, 84 | menus = [], 85 | actions = [], 86 | autoDectect = true, 87 | type = 'ios' 88 | }) => { 89 | let _type = type 90 | if (autoDectect) _type = Platform.OS 91 | 92 | const _renderMenuItems = () => 93 | menus.map(({ type: btnType, label, style: btnStyle, textStyle, ...others }, idx) => 94 | 105 | {label} 113 | 114 | ) 115 | 116 | const _renderActions = () => 117 | actions.map(({ type: btnType, label, style: btnStyle, textStyle, ...others, }, idx) => 118 | 129 | {label} 137 | 138 | ) 139 | 140 | return _type === 'ios' ? ( 141 | 148 | {menus.length ? ( 149 | 150 | {_renderMenuItems()} 151 | 152 | ) : false} 153 | {actions.length ? ( 154 | 155 | {_renderActions()} 156 | 157 | ) : false} 158 | 159 | ) : ( 160 | 167 | 168 | 169 | {menus.length ? ( 170 | 171 | {_renderMenuItems()} 172 | 173 | ) : false} 174 | {actions.length ? ( 175 | 176 | {_renderActions()} 177 | 178 | ) : false} 179 | 180 | 181 | 182 | ) 183 | } 184 | 185 | ActionSheet.propTypes = { 186 | autoDectect: PropTypes.bool, 187 | type: PropTypes.oneOf(['ios', 'android']), 188 | menus: PropTypes.arrayOf(PropTypes.object), 189 | actions: PropTypes.arrayOf(PropTypes.object), 190 | visible: PropTypes.bool, 191 | onShow: PropTypes.func, 192 | onClose: PropTypes.func, 193 | style: View.propTypes.style, 194 | maskStyle: View.propTypes.style, 195 | } 196 | 197 | export default ActionSheet 198 | -------------------------------------------------------------------------------- /src/Form/Uploader.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native' 3 | import ImagePicker from 'react-native-image-picker' 4 | import concat from 'lodash/concat' 5 | import { Icon } from '../Icon' 6 | import V from '../variable' 7 | 8 | const styles = StyleSheet.create({ 9 | uploader: {}, 10 | uploaderHeader: { 11 | paddingTop: 0, 12 | paddingRight: 0, 13 | paddingLeft: 0, 14 | paddingBottom: 10, 15 | flexDirection: 'row', 16 | alignItems: 'center', 17 | justifyContent: 'space-between', 18 | height: 17 * V.baseLineHeight, 19 | }, 20 | uploaderTitle: { 21 | fontSize: 17, 22 | }, 23 | uploaderCounter: { 24 | fontSize: 17, 25 | color: '#888', 26 | }, 27 | uploaderBody: { 28 | marginBottom: V.weuiCellGapH - (V.weuiCellGapV + V.weuiUploaderFileSpacing), 29 | marginRight: 0 - V.weuiUploaderFileSpacing, 30 | flexDirection: 'row', 31 | flexWrap: 'wrap', 32 | overflow: 'hidden', 33 | }, 34 | uploaderFile: { 35 | position: 'relative', 36 | width: V.weuiUploaderSize, 37 | height: V.weuiUploaderSize, 38 | marginRight: V.weuiUploaderFileSpacing, 39 | marginBottom: V.weuiUploaderFileSpacing, 40 | }, 41 | uploaderFileImage: { 42 | width: V.weuiUploaderSize, 43 | height: V.weuiUploaderSize, 44 | }, 45 | uploaderStatus: { 46 | position: 'absolute', 47 | top: 0, 48 | right: 0, 49 | bottom: 0, 50 | left: 0, 51 | backgroundColor: 'rgba(0, 0, 0, .5)', 52 | flex: 1, 53 | justifyContent: 'center', 54 | alignItems: 'center', 55 | }, 56 | uploaderStatusContent: { 57 | color: '#fff', 58 | }, 59 | uploaderRemove: { 60 | position: 'absolute', 61 | top: 2, 62 | right: 2, 63 | }, 64 | uploaderAddButton: { 65 | position: 'relative', 66 | marginRight: V.weuiUploaderFileSpacing, 67 | marginBottom: V.weuiUploaderFileSpacing, 68 | width: V.weuiUploaderSize - (V.weuiUploaderBorderWidth * 2), 69 | height: V.weuiUploaderSize - (V.weuiUploaderBorderWidth * 2), 70 | borderWidth: V.weuiUploaderBorderWidth, 71 | borderColor: V.weuiUploaderBorderColor, 72 | }, 73 | uploaderAddButtonRec: { 74 | position: 'absolute', 75 | top: (V.weuiUploaderSize / 4) - 4, 76 | left: (V.weuiUploaderSize / 2) - 4, 77 | width: V.weuiUploaderBorderWidth + 1, 78 | height: V.weuiUploaderSize / 2, 79 | backgroundColor: V.weuiUploaderBorderColor, 80 | } 81 | }) 82 | 83 | const Uploader = ({ 84 | title = '图片上传', 85 | maxCount = 4, 86 | files = [], 87 | onChange, 88 | onFilePress, 89 | disabled = false, 90 | style, 91 | ...others 92 | }) => { 93 | const options = { 94 | title, 95 | cancelButtonTitle: '取消', 96 | takePhotoButtonTitle: '拍照', 97 | chooseFromLibraryButtonTitle: '从照片库选择...', 98 | cameraType: 'back', // 'front' or 'back' 99 | mediaType: 'photo', // 'photo' or 'video' 100 | // videoQuality: 'high', // 'low', 'medium', or 'high' 101 | // durationLimit: 10, 102 | // maxWidth: 100, 103 | // maxHeight: 100, 104 | // quality: 0.2, 105 | // rotation: 0, // 0 to 360 106 | // allowsEditing: true, // bool 107 | noData: true, // disables the base64 108 | // storageOptions: { 109 | // skipBackup: true, 110 | // path: 'images', 111 | // cameraRoll: true, 112 | // waitUntilSaved() {} 113 | // } 114 | } 115 | 116 | const showImagePicker = () => { 117 | ImagePicker.showImagePicker(options, (response) => { 118 | if (response.didCancel) { 119 | console.log('User cancelled image picker') 120 | } else if (response.error) { 121 | console.log('ImagePicker Error: ', response.error) 122 | } else { 123 | return onChange && onChange(concat(files, response), response) 124 | } 125 | return false 126 | }) 127 | } 128 | 129 | return ( 130 | 131 | 132 | {title} 133 | {files.length} / {maxCount} 134 | 135 | 136 | {files.map((file, idx) => { 137 | const { uri, error, status, isVertical } = file 138 | const source = { uri, isVertical } 139 | return ( 140 | !disabled && onFilePress(file)} 144 | > 145 | 146 | 147 | {error || status ? 148 | 149 | {error ? 150 | : {status}} 151 | : null} 152 | 153 | 154 | ) 155 | })} 156 | {files.length < maxCount ? 157 | 162 | 163 | 164 | 165 | : null} 166 | 167 | 168 | ) 169 | } 170 | 171 | Uploader.propTypes = { 172 | title: PropTypes.string, 173 | maxCount: PropTypes.number, 174 | onChange: PropTypes.func, 175 | onFilePress: PropTypes.func, 176 | files: PropTypes.array, 177 | disabled: PropTypes.bool, 178 | style: View.propTypes.style, 179 | } 180 | 181 | export default Uploader 182 | -------------------------------------------------------------------------------- /src/Dialog/Dialog.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { 3 | Modal, 4 | View, 5 | Text, 6 | TouchableHighlight, 7 | Dimensions, 8 | StyleSheet, 9 | Platform, 10 | } from 'react-native' 11 | import { Mask } from '../Mask' 12 | import { create } from '../StyleSheet' 13 | import V from '../variable' 14 | 15 | const { width } = Dimensions.get('window') 16 | const styles = create({ 17 | dialogWrapper: { 18 | flexDirection: 'row', 19 | alignItems: 'center', 20 | justifyContent: 'center', 21 | }, 22 | dialog: { 23 | width: width - 60, 24 | backgroundColor: V.weuiDialogBackgroundColor, 25 | borderRadius: 3, 26 | overflow: 'hidden', 27 | }, 28 | dialogHeader: { 29 | paddingTop: 1.3 * V.baseFontSize, 30 | paddingBottom: 0.5 * V.baseFontSize, 31 | paddingLeft: V.weuiDialogGapWidth, 32 | paddingRight: V.weuiDialogGapWidth, 33 | }, 34 | dialogTitle: { 35 | fontWeight: '400', 36 | }, 37 | iosDialogTitle: { 38 | fontSize: 18, 39 | textAlign: 'center', 40 | }, 41 | androidDialogTitle: { 42 | fontSize: 21, 43 | textAlign: 'left', 44 | }, 45 | dialogBody: { 46 | paddingLeft: V.weuiDialogGapWidth, 47 | paddingRight: V.weuiDialogGapWidth, 48 | }, 49 | iosDialogBody: { 50 | paddingBottom: (0.8 * 15) + 20, 51 | }, 52 | androidDialogBody: { 53 | paddingTop: 0.25 * 17, 54 | paddingBottom: (17 * 2) + 20, 55 | }, 56 | dialogBodyText: { 57 | color: V.weuiTextColorGray, 58 | lineHeight: 15 * 1.3, 59 | android: { 60 | lineHeight: Math.round(15 * 1.3), 61 | }, 62 | }, 63 | iosDialogBodyText: { 64 | fontSize: 15, 65 | textAlign: 'center', 66 | }, 67 | androidDialogBodyText: { 68 | fontSize: 17, 69 | textAlign: 'left', 70 | }, 71 | dialogFooter: { 72 | flexDirection: 'row', 73 | }, 74 | iosDialogFooter: { 75 | height: 48, 76 | alignItems: 'center', 77 | justifyContent: 'center', 78 | borderTopWidth: StyleSheet.hairlineWidth, 79 | borderColor: V.weuiDialogLineColor, 80 | borderStyle: 'solid', 81 | }, 82 | androidDialogFooter: { 83 | height: 42, 84 | alignItems: 'flex-end', 85 | justifyContent: 'flex-end', 86 | paddingLeft: V.weuiDialogGapWidth, 87 | paddingRight: V.weuiDialogGapWidth, 88 | paddingBottom: 16 * 0.7, 89 | }, 90 | dialogFooterOpr: { 91 | alignItems: 'center', 92 | justifyContent: 'center', 93 | }, 94 | iosDialogFooterOpr: { 95 | height: 48, 96 | flex: 1, 97 | }, 98 | androidDialogFooterOpr: { 99 | height: 42, 100 | paddingLeft: 16 * 0.8, 101 | paddingRight: 16 * 0.8, 102 | }, 103 | dialogFooterOprWithNegativeMarginRight: { 104 | marginRight: 0 - (16 * 0.8), 105 | }, 106 | dialogFooterOprWithBorder: { 107 | borderLeftWidth: StyleSheet.hairlineWidth, 108 | borderColor: V.weuiDialogLineColor, 109 | borderStyle: 'solid', 110 | }, 111 | iosDialogFooterOprText: { 112 | fontSize: 18, 113 | }, 114 | androidDialogFooterOprText: { 115 | fontSize: 16, 116 | }, 117 | defaultDialogFooterOprText: { 118 | color: '#353535', 119 | }, 120 | primaryDialogFooterOprText: { 121 | color: '#0BB20C', 122 | }, 123 | warnDialogFooterOprText: { 124 | color: V.weuiColorWarn, 125 | } 126 | }) 127 | 128 | const underlayColor = V.weuiDialogLinkActiveBc 129 | 130 | const Dialog = ({ 131 | visible = false, 132 | buttons = [], 133 | title, 134 | style, 135 | maskStyle, 136 | headerStyle, 137 | titleStyle, 138 | bodyStyle, 139 | bodyTextStyle, 140 | footerStyle, 141 | children, 142 | onShow, 143 | onClose, 144 | autoDectect = true, 145 | type = 'ios' 146 | }) => { 147 | let _type = type 148 | if (autoDectect) _type = Platform.OS 149 | 150 | const _renderButtons = () => 151 | buttons.map(({ type: btnType, label, ...others }, idx) => 152 | 0 ? styles.dialogFooterOprWithBorder : {}, 158 | _type === 'android' && idx === buttons.length - 1 ? styles.dialogFooterOprWithNegativeMarginRight : {} 159 | ]} 160 | underlayColor={underlayColor} 161 | {...others} 162 | > 163 | {label} 166 | 167 | ) 168 | 169 | const childrenWithProps = React.Children.map(children, (child) => { 170 | if (child.type.displayName === 'Text') { 171 | return React.cloneElement(child, { 172 | style: [styles.dialogBodyText, styles[`${type}DialogBodyText`], bodyTextStyle, child.props.style] 173 | }) 174 | } 175 | return child 176 | }) 177 | 178 | return ( 179 | 185 | 186 | 187 | 188 | {title} 189 | 190 | 191 | {childrenWithProps} 192 | 193 | 194 | {_renderButtons()} 195 | 196 | 197 | 198 | 199 | ) 200 | } 201 | 202 | Dialog.propTypes = { 203 | autoDectect: PropTypes.bool, 204 | type: PropTypes.oneOf(['ios', 'android']), 205 | title: PropTypes.string, 206 | buttons: PropTypes.arrayOf(PropTypes.object), 207 | visible: PropTypes.bool, 208 | onShow: PropTypes.func, 209 | onClose: PropTypes.func, 210 | style: View.propTypes.style, 211 | maskStyle: View.propTypes.style, 212 | headerStyle: View.propTypes.style, 213 | titleStyle: Text.propTypes.style, 214 | bodyStyle: View.propTypes.style, 215 | bodyTextStyle: Text.propTypes.style, 216 | footerStyle: View.propTypes.style, 217 | children: PropTypes.node 218 | } 219 | 220 | export default Dialog 221 | -------------------------------------------------------------------------------- /example/page/Cell.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { 3 | StyleSheet, 4 | ScrollView, 5 | View, 6 | Image, 7 | Text, 8 | } from 'react-native' 9 | import { 10 | Cells, 11 | CellsTitle, 12 | CellsTips, 13 | Cell, 14 | CellHeader, 15 | CellBody, 16 | CellFooter, 17 | CellText, 18 | Input, 19 | Label, 20 | TextArea, 21 | Switch, 22 | RadioCells, 23 | CheckboxCells, 24 | Uploader, 25 | Select, 26 | Slider, 27 | Badge, 28 | Agreement, 29 | Gallery, 30 | GalleryDelete, 31 | Picker, 32 | Toptips 33 | } from '../../src' 34 | 35 | const styles = StyleSheet.create({ 36 | wrapper: { 37 | flex: 1, 38 | backgroundColor: '#fbf9fe', 39 | }, 40 | }) 41 | 42 | const pickerData1 = [ 43 | [{ label: '1', value: '1' }, { label: '2', value: '2' }, { label: '3', value: '3' }], 44 | [{ label: '4', value: '4' }, { label: '5', value: '5' }, { label: '6', value: '6' }], 45 | [{ label: '7', value: '7' }, { label: '8', value: '8' }, { label: '9', value: '9' }], 46 | ] 47 | 48 | const pickerData2 = [{ 49 | label: 'a', 50 | value: 'value of a', 51 | children: [{ 52 | label: 'a-a', 53 | value: 'value of a-a' 54 | }, { 55 | label: 'a-b', 56 | value: 'value of a-b', 57 | children: [{ 58 | label: 'a-b-a', 59 | value: 'value of a-b-a' 60 | }, { 61 | label: 'a-b-b', 62 | value: 'value of a-b-b' 63 | }] 64 | }] 65 | }, { 66 | label: 'b', 67 | value: 'value of b', 68 | children: [{ 69 | label: 'b-a', 70 | value: 'value of b-a', 71 | children: [{ 72 | label: 'b-a-a', 73 | value: 'value of b-a-a' 74 | }, { 75 | label: 'b-a-b', 76 | value: 'value of b-a-b' 77 | }] 78 | }, { 79 | label: 'b-b', 80 | value: 'value of b-b', 81 | children: [{ 82 | label: 'b-b-a', 83 | value: 'value of b-b-a' 84 | }, { 85 | label: 'b-b-b', 86 | value: 'value of b-b-b' 87 | }] 88 | }] 89 | }] 90 | 91 | const pickerData3 = [{ 92 | label: '北京', 93 | value: '1', 94 | children: [{ 95 | label: '上海', 96 | value: '2', 97 | children: [{ 98 | label: '浦东', 99 | value: '4', 100 | children: [] 101 | }] 102 | }, { 103 | label: '天津', 104 | value: '3', 105 | children: [{ 106 | label: '天行', 107 | value: '5', 108 | children: [] 109 | }] 110 | }] 111 | }] 112 | 113 | // const selectedValue = ['value of b', 'value of b-b', 'value of b-b-b'] 114 | 115 | class CellScene extends Component { 116 | constructor() { 117 | super() 118 | this.state = { 119 | files: [], 120 | radio: '', 121 | checkbox: [1], 122 | text: '默认', 123 | switchValue: false, 124 | textarea: '', 125 | selectedValue: ['', '', ''], 126 | select: ['', '', ''], 127 | selectLabel: [], 128 | sliderValue: 5, 129 | galleryVisible: false, 130 | agreement: false, 131 | pickerVisible: false, 132 | picker: ['', '', ''], 133 | toptipsVisible: false, 134 | } 135 | this.setSelect = this.setSelect.bind(this) 136 | this.handleUploadChange = this.handleUploadChange.bind(this) 137 | this.handleRadioChange = this.handleRadioChange.bind(this) 138 | this.handleCheckboxChange = this.handleCheckboxChange.bind(this) 139 | this.handleSwitchChange = this.handleSwitchChange.bind(this) 140 | this.handleTextareaChange = this.handleTextareaChange.bind(this) 141 | this.handleSelectChange = this.handleSelectChange.bind(this) 142 | this.handleSliderChange = this.handleSliderChange.bind(this) 143 | this.updateState = this.updateState.bind(this) 144 | this.onToptipsShow = this.onToptipsShow.bind(this) 145 | } 146 | setSelect(value) { 147 | this.setState({ select: value }) 148 | } 149 | updateState(key, value) { 150 | this.setState({ [key]: value }) 151 | } 152 | handleUploadChange(files) { 153 | this.setState({ files }) 154 | } 155 | handleRadioChange(radio) { 156 | this.setState({ radio }) 157 | } 158 | handleCheckboxChange(checkbox) { 159 | this.setState({ checkbox }) 160 | } 161 | handleChangeText(text) { 162 | this.setState({ text }) 163 | } 164 | handleSwitchChange(switchValue) { 165 | this.setState({ switchValue }) 166 | } 167 | handleTextareaChange(textarea) { 168 | this.setState({ textarea }) 169 | } 170 | handleSelectChange(value) { 171 | this.setState({ selectedValue: value }) 172 | } 173 | handleSliderChange(value) { 174 | this.setState({ sliderValue: value }) 175 | } 176 | onToptipsShow() { 177 | this.setState({ toptipsVisible: true }) 178 | setTimeout(() => this.setState({ toptipsVisible: false }), 3000) 179 | } 180 | render() { 181 | return ( 182 | 183 | 带说明的列表项 184 | 185 | 186 | 标题文字 187 | 说明文字 188 | 189 | 190 | 带图标、说明的列表项 191 | 192 | 193 | 194 | 195 | 196 | 标题文字 197 | 说明文字 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 标题文字News 206 | 207 | 208 | 209 | 说明文字8 210 | 211 | 212 | 213 | 带跳转的列表项 214 | 215 | {}}> 216 | 标题文字 217 | 218 | 219 | {}}> 220 | 标题文字 221 | 222 | 223 | 224 | 带说明、跳转的列表项 225 | 226 | {}}> 227 | 标题文字 228 | 说明文字 229 | 230 | {}}> 231 | 标题文字 232 | 说明文字 233 | 234 | 235 | 带图标、说明、跳转的列表项 236 | 237 | {}}> 238 | 239 | 240 | 241 | 标题文字 242 | 说明文字 243 | 244 | {}}> 245 | 246 | 247 | 248 | 标题文字 249 | 说明文字 250 | 251 | 252 | 单选列表项 253 | 266 | {}}> 267 | more 268 | 269 | 270 | 复选列表项 271 | 285 | 开关 286 | 287 | 288 | 标题文字 289 | 293 | 294 | 295 | 表单 296 | 297 | 298 | 299 | 300 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 底部说明文字底部说明文字 314 | 315 | 图片上传 316 | 317 | 318 | 319 | this.setState({ file, galleryVisible: true })} 324 | /> 325 | this.setState({ galleryVisible: false })} 329 | > 330 | { 332 | this.handleUploadChange( 333 | this.state.files.filter(file => file.uri !== this.state.file.uri)) 334 | this.setState({ galleryVisible: false }) 335 | }} 336 | /> 337 | 338 | 339 | 340 | 341 | 342 | 文本域 343 | 344 | 345 | 346 |