diff --git a/components/CellWrapper.js b/components/CellWrapper.js index 0b10779..daf8331 100644 --- a/components/CellWrapper.js +++ b/components/CellWrapper.js @@ -1,46 +1,45 @@ -'use strict'; - -import React, { - Component, - PropTypes -} from 'react'; -import ReactNative, { - View -} from 'react-native'; - -export default class CellWrapper extends Component { - componentDidMount() { - this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); - } - - render() { - const Cell = this.props.component; - return ( - - - - ); - } -} - -CellWrapper.propTypes = { - - /** - * The id of the section - */ - sectionId: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - - /** - * A component to render for each cell - */ - component: PropTypes.func.isRequired, - - /** - * A function used to propagate the root nodes handle back to the parent - */ - updateTag: PropTypes.func - -}; +'use strict'; + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactNative, { + View +} from 'react-native'; + +export default class CellWrapper extends Component { + + componentDidMount() { + this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); + } + + render() { + var Cell = this.props.component; + return ( + + + + ); + } +} + +CellWrapper.propTypes = { + + /** + * The id of the section + */ + sectionId: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]), + + /** + * A component to render for each cell + */ + component: PropTypes.func.isRequired, + + /** + * A function used to propagate the root nodes handle back to the parent + */ + updateTag: PropTypes.func + +}; diff --git a/components/SectionHeader.js b/components/SectionHeader.js index 715dabc..5da386b 100644 --- a/components/SectionHeader.js +++ b/components/SectionHeader.js @@ -1,69 +1,68 @@ -'use strict'; - -import React, { - Component, - PropTypes -} from 'react'; -import ReactNative, { - StyleSheet, - View, - Text, - NativeModules -} from 'react-native'; - -const { UIManager } = NativeModules; - -export default class SectionHeader extends Component { - - componentDidMount() { - this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); - } - - render() { - const SectionComponent = this.props.component; - const content = SectionComponent ? - : - {this.props.title}; - - return ( - - {content} - - ); - } -} - -const styles = StyleSheet.create({ - container: { - backgroundColor:'#f8f8f8', - borderTopWidth: 1, - borderTopColor: '#ececec' - }, - text: { - fontWeight: '700', - paddingTop:2, - paddingBottom:2, - paddingLeft: 2 - } -}); - -SectionHeader.propTypes = { - - /** - * The id of the section - */ - sectionId: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string - ]), - - /** - * A component to render for each section item - */ - component: PropTypes.func, - - /** - * A function used to propagate the root nodes handle back to the parent - */ - updateTag: PropTypes.func -}; +'use strict'; + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactNative, { + StyleSheet, + View, + Text, + NativeModules +} from 'react-native'; + +const UIManager = NativeModules; + +export default class SectionHeader extends Component { + + componentDidMount() { + this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); + } + + render() { + var SectionComponent = this.props.component; + var content = SectionComponent ? + : + {this.props.title}; + + return ( + + {content} + + ); + } +} + +var styles = StyleSheet.create({ + container: { + backgroundColor:'#f8f8f8', + borderTopWidth: 1, + borderTopColor: '#ececec' + }, + text: { + fontWeight: '700', + paddingTop:2, + paddingBottom:2, + paddingLeft: 2 + } +}); + +SectionHeader.propTypes = { + + /** + * The id of the section + */ + sectionId: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string + ]), + + /** + * A component to render for each section item + */ + component: PropTypes.func, + + /** + * A function used to propagate the root nodes handle back to the parent + */ + updateTag: PropTypes.func + +}; diff --git a/components/SectionList.js b/components/SectionList.js index 2f4afe7..5d66b97 100644 --- a/components/SectionList.js +++ b/components/SectionList.js @@ -1,218 +1,161 @@ -'use strict'; - -import React, { - Component, - PropTypes, -} from 'react'; -import ReactNative, { - StyleSheet, - View, - Text, - NativeModules, -} from 'react-native'; - -const { UIManager } = NativeModules; - -const noop = () => {}; -const returnTrue = () => true; - -export default class SectionList extends Component { - - constructor(props, context) { - super(props, context); - - this.onSectionSelect = this.onSectionSelect.bind(this); - this.resetSection = this.resetSection.bind(this); - this.detectAndScrollToSection = this.detectAndScrollToSection.bind(this); - this.lastSelectedIndex = null; - } - - onSectionSelect(sectionId, fromTouch) { - this.props.onSectionSelect && this.props.onSectionSelect(sectionId); - - if (!fromTouch) { - this.lastSelectedIndex = null; - } - } - - resetSection() { - this.lastSelectedIndex = null; - } - - detectAndScrollToSection(e) { - const ev = e.nativeEvent.touches[0]; - //var rect = {width:1, height:1, x: ev.locationX, y: ev.locationY}; - //var rect = [ev.locationX, ev.locationY]; - - //UIManager.measureViewsInRect(rect, e.target, noop, (frames) => { - // if (frames.length) { - // var index = frames[0].index; - // if (this.lastSelectedIndex !== index) { - // this.lastSelectedIndex = index; - // this.onSectionSelect(this.props.sections[index], true); - // } - // } - //}); - //UIManager.findSubviewIn(e.target, rect, viewTag => { - //this.onSectionSelect(view, true); - //}) - const targetY = ev.pageY; - const { y, width, height } = this.measure; - if(!y || targetY < y){ - return; - } - let index = Math.floor((targetY - y) / height); - index = Math.min(index, this.props.sections.length - 1); - if (this.lastSelectedIndex !== index && this.props.data[this.props.sections[index]].length) { - this.lastSelectedIndex = index; - this.onSectionSelect(this.props.sections[index], true); - } - } - - fixSectionItemMeasure() { - const sectionItem = this.refs.sectionItem0; - if (!sectionItem) { - return; - } - this.measureTimer = setTimeout(() => { - sectionItem.measure((x, y, width, height, pageX, pageY) => { - //console.log([x, y, width, height, pageX, pageY]); - this.measure = { - y: pageY, - width, - height - }; - }) - }, 0); - } - - componentDidMount() { - this.fixSectionItemMeasure(); - } - - // fix bug when change data - componentDidUpdate() { - this.fixSectionItemMeasure(); - } - - componentWillUnmount() { - this.measureTimer && clearTimeout(this.measureTimer); - } - - render() { - const SectionComponent = this.props.component; - const sections = this.props.sections.map((section, index) => { - const title = this.props.getSectionListTitle ? - this.props.getSectionListTitle(section) : - section; - - const textStyle = this.props.data[section].length ? - styles.text : - styles.inactivetext; - - const child = SectionComponent ? - : - - {title} - ; - - //if(index){ - return ( - - {child} - - ); - //} - //else{ - // return ( - // {console.log(e.nativeEvent.layout)}}> - // {child} - // - // ); - // - //} - }); - - return ( - - {sections} - - ); - } -} - -SectionList.propTypes = { - - /** - * A component to render for each section item - */ - component: PropTypes.func, - - /** - * Function to provide a title the section list items. - */ - getSectionListTitle: PropTypes.func, - - /** - * Function to be called upon selecting a section list item - */ - onSectionSelect: PropTypes.func, - - /** - * The sections to render - */ - sections: PropTypes.array.isRequired, - - /** - * A style to apply to the section list container - */ - style: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.object, - ]), - - /** - * Text font size - */ - fontStyle: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.object, - ]), -}; - -const styles = StyleSheet.create({ - container: { - position: 'absolute', - backgroundColor: 'transparent', - alignItems:'flex-end', - justifyContent:'center', - right: 5, - top: 0, - bottom: 0 - }, - - item: { - padding: 0 - }, - - text: { - fontWeight: '700', - color: '#008fff' - }, - - inactivetext: { - fontWeight: '700', - color: '#CCCCCC' - } -}); +'use strict'; + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactNative, { + StyleSheet, + View, + Text, + NativeModules, +} from 'react-native'; + +const { UIManager } = NativeModules; + +var noop = () => {}; +var returnTrue = () => true; +var firstSectionOffset = 0; +var sectionHeight = 0; + +class SectionList extends Component { + + constructor(props, context) { + super(props, context); + + this.onSectionSelect = this.onSectionSelect.bind(this); + this.resetSection = this.resetSection.bind(this); + this.detectAndScrollToSection = this.detectAndScrollToSection.bind(this); + this.onLayout = this.onLayout.bind(this); + this.lastSelectedIndex = null; + } + + onSectionSelect(sectionId, fromTouch) { + this.props.onSectionSelect && this.props.onSectionSelect(sectionId); + + if (!fromTouch) { + this.lastSelectedIndex = null; + } + } + + resetSection() { + this.lastSelectedIndex = null; + } + + detectAndScrollToSection(e) { + var ev = e.nativeEvent.touches[0]; + + let targetY = ev.locationY; + let index = targetY < firstSectionOffset ? 0 : Math.floor((targetY - firstSectionOffset) / sectionHeight); + + index = Math.min(index, this.props.sections.length - 1); + + //console.log('check if section contains data: ', this.props.data[this.props.sections[index]].length); + if (this.lastSelectedIndex !== index) { + this.lastSelectedIndex = index; + this.onSectionSelect(this.props.sections[index], true); + } + } + + onLayout(event) { + firstSectionOffset = event.nativeEvent.layout.y; + sectionHeight = event.nativeEvent.layout.height; + } + + render() { + var SectionComponent = this.props.component; + var sections = this.props.sections.map((section, index) => { + var title = this.props.getSectionListTitle ? + this.props.getSectionListTitle(section) : + section; + + var textStyle = this.props.data[section].length ? + styles.text : + styles.inactivetext; + + var child = SectionComponent ? + : + + {title} + ; + + return ( + + {child} + + ); + }); + + return ( + + {sections} + + ); + } +} + +SectionList.propTypes = { + + /** + * A component to render for each section item + */ + component: PropTypes.func, + + /** + * Function to provide a title the section list items. + */ + getSectionListTitle: PropTypes.func, + + /** + * Function to be called upon selecting a section list item + */ + onSectionSelect: PropTypes.func, + + /** + * The sections to render + */ + sections: PropTypes.array.isRequired, + + /** + * A style to apply to the section list container + */ + style: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.object, + ]) +}; + +var styles = StyleSheet.create({ + container: { + position: 'absolute', + backgroundColor: 'transparent', + alignItems:'center', + justifyContent:'center', + right: 0, + top: 0, + bottom: 0, + width: 15 + }, + + item: { + padding: 0 + }, + + text: { + fontWeight: '700', + color: '#008fff' + }, + + inactivetext: { + fontWeight: '700', + color: '#CCCCCC' + } +}); diff --git a/components/SelectableSectionsListView.js b/components/SelectableSectionsListView.js index a2d4510..1160125 100644 --- a/components/SelectableSectionsListView.js +++ b/components/SelectableSectionsListView.js @@ -1,401 +1,385 @@ -'use strict'; -/* jshint esnext: true */ - -import React, { - Component, - PropTypes, -} from 'react'; -import ReactNative, { - ListView, - StyleSheet, - View, - NativeModules, -} from 'react-native'; -import merge from 'merge'; - -import SectionHeader from './SectionHeader'; -import SectionList from './SectionList'; -import CellWrapper from './CellWrapper'; - -const { UIManager } = NativeModules; - -export default class SelectableSectionsListView extends Component { - - constructor(props, context) { - super(props, context); - - this.state = { - dataSource: new ListView.DataSource({ - rowHasChanged: (row1, row2) => row1 !== row2, - sectionHeaderHasChanged: (prev, next) => prev !== next - }), - offsetY: 0 - }; - - this.renderFooter = this.renderFooter.bind(this); - this.renderHeader = this.renderHeader.bind(this); - this.renderRow = this.renderRow.bind(this); - this.renderSectionHeader = this.renderSectionHeader.bind(this); - - this.onScroll = this.onScroll.bind(this); - this.onScrollAnimationEnd = this.onScrollAnimationEnd.bind(this); - this.scrollToSection = this.scrollToSection.bind(this); - - // used for dynamic scrolling - // always the first cell of a section keyed by section id - this.cellTagMap = {}; - this.sectionTagMap = {}; - this.updateTagInCellMap = this.updateTagInCellMap.bind(this); - this.updateTagInSectionMap = this.updateTagInSectionMap.bind(this); - } - - componentWillMount() { - this.calculateTotalHeight(); - } - - componentDidMount() { - // push measuring into the next tick - setTimeout(() => { - UIManager.measure(ReactNative.findNodeHandle(this.refs.view), (x,y,w,h) => { - this.containerHeight = h; - }); - }, 0); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.data && nextProps.data !== this.props.data) { - this.calculateTotalHeight(nextProps.data); - } - } - - calculateTotalHeight(data) { - data = data || this.props.data; - - if (Array.isArray(data)) { - return; - } - - this.sectionItemCount = {}; - this.totalHeight = Object.keys(data) - .reduce((carry, key) => { - var itemCount = data[key].length; - carry += itemCount * this.props.cellHeight; - carry += this.props.sectionHeaderHeight; - - this.sectionItemCount[key] = itemCount; - - return carry; - }, 0); - } - - updateTagInSectionMap(tag, section) { - this.sectionTagMap[section] = tag; - } - - updateTagInCellMap(tag, section) { - this.cellTagMap[section] = tag; - } - - scrollToSection(section) { - let y = 0; - let headerHeight = this.props.headerHeight || 0; - y += headerHeight; - - if (!this.props.useDynamicHeights) { - const cellHeight = this.props.cellHeight; - let sectionHeaderHeight = this.props.sectionHeaderHeight; - let keys = Object.keys(this.props.data); - if (typeof(this.props.compareFunction) === "function") { - keys = keys.sort(this.props.compareFunction); - } - const index = keys.indexOf(section); - - let numcells = 0; - for (var i = 0; i < index; i++) { - numcells += this.props.data[keys[i]].length; - } - - sectionHeaderHeight = index * sectionHeaderHeight; - y += numcells * cellHeight + sectionHeaderHeight; - const maxY = this.totalHeight - this.containerHeight + headerHeight; - y = y > maxY ? maxY : y; - - this.refs.listview.scrollTo({ x:0, y, animated: true }); - } else { - UIManager.measureLayout(this.cellTagMap[section], ReactNative.findNodeHandle(this.refs.listview), () => {}, (x, y, w, h) => { - y = y - this.props.sectionHeaderHeight; - this.refs.listview.scrollTo({ x:0, y, animated: true }); - }); - } - - this.props.onScrollToSection && this.props.onScrollToSection(section); - } - - renderSectionHeader(sectionData, sectionId) { - const updateTag = this.props.useDynamicHeights ? - this.updateTagInSectionMap : - null; - - const title = this.props.getSectionTitle ? - this.props.getSectionTitle(sectionId) : - sectionId; - - return ( - - ); - } - - renderFooter() { - const Footer = this.props.footer; - return