From 3adba1de00b66863b6bc4f5b5e1fc92d9808f7e2 Mon Sep 17 00:00:00 2001 From: Zyd Date: Tue, 27 Jun 2017 20:10:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TreeSelect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Example/src/components/TestTree.js | 70 +-------- Example/src/components/TestTreeSelect.js | 6 +- Example/src/components/treeData.js | 69 +++++++++ README.md | 178 +++++++---------------- README2.md | 33 ++--- index.js | 4 +- package.json | 8 +- src/Tree.js | 7 +- src/TreeSelect.js | 135 +++++++++++++++++ 9 files changed, 280 insertions(+), 230 deletions(-) create mode 100644 Example/src/components/treeData.js create mode 100644 src/TreeSelect.js diff --git a/Example/src/components/TestTree.js b/Example/src/components/TestTree.js index 786d3be..497dfb4 100644 --- a/Example/src/components/TestTree.js +++ b/Example/src/components/TestTree.js @@ -1,4 +1,5 @@ import {Tree} from 'react-native-tree'; +const {treeData} = require('./treeData'); export default class TestTree extends Component { constructor() { super(); @@ -57,72 +58,3 @@ const styles = { }, } -const treeData = [ - { - key: 'food', - label: '食品', - children: [ - { - key: 'mainfood', - label: '主食', - children: [ - { - key: 'noodle', - label: '面', - }, - { - key: 'rice', - label: '米饭', - }, - { - key: 'chaocai', - label: '炒菜', - children: [ - { - key: 'chuan', - label: '川菜', - }, - { - key: 'xiang', - label: '湘菜', - }, - { - key: 'yue', - label: '粤菜', - } - ] - } - ] - }, - { - key: 'fruit', - label: '水果', - children: [ - { - key: 'apple', - label: '苹果', - }, - { - key: 'xigua', - label: '西瓜', - }, - { - key: 'putao', - label: '葡萄', - children: [ - { - key: 'hutai', - label: '沪太8号', - }, - { - disabled: true, - key: 'hutai9', - label: '沪太9号', - } - ] - } - ] - } - ] - } -] \ No newline at end of file diff --git a/Example/src/components/TestTreeSelect.js b/Example/src/components/TestTreeSelect.js index c6cb9a2..57e1515 100644 --- a/Example/src/components/TestTreeSelect.js +++ b/Example/src/components/TestTreeSelect.js @@ -1,7 +1,9 @@ +import TreeSelect from './TreeSelect'; +const {treeData} = require('./treeData'); export default class TestTreeSelect extends React.Component { render () { - return - 什么都没有 + return + } } \ No newline at end of file diff --git a/Example/src/components/treeData.js b/Example/src/components/treeData.js new file mode 100644 index 0000000..120ad01 --- /dev/null +++ b/Example/src/components/treeData.js @@ -0,0 +1,69 @@ +export const treeData = [ + { + key: 'food', + label: '食品', + children: [ + { + key: 'mainfood', + label: '主食', + children: [ + { + key: 'noodle', + label: '面', + }, + { + key: 'rice', + label: '米饭', + }, + { + key: 'chaocai', + label: '炒菜', + children: [ + { + key: 'chuan', + label: '川菜', + }, + { + key: 'xiang', + label: '湘菜', + }, + { + key: 'yue', + label: '粤菜', + } + ] + } + ] + }, + { + key: 'fruit', + label: '水果', + children: [ + { + key: 'apple', + label: '苹果', + }, + { + key: 'xigua', + label: '西瓜', + }, + { + key: 'putao', + label: '葡萄', + children: [ + { + key: 'hutai', + label: '沪太8号', + }, + { + disabled: true, + key: 'hutai9', + label: '沪太9号', + } + ] + } + ] + } + ] + } +]; diff --git a/README.md b/README.md index aee55d6..302fdea 100644 --- a/README.md +++ b/README.md @@ -1,128 +1,52 @@ -# React Native Router [![Join the chat at https://gitter.im/aksonov/react-native-router-flux](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aksonov/react-native-router-flux?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Codacy Badge](https://api.codacy.com/project/badge/grade/c6d869e2367a4fb491efc9de228c5ac6)](https://www.codacy.com/app/aksonov-github/react-native-router-flux) [![npm version](https://badge.fury.io/js/react-native-router-flux.svg)](http://badge.fury.io/js/react-native-router-flux) [![CircleCI](https://circleci.com/gh/aksonov/react-native-router-flux.svg?style=svg)](https://circleci.com/gh/aksonov/react-native-router-flux) - -[![NPM](https://nodei.co/npm/react-native-router-flux.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/react-native-router-flux/) -Router for React Native based on new React Native Navigation API. - -## Documentation - -- [Mini-Tutorial](docs/MINI_TUTORIAL.md) -- [API and Configuration](docs/API_CONFIGURATION.md) - - Available Imports - - Router API - - Scene API -- [Detailed Example](docs/DETAILED_EXAMPLE.md) -- [Reactive/Redux/Flux](docs/REDUX_FLUX.md) -- [Other Info](docs/OTHER_INFO.md) - - Modals - - Tabbar - - Custom navbar - - Switch - - Splitting Scenes - - Drawer (side-menu) - - Sub-Scenes -- [Changelog](docs/CHANGELOG.md) -- [Migrating from 2.x](docs/MIGRATION.md) - -## Features - -`react-native-router-flux` is a routing package that allows you to: - -- Define scene transitions in one central location -- Without having to pass navigator objects around, and allow you to -- Call transitions anywhere in your code with a simple syntax (e.g. `Actions.login({username, password})` or `Actions.profile({profile})` or even `Actions.profile(123)` - all params will be part of `this.props` for given Scene component). - -### New Features and Highlights - -- **Highly Customizable Navigation Bar** - Show/hide the navbar depending on Scene or even the state of a Scene (e.g. Edit/Save navbar for edit mode). - -- **Tab Bar Support** using [react-native-tabs](https://github.com/aksonov/react-native-tabs) (see Example app). - -- **Nested Navigators** (e.g. Each tab can have its own navigator, nested in a root navigator). - -- **Custom Scene Renderers** for action sheet, native TabBarIOS or anything else. See built-in `Modal` renderer (to display popups) for example. *Call for PRs!* let's build some custom renderers for ActionSheet, Drawer, etc. Let's make an awesome library! Currently, if you want to use Action Sheets you'll need to use a 3rd party module. - -- **Dynamic Routing** allows you to choose which scene to render depending on application state (see the `Switch` renderer, useful for authentication). - -- **Bring Your Own Reducer** for navigation state. - -- **Reset History Stack** - The new [`reset`](docs/API_CONFIGURATION.md#scene)type for clearing the history stack and eliminating the navbar back button. - -- **More Powerful State Control** - Support for having different states while on the same screen. For example, "View My Account" could allow in-place editing of fields and "Save", "Cancel" navigation bar buttons should appear. - -## Getting Started - -Check out the [mini-tutorial](docs/MINI_TUTORIAL.md) for a quick walkthrough of the routing system. - -## Supported configurations - -While ExperimentalNavigation API is not stable, RNRF uses separated fork of ExperimentalNavigation API to avoid dependency from React Native version. -So 3.30 version of RNRF doesn't depend from React Native version, but from that fork (now it is based on 0.26 API). -You could use this component with RN0.26+ - -## Installation -``` -npm i react-native-router-flux --save -``` - -## Usage -In your top-level `index.js`, define your scenes using the `Scene` component and pass it into the `Router` as children: -```js -import {Scene, Router} from 'react-native-router-flux'; - -class App extends React.Component { - render() { - return - - - - - - - } -} -``` -Alternatively, you could define all of your scenes during compile time and use it later within `Router`: -```js -import {Actions, Scene, Router} from 'react-native-router-flux'; - -const scenes = Actions.create( - - - - - -); - -/* ... */ - -class App extends React.Component { - render() { - return - } -} -``` - -On any Scene, you can also call the following functions by first importing the `Actions` object: -```js -import {Actions} from 'react-native-router-flux' +--- +category: Components +type: Data Display +title: Tree +subtitle: 树形控件 +--- + +## 何时使用 + +组织架构、分类、国家地区等等,都是树形结构。使用`树控件`可以完整展现其中的层级关系,并具有展开收起选择等交互功能。 + +## API + +### Tree props + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|--------| +|treeData | 创建树的数据 | TreeNode[] | [] | +|multiple | 支持点选多个节点(节点本身) | boolean | false | +|checkable | 节点前添加 Checkbox 复选框 | boolean | false | +|defaultExpandAll | 默认展开所有树节点 | boolean | false | +|defaultExpandRoot | 默认树根节点 | boolean | false | +|defaultExpandedKeys | 默认展开指定的树节点 | string[] | [] | +|expandedKeys | (受控)展开指定的树节点 | string[] | [] | +|defaultSelectedKeys | 默认选中的树节点 | string[] | [] | +|selectedKeys | (受控)选中的树节点(注意:checkable为true时,父子节点有关联,如果传入父节点key,则子节点自动选中 | string[] | [] | +|checkStrictly | 父子节点选中状态不再关联 | boolean | false | +|onExpand | 展开/收起节点时触发 | function(expandedKeys, {expanded: bool, node}) | - | +|onSelect | 点击树节点触发 | function(selectedKeys, e:{selected: bool, selectedNodes, node}) | - | +|showLine | 是否展示连接线 | boolean | false | +|expandIconSize | 展开收起图标大小 | number | 11 | +|iconSize | 复选框和文本前自定义图标大小 | number | 15 | +|treeStyle | 组件最外层由ScrollView包裹,请传入该组件的style | StyleSheet | { padding: 10,height: ScreenHeight - 90} | + +### TreeNode props + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|------------------------------------------|------------|--------| +|disabled | 禁止选中 | boolean | false | +|label | 标题 | string\|ReactNode | '---' | +|key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | string | 内部计算出的节点位置 | +|icon | 文本前自定义图标 | string | null | +|children | 树节点的子节点 | TreeNode[] | null | + +## 注意 + +树节点可以有很多,但在设置`checkable`时,将会花费更多的计算时间,因此我们缓存了一些计算结果(`this.treeNodesStates`)来复用,避免多次重复计算,以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树: + +## 使用 +```jsx + ``` -And then: - -* `Actions.ACTION_NAME(PARAMS)` will call the appropriate action and params will be passed to the scene. -* `Actions.pop()` will pop the current screen. It accepts following optional params: - * `{popNum: [number]}` allows to pop multiple screens at once - * `{refresh: {...propsToSetOnPreviousScene}}` allows to refresh the props of the scene that it pops back to -* `Actions.refresh(PARAMS)` will update the properties of the current screen. - -## Production Apps using react-native-router-flux -+ Buddify ([iOS](https://itunes.apple.com/app/id1149011372), [Android](https://play.google.com/store/apps/details?id=com.buddify)) - helps travelers discover fun things to do locally. -+ GuavaPass.com ([iOS](https://itunes.apple.com/en/app/guavapass-one-pass-fitness/id1050491044?l=en&mt=8), Android) - offers convenient access to top classes at boutique fitness studios across Asia. -+ Epic Fail Videos ([iOS](https://itunes.apple.com/us/app/epic-fail-videos-best-fail/id1115219339), [Android](https://play.google.com/store/apps/details?id=com.hazuu.epicfailvideos)) - The best Fail Videos Collection, never miss a laugh with your friends! -+ Junk Free ([iOS](https://itunes.apple.com/us/app/junk-free-by-junk-free-june/id1109940159)) - A simple way to find, share, and save recipes, workouts, and other healthy content with your friends, family and workmates. -+ chozun ([iOS](https://itunes.apple.com/au/app/chozun/id1097365167), [Android](https://play.google.com/store/apps/details?id=com.chozun)) - Your travel companion, matching your lifestyle on the go! -+ Snappatizer ([iOS](https://itunes.apple.com/us/app/snappatizer-find-rank-best/id1147400405?mt=8)) - Find and rank the best food around you. -+ Look Lock ([GitHub](https://github.com/7kfpun/PhotosReactNative), [iOS](https://itunes.apple.com/us/app/look-lock-show-photos-without/id1151863742), [Android](https://play.google.com/store/apps/details?id=com.kfpun.photos)) - Show photos without worries. -+ BusDue, ([iOS](https://itunes.apple.com/gb/app/busdue/id1185327843?mt=8), [Android](https://play.google.com/store/apps/details?id=com.busdue)) - London bus arrival time app - -## Support -Thanks to all who submitted PRs to 2.x release. If you like the component and want to support it, feel free to donate any amount or help with issues. diff --git a/README2.md b/README2.md index cf6845f..48b9283 100644 --- a/README2.md +++ b/README2.md @@ -1,7 +1,7 @@ --- category: Components type: Data Display -title: Tree +title: TreeSelect subtitle: 树形控件 --- @@ -18,32 +18,17 @@ subtitle: 树形控件 |treeData | 创建树的数据 | TreeNode[] | [] | |multiple | 支持点选多个节点(节点本身) | boolean | false | |checkable | 节点前添加 Checkbox 复选框 | boolean | false | -|defaultExpandAll | 默认展开所有树节点 | boolean | false | -|defaultExpandRoot | 默认树根节点 | boolean | false | -|defaultExpandedKeys | 默认展开指定的树节点 | string[] | [] | -|expandedKeys | (受控)展开指定的树节点 | string[] | [] | -|defaultSelectedKeys | 默认选中的树节点 | string[] | [] | -|selectedKeys | (受控)选中的树节点(注意:checkable为true时,父子节点有关联,如果传入父节点key,则子节点自动选中 | string[] | [] | -|checkStrictly | 父子节点选中状态不再关联 | boolean | false | -|onExpand | 展开/收起节点时触发 | function(expandedKeys, {expanded: bool, node}) | - | -|onSelect | 点击树节点触发 | function(selectedKeys, e:{selected: bool, selectedNodes, node, event}) | - | +|onChange | 点击确定触发 | function(selectedKeys, e:{selected: bool, selectedNodes, node}) | - | |showLine | 是否展示连接线 | boolean | false | |expandIconSize | 展开收起图标大小 | number | 11 | |iconSize | 复选框和文本前自定义图标大小 | number | 15 | - -### TreeNode props - -| 参数 | 说明 | 类型 | 默认值 | -|-----------|------------------------------------------|------------|--------| -|disabled | 禁止选中 | boolean | false | -|label | 标题 | string\|ReactNode | '---' | -|key | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复! | string | 内部计算出的节点位置 | -|icon | 文本前自定义图标 | string | null | -|children | 树节点的子节点 | TreeNode[] | null | - -## 注意 - -树节点可以有很多,但在设置`checkable`时,将会花费更多的计算时间,因此我们缓存了一些计算结果(`this.treeNodesStates`)来复用,避免多次重复计算,以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树: +|treeStyle | 组件最外层由ScrollView包裹,请传入该组件的style | StyleSheet | { padding: 10,height: ScreenHeight - 90} | +|iconSize | 复选框和文本前自定义图标大小 | number | 15 | +|dismissText | 取消 | string | 取消 | +|okText | 确定 | string | 确定 | +|title | 确定取消中间的标题 | string | null | +|inputIconSize | input右边的图表大小 | number | 25 | +|inputStyles | 输入框的Style | StyleSheet | null | ## 使用 ```jsx diff --git a/index.js b/index.js index 0679e86..fc26167 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ import Tree from './src/Tree'; +import TreeSelect from './src/TreeSelect' export { - Tree + Tree, + TreeSelect }; diff --git a/package.json b/package.json index bd74acc..355555c 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,15 @@ "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { + "rc-dialog": "^6.5.10", "react": "^15.4.0", "react-native": "^0.41.2", - "react-native-vector-icons": "^4.2.0" + "react-native-vector-icons": "^4.2.0", + "rmc-picker": "^3.10.7" }, "description": "[![NPM](https://nodei.co/npm/react-native-router-flux.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/react-native-router-flux/) \r Router for React Native based on new React Native Navigation API.", "main": "index.js", - "devDependencies": { - - }, + "devDependencies": {}, "repository": { "type": "git", "url": "git+https://github.com/AdamRobertHall/react-native-tree.git" diff --git a/src/Tree.js b/src/Tree.js index a454a8b..67e9ecd 100644 --- a/src/Tree.js +++ b/src/Tree.js @@ -206,6 +206,7 @@ class Tree extends React.Component { selectedKeys = [node.key]; selected = {}; selected[node.key] = true; + selectedNodes = [node]; } /** * 1. 如果定义checkable则建立父子节点关联关系 @@ -263,7 +264,7 @@ class Tree extends React.Component { expandedKeys.push(key); } - if (selected[key] === true) { + if (selected[key] === true && checkable && !checkStrictly) { children && children.map(item => { if (isExpanded) { selected[item.key] = false; @@ -339,7 +340,7 @@ class Tree extends React.Component { textStyle.color = '#D0D0D0'; } - textStyle.marginLeft = 5; + textStyle.marginLeft = 2; if (selected[key]) { textStyle.backgroundColor = '#D2EAFB' } @@ -426,7 +427,7 @@ class Tree extends React.Component { } render() { - return + return {this.renderTree(this.props.treeData || [], null)} } diff --git a/src/TreeSelect.js b/src/TreeSelect.js new file mode 100644 index 0000000..821c5ae --- /dev/null +++ b/src/TreeSelect.js @@ -0,0 +1,135 @@ +const Modal = require('rc-dialog/lib/Modal') +import Icon from 'react-native-vector-icons/FontAwesome'; +import Tree from './Tree' +import React, { PropTypes, Component } from 'react'; +import {ScrollView, View, Text, TouchableOpacity, TextInput, TouchableHighlight} from 'react-native'; +export default class TreeSelect extends Component { + constructor() { + super(); + this.state = { + valueText: "", + visible: false, + value: [], + info: {}, + } + } + onDismiss() { + this.setState({visible: false}); + this.props.onDismiss && this.props.onDismiss(); + } + onOk() { + let nodes = this.state.info.selectedNodes || []; + let str = ""; + for (let i in nodes) { + let node = nodes[i]; + if (i < nodes.length - 1) { + str += node.label + ","; + } else { + str += node.label; + } + + } + this.setState({valueText: str, visible: false}) + this.props.onChange && this.props.onChange(this.state.value, this.state.info); + } + onChange(value, info) { + this.setState({value: value, info: info}); + } + render() { + const dismissText = this.props.dismissText || '取消'; + const okText = this.props.okText || '确定' + let styles = this.props.styles || defautStyles; + + const {checkable, checkStrictly, multiple, treeStyle, showLine, iconSize, expandIconSize} = this.props; + return ( + + + + + + this.setState({visible: !this.state.visible})} + name='angle-right' + size={this.props.inputIconSize || 28} + /> + + + + + {dismissText} + + + {this.props.title} + + + {okText} + + + + + + ) + } +} +let textStyle = { + color: '#0ae', + fontSize: 18, + textAlign: 'center' +}; +const defautStyles = { + input: { + height: 50, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'center', + }, + modal: { + flexDirection: 'column', + justifyContent: 'flex-end' + }, + header: { + height: 50, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + borderBottomWidth: 1, + borderBottomColor: '#e7e7e7' + }, + headerItem: { + flex: 1, + height: 44, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'center', + }, + text: { + }, + actionText: textStyle +} \ No newline at end of file