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 ;
- }
-
- renderHeader() {
- const Header = this.props.header;
- return ;
- }
-
- renderRow(item, sectionId, index) {
- const CellComponent = this.props.cell;
- index = parseInt(index, 10);
-
- const isFirst = index === 0;
- const isLast = this.sectionItemCount[sectionId]-1 === index;
-
- const props = {
- isFirst,
- isLast,
- sectionId,
- index,
- item,
- offsetY: this.state.offsetY,
- onSelect: this.props.onCellSelect
- };
-
- return index === 0 && this.props.useDynamicHeights ?
- :
- ;
- }
-
- onScroll(e) {
- const offsetY = e.nativeEvent.contentOffset.y;
- if (this.props.updateScrollState) {
- this.setState({
- offsetY
- });
- }
-
- this.props.onScroll && this.props.onScroll(e);
- }
-
- onScrollAnimationEnd(e) {
- if (this.props.updateScrollState) {
- this.setState({
- offsetY: e.nativeEvent.contentOffset.y
- });
- }
- }
-
- render() {
- const { data } = this.props;
- const dataIsArray = Array.isArray(data);
- let sectionList;
- let renderSectionHeader;
- let dataSource;
- let sections = Object.keys(data);
-
- if (typeof(this.props.compareFunction) === "function") {
- sections = sections.sort(this.props.compareFunction);
- }
-
- if (dataIsArray) {
- dataSource = this.state.dataSource.cloneWithRows(data);
- } else {
- sectionList = !this.props.hideSectionList ?
- :
- null;
-
- renderSectionHeader = this.renderSectionHeader;
- dataSource = this.state.dataSource.cloneWithRowsAndSections(data, sections);
- }
-
- const renderFooter = this.props.footer ?
- this.renderFooter :
- this.props.renderFooter;
-
- const renderHeader = this.props.header ?
- this.renderHeader :
- this.props.renderHeader;
-
- const props = merge({}, this.props, {
- onScroll: this.onScroll,
- onScrollAnimationEnd: this.onScrollAnimationEnd,
- dataSource,
- renderFooter,
- renderHeader,
- renderRow: this.renderRow,
- renderSectionHeader
- });
-
- props.style = void 0;
-
- return (
-
-
- {sectionList}
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1
- }
-});
-
-const stylesheetProp = PropTypes.oneOfType([
- PropTypes.number,
- PropTypes.object,
-]);
-
-SelectableSectionsListView.propTypes = {
- /**
- * The data to render in the listview
- */
- data: PropTypes.oneOfType([
- PropTypes.array,
- PropTypes.object,
- ]).isRequired,
-
- /**
- * Whether to show the section listing or not
- */
- hideSectionList: PropTypes.bool,
-
- /**
- * Functions to provide a title for the section header and the section list
- * items. If not provided, the section ids will be used (the keys from the data object)
- */
- getSectionTitle: PropTypes.func,
- getSectionListTitle: PropTypes.func,
-
- /**
- * Function to sort sections. If not provided, the sections order will match data source
- */
- compareFunction: PropTypes.func,
-
- /**
- * Callback which should be called when a cell has been selected
- */
- onCellSelect: PropTypes.func,
-
- /**
- * Callback which should be called when the user scrolls to a section
- */
- onScrollToSection: PropTypes.func,
-
- /**
- * The cell element to render for each row
- */
- cell: PropTypes.func.isRequired,
-
- /**
- * A custom element to render for each section list item
- */
- sectionListItem: PropTypes.func,
-
- /**
- * A custom element to render for each section header
- */
- sectionHeader: PropTypes.func,
-
- /**
- * A custom element to render as footer
- */
- footer: PropTypes.func,
-
- /**
- * A custom element to render as header
- */
- header: PropTypes.func,
-
- /**
- * The height of the header element to render. Is required if a
- * header element is used, so the positions can be calculated correctly
- */
- headerHeight: PropTypes.number,
-
- /**
- * A custom function to render as footer
- */
- renderHeader: PropTypes.func,
-
- /**
- * A custom function to render as header
- */
- renderFooter: PropTypes.func,
-
- /**
- * An object containing additional props, which will be passed
- * to each cell component
- */
- cellProps: PropTypes.object,
-
- /**
- * The height of the section header component
- */
- sectionHeaderHeight: PropTypes.number.isRequired,
-
- /**
- * The height of the cell component
- */
- cellHeight: PropTypes.number.isRequired,
-
- /**
- * Whether to determine the y postion to scroll to by calculating header and
- * cell heights or by using the UIManager to measure the position of the
- * destination element. This is an exterimental feature
- */
- useDynamicHeights: PropTypes.bool,
-
- /**
- * Whether to set the current y offset as state and pass it to each
- * cell during re-rendering
- */
- updateScrollState: PropTypes.bool,
-
- /**
- * Styles to pass to the container
- */
- style: stylesheetProp,
-
- /**
- * Styles to pass to the section list container
- */
- sectionListStyle: stylesheetProp,
-
- /**
- * Selector styles
- */
- sectionListFontStyle: stylesheetProp,
-};
+'use strict';
+/* jshint esnext: true */
+
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+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) {
+ console.log('total height : ', this.calculateTotalHeight(nextProps.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) {
+ var y = 0;
+ var headerHeight = this.props.headerHeight || 0;
+ y += headerHeight;
+
+ if (!this.props.useDynamicHeights) {
+ var cellHeight = this.props.cellHeight;
+ var sectionHeaderHeight = this.props.sectionHeaderHeight;
+ var keys = Object.keys(this.props.data);
+ var index = keys.indexOf(section);
+
+ var numcells = 0;
+ for (var i = 0; i < index; i++) {
+ numcells += this.props.data[keys[i]].length;
+ }
+
+ sectionHeaderHeight = index * sectionHeaderHeight;
+ y += numcells * cellHeight + sectionHeaderHeight;
+
+ var maxY = this.totalHeight - this.containerHeight + headerHeight;
+ y = y > maxY ? maxY : y;
+
+ this.refs.listview.scrollTo({ x:0, y, animated: true });
+ } else {
+ // this breaks, if not all of the listview is pre-rendered!
+ UIManager.measure(this.cellTagMap[section], (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) {
+ var updateTag = this.props.useDynamicHeights ?
+ this.updateTagInSectionMap :
+ null;
+
+ var title = this.props.getSectionTitle ?
+ this.props.getSectionTitle(sectionId) :
+ sectionId;
+
+ return (
+
+ );
+ }
+
+ renderFooter() {
+ var Footer = this.props.footer;
+ return ;
+ }
+
+ renderHeader() {
+ var Header = this.props.header;
+ return ;
+ }
+
+ renderRow(item, sectionId, index) {
+ var CellComponent = this.props.cell;
+ index = parseInt(index, 10);
+
+ var isFirst = index === 0;
+ var isLast = this.sectionItemCount[sectionId]-1 === index;
+
+ var props = {
+ isFirst,
+ isLast,
+ sectionId,
+ index,
+ item,
+ offsetY: this.state.offsetY,
+ onSelect: this.props.onCellSelect
+ };
+
+ return index === 0 && this.props.useDynamicHeights ?
+ :
+ ;
+ }
+
+ onScroll(e) {
+ var offsetY = e.nativeEvent.contentOffset.y;
+ if (this.props.updateScrollState) {
+ this.setState({
+ offsetY
+ });
+ }
+
+ this.props.onScroll && this.props.onScroll(e);
+ }
+
+ onScrollAnimationEnd(e) {
+ if (this.props.updateScrollState) {
+ this.setState({
+ offsetY: e.nativeEvent.contentOffset.y
+ });
+ }
+ }
+
+ render() {
+ var data = this.props.data;
+ var dataIsArray = Array.isArray(data);
+ var sectionList;
+ var renderSectionHeader;
+ var dataSource;
+
+ if (dataIsArray) {
+ dataSource = this.state.dataSource.cloneWithRows(data);
+ } else {
+ sectionList = !this.props.hideSectionList ?
+ :
+ null;
+
+ renderSectionHeader = this.renderSectionHeader;
+ dataSource = this.state.dataSource.cloneWithRowsAndSections(data);
+ }
+
+ var renderFooter = this.props.footer ?
+ this.renderFooter :
+ this.props.renderFooter;
+
+ var renderHeader = this.props.header ?
+ this.renderHeader :
+ this.props.renderHeader;
+
+ var props = merge({}, this.props, {
+ onScroll: this.onScroll,
+ onScrollAnimationEnd: this.onScrollAnimationEnd,
+ dataSource,
+ renderFooter,
+ renderHeader,
+ renderRow: this.renderRow,
+ renderSectionHeader
+ });
+
+ props.style = void 0;
+
+ return (
+
+
+ {sectionList}
+
+ );
+ }
+}
+
+var styles = StyleSheet.create({
+ container: {
+ flex: 1
+ }
+});
+
+var stylesheetProp = PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.object,
+]);
+
+SelectableSectionsListView.propTypes = {
+ /**
+ * The data to render in the listview
+ */
+ data: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.object,
+ ]).isRequired,
+
+ /**
+ * Whether to show the section listing or not
+ */
+ hideSectionList: PropTypes.bool,
+
+ /**
+ * Functions to provide a title for the section header and the section list
+ * items. If not provided, the section ids will be used (the keys from the data object)
+ */
+ getSectionTitle: PropTypes.func,
+ getSectionListTitle: PropTypes.func,
+
+ /**
+ * Callback which should be called when a cell has been selected
+ */
+ onCellSelect: PropTypes.func,
+
+ /**
+ * Callback which should be called when the user scrolls to a section
+ */
+ onScrollToSection: PropTypes.func,
+
+ /**
+ * The cell element to render for each row
+ */
+ cell: PropTypes.func.isRequired,
+
+ /**
+ * A custom element to render for each section list item
+ */
+ sectionListItem: PropTypes.func,
+
+ /**
+ * A custom element to render for each section header
+ */
+ sectionHeader: PropTypes.func,
+
+ /**
+ * A custom element to render as footer
+ */
+ footer: PropTypes.func,
+
+ /**
+ * A custom element to render as header
+ */
+ header: PropTypes.func,
+
+ /**
+ * The height of the header element to render. Is required if a
+ * header element is used, so the positions can be calculated correctly
+ */
+ headerHeight: PropTypes.number,
+
+ /**
+ * A custom function to render as footer
+ */
+ renderHeader: PropTypes.func,
+
+ /**
+ * A custom function to render as header
+ */
+ renderFooter: PropTypes.func,
+
+ /**
+ * An object containing additional props, which will be passed
+ * to each cell component
+ */
+ cellProps: PropTypes.object,
+
+ /**
+ * The height of the section header component
+ */
+ sectionHeaderHeight: PropTypes.number.isRequired,
+
+ /**
+ * The height of the cell component
+ */
+ cellHeight: PropTypes.number.isRequired,
+
+ /**
+ * Whether to determine the y postion to scroll to by calculating header and
+ * cell heights or by using the UIManager to measure the position of the
+ * destination element. This is an exterimental feature
+ */
+ useDynamicHeights: PropTypes.bool,
+
+ /**
+ * Whether to set the current y offset as state and pass it to each
+ * cell during re-rendering
+ */
+ updateScrollState: PropTypes.bool,
+
+ /**
+ * Styles to pass to the container
+ */
+ style: stylesheetProp,
+
+ /**
+ * Styles to pass to the section list container
+ */
+ sectionListStyle: stylesheetProp
+
+};