├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── components ├── CellWrapper.js ├── SectionHeader.js ├── SectionList.js └── SelectableSectionsListView.js ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | workbench 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "-W093": true, 3 | "asi": false, 4 | "bitwise": true, 5 | "boss": false, 6 | "browser": false, 7 | "camelcase": true, 8 | "couch": false, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "dojo": false, 13 | "eqeqeq": true, 14 | "eqnull": false, 15 | "esnext": true, 16 | "evil": false, 17 | "expr": true, 18 | "forin": false, 19 | "freeze": true, 20 | "funcscope": true, 21 | "gcl": false, 22 | "globalstrict": true, 23 | "immed": false, 24 | "indent": 2, 25 | "iterator": false, 26 | "jquery": false, 27 | "lastsemic": false, 28 | "latedef": false, 29 | "laxbreak": true, 30 | "laxcomma": false, 31 | "loopfunc": false, 32 | "maxcomplexity": false, 33 | "maxdepth": false, 34 | "maxerr": 50, 35 | "maxlen": 80, 36 | "maxparams": false, 37 | "maxstatements": false, 38 | "mootools": false, 39 | "moz": false, 40 | "multistr": false, 41 | "newcap": true, 42 | "noarg": true, 43 | "node": true, 44 | "noempty": true, 45 | "nonbsp": true, 46 | "nonew": true, 47 | "nonstandard": false, 48 | "notypeof": false, 49 | "noyield": false, 50 | "phantom": false, 51 | "plusplus": false, 52 | "predef": [ 53 | "jasmine", 54 | "describe", 55 | "beforeEach", 56 | "it", 57 | "jest", 58 | "pit", 59 | "expect", 60 | "rootRequire" 61 | ], 62 | "proto": false, 63 | "prototypejs": false, 64 | "quotmark": true, 65 | "rhino": false, 66 | "scripturl": false, 67 | "shadow": false, 68 | "smarttabs": false, 69 | "strict": true, 70 | "sub": false, 71 | "supernew": false, 72 | "trailing": true, 73 | "undef": true, 74 | "unused": true, 75 | "validthis": false, 76 | "worker": false, 77 | "wsh": false, 78 | "yui": false 79 | } 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Johannes Lumpe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Based on johanneslumpe's react-native-selectablesectionlistview, thanks to him for the awesome component! 2 | 99% of this component was done by @johanneslumpe, and I just replaced the deprecated API for newer react native version(>=0.13), and used a little trick to support both iOS and Android platforms. 3 | 4 | 5 | You can find this component on npm: 6 | ``` 7 | npm install react-native-alphabetlistview --save 8 | ``` 9 | 10 | ## Changelog 11 | 12 | - **v2.0.0** 13 | + Support RN 0.25+.(Thanks [@mbernardeau](https://github.com/mbernardeau)). If you have problem with an earlier version of RN, try v0.1.3. 14 | 15 | 16 | 17 | 18 | # Following is the original readme 19 | 20 | A Listview with a sidebar to directly jump to sections. 21 | 22 | Please file issues for missing features or bugs. 23 | 24 | I apologize for the bad name. 25 | 26 | ![How it looks](http://lum.pe/sectionlistview.gif) 27 | 28 | ## Usage 29 | 30 | The most basic way to use this component is as follows: 31 | 32 | ```javascript 33 | var AlphabetListView = require('react-native-alphabetlistview'); 34 | 35 | // inside your render function 36 | 42 | ``` 43 | 44 | You can find a more complete example below 45 | 46 | ## Props 47 | 48 | ### SelectableSectionsListView 49 | 50 | All props are passed through to the underlying `ListView`, so you can specify all the available props for `ListView` normally - except the following, which are defined internally and will be overwritten: 51 | 52 | * `onScroll` 53 | * `onScrollAnimationEnd` 54 | * `dataSource` 55 | * `renderRow` 56 | * `renderSectionHeader` 57 | 58 | #### data 59 | `array|object`, **required** 60 | The data to render in the listview 61 | 62 | #### hideSectionList 63 | `boolean` 64 | Whether to show the section listing or not. *Note: If the data your are providing to 65 | the component is an array, the section list will automatically be hidden.* 66 | 67 | #### getSectionTitle 68 | `function` 69 | Function to provide titles for the section headers 70 | 71 | #### getSectionListTitle 72 | `function` 73 | Function to provide titles for the section list items 74 | 75 | #### onCellSelect 76 | `function` 77 | Callback which should be called when a cell has been selected 78 | 79 | #### onScrollToSection 80 | `function` 81 | Callback which should be called when the user scrolls to a section 82 | 83 | #### cell 84 | `function` **required** 85 | The cell component to render for each row 86 | 87 | #### sectionListItem 88 | `function` 89 | A custom component to render for each section list item 90 | 91 | #### sectionHeader 92 | `function` 93 | A custom component to render for each section header 94 | 95 | #### footer 96 | `function` 97 | A custom component to render as footer 98 | **This props takes precedence over `renderFooter`** 99 | 100 | #### renderFooter 101 | `function` 102 | A custom function which has to return a valid React element, which will be 103 | used as footer. 104 | 105 | #### header 106 | `function` 107 | A custom component to render as header 108 | **This props takes precedence over `renderHeader`** 109 | 110 | #### renderHeader 111 | `function` 112 | A custom function which has to return a valid React element, which will be used as header. 113 | 114 | #### headerHeight 115 | `number` 116 | The height of the rendered header element. 117 | **Is required if a header element is used, so the positions can be calculated correctly** 118 | 119 | #### cellProps 120 | `object` 121 | An object containing additional props, which will be passed to each cell component 122 | 123 | #### sectionHeaderHeight 124 | `number` **required** 125 | The height of the section header component 126 | 127 | #### cellHeight 128 | `number` **required** 129 | The height of the cell component 130 | 131 | #### useDynamicHeights 132 | `boolean` 133 | Whether to determine the y position to scroll to by calculating header and cell heights or by using the UIManager to measure the position of the destination element. Defaults to `false` 134 | **This is an experimental feature. For it to work properly you will most likely have to experiment with different values for `scrollRenderAheadDistance`, depending on the size of your data set.** 135 | 136 | #### updateScrollState 137 | `boolean` 138 | Whether to set the current y offset as state and pass it to each cell during re-rendering 139 | 140 | #### style 141 | `object|number` 142 | Styles to pass to the container 143 | 144 | #### sectionListStyle 145 | `object|number` 146 | Styles to pass to the section list container 147 | 148 | #### sectionListFontStyle 149 | `object|number` 150 | Styles to pass to the section list letters 151 | 152 | --- 153 | ### Cell component 154 | 155 | These props are automatically passed to your component. In addition to these, your cell will receive all props which you specified in the object you passed as `cellProps` prop to the listview. 156 | 157 | #### index 158 | `number` 159 | The index of the cell inside the current section 160 | 161 | #### sectionId 162 | `string` 163 | The id of the parent section 164 | 165 | #### isFirst 166 | `boolean` 167 | Whether the cell is the first in the section 168 | 169 | #### isLast 170 | `boolean` 171 | Whether the cell is the last in the section 172 | 173 | #### item 174 | `mixed` 175 | The item to render 176 | 177 | #### offsetY 178 | `number` 179 | The current y offset of the list view 180 | **If you do not specify `updateScrollState={true}` for the list component, this props will always be 0** 181 | 182 | #### onSelect 183 | `function` 184 | The function which should be called when a cell is being selected 185 | 186 | --- 187 | ### Section list item component 188 | 189 | These props are automatically passed to your component 190 | 191 | #### sectionId 192 | `string` 193 | The id of the parent section 194 | 195 | #### title 196 | `string` 197 | The title for this section. Either the return value of `getSectionListTitle` or the same value as `sectionId` 198 | 199 | ## Example 200 | 201 | ```javascript 202 | class SectionHeader extends Component { 203 | render() { 204 | // inline styles used for brevity, use a stylesheet when possible 205 | var textStyle = { 206 | textAlign:'center', 207 | color:'#fff', 208 | fontWeight:'700', 209 | fontSize:16 210 | }; 211 | 212 | var viewStyle = { 213 | backgroundColor: '#ccc' 214 | }; 215 | return ( 216 | 217 | {this.props.title} 218 | 219 | ); 220 | } 221 | } 222 | 223 | class SectionItem extends Component { 224 | render() { 225 | return ( 226 | {this.props.title} 227 | ); 228 | } 229 | } 230 | 231 | class Cell extends Component { 232 | render() { 233 | return ( 234 | 235 | {this.props.item} 236 | 237 | ); 238 | } 239 | } 240 | 241 | class MyComponent extends Component { 242 | 243 | constructor(props, context) { 244 | super(props, context); 245 | 246 | this.state = { 247 | data: { 248 | A: ['some','entries','are here'], 249 | B: ['some','entries','are here'], 250 | C: ['some','entries','are here'], 251 | D: ['some','entries','are here'], 252 | E: ['some','entries','are here'], 253 | F: ['some','entries','are here'], 254 | G: ['some','entries','are here'], 255 | H: ['some','entries','are here'], 256 | I: ['some','entries','are here'], 257 | J: ['some','entries','are here'], 258 | K: ['some','entries','are here'], 259 | L: ['some','entries','are here'], 260 | M: ['some','entries','are here'], 261 | N: ['some','entries','are here'], 262 | O: ['some','entries','are here'], 263 | P: ['some','entries','are here'], 264 | Q: ['some','entries','are here'], 265 | R: ['some','entries','are here'], 266 | S: ['some','entries','are here'], 267 | T: ['some','entries','are here'], 268 | U: ['some','entries','are here'], 269 | V: ['some','entries','are here'], 270 | W: ['some','entries','are here'], 271 | X: ['some','entries','are here'], 272 | Y: ['some','entries','are here'], 273 | Z: ['some','entries','are here'], 274 | } 275 | }; 276 | } 277 | 278 | render() { 279 | return ( 280 | 288 | ); 289 | } 290 | } 291 | 292 | ``` 293 | -------------------------------------------------------------------------------- /components/CellWrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative, { 6 | View 7 | } from 'react-native'; 8 | 9 | export default class CellWrapper extends Component { 10 | componentDidMount() { 11 | this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); 12 | } 13 | 14 | render() { 15 | const Cell = this.props.component; 16 | return ( 17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | 24 | CellWrapper.propTypes = { 25 | 26 | /** 27 | * The id of the section 28 | */ 29 | sectionId: PropTypes.oneOfType([ 30 | PropTypes.number, 31 | PropTypes.string 32 | ]), 33 | 34 | /** 35 | * A component to render for each cell 36 | */ 37 | component: PropTypes.func.isRequired, 38 | 39 | /** 40 | * A function used to propagate the root nodes handle back to the parent 41 | */ 42 | updateTag: PropTypes.func 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /components/SectionHeader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative, { 6 | StyleSheet, 7 | View, 8 | Text, 9 | NativeModules 10 | } from 'react-native'; 11 | 12 | const { UIManager } = NativeModules; 13 | 14 | export default class SectionHeader extends Component { 15 | 16 | componentDidMount() { 17 | this.props.updateTag && this.props.updateTag(ReactNative.findNodeHandle(this.refs.view), this.props.sectionId); 18 | } 19 | 20 | render() { 21 | const SectionComponent = this.props.component; 22 | const content = SectionComponent ? 23 | : 24 | {this.props.title}; 25 | 26 | return ( 27 | 28 | {content} 29 | 30 | ); 31 | } 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | container: { 36 | backgroundColor: '#f8f8f8', 37 | borderTopWidth: 1, 38 | borderTopColor: '#ececec' 39 | }, 40 | text: { 41 | fontWeight: '700', 42 | paddingTop: 2, 43 | paddingBottom: 2, 44 | paddingLeft: 2 45 | } 46 | }); 47 | 48 | SectionHeader.propTypes = { 49 | 50 | /** 51 | * The id of the section 52 | */ 53 | sectionId: PropTypes.oneOfType([ 54 | PropTypes.number, 55 | PropTypes.string 56 | ]), 57 | 58 | /** 59 | * A component to render for each section item 60 | */ 61 | component: PropTypes.func, 62 | 63 | /** 64 | * A function used to propagate the root nodes handle back to the parent 65 | */ 66 | updateTag: PropTypes.func 67 | }; 68 | -------------------------------------------------------------------------------- /components/SectionList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React, { Component } from 'react'; 4 | import PropTypes from 'prop-types'; 5 | import ReactNative, { 6 | StyleSheet, 7 | View, 8 | Text, 9 | NativeModules, 10 | } from 'react-native'; 11 | 12 | const { UIManager } = NativeModules; 13 | 14 | const noop = () => {}; 15 | const returnTrue = () => true; 16 | 17 | export default class SectionList extends Component { 18 | 19 | constructor(props, context) { 20 | super(props, context); 21 | 22 | this.onSectionSelect = this.onSectionSelect.bind(this); 23 | this.resetSection = this.resetSection.bind(this); 24 | this.detectAndScrollToSection = this.detectAndScrollToSection.bind(this); 25 | this.lastSelectedIndex = null; 26 | } 27 | 28 | onSectionSelect(sectionId, fromTouch) { 29 | this.props.onSectionSelect && this.props.onSectionSelect(sectionId); 30 | 31 | if (!fromTouch) { 32 | this.lastSelectedIndex = null; 33 | } 34 | } 35 | 36 | resetSection() { 37 | this.lastSelectedIndex = null; 38 | } 39 | 40 | detectAndScrollToSection(e) { 41 | const ev = e.nativeEvent.touches[0]; 42 | //var rect = {width:1, height:1, x: ev.locationX, y: ev.locationY}; 43 | //var rect = [ev.locationX, ev.locationY]; 44 | 45 | //UIManager.measureViewsInRect(rect, e.target, noop, (frames) => { 46 | // if (frames.length) { 47 | // var index = frames[0].index; 48 | // if (this.lastSelectedIndex !== index) { 49 | // this.lastSelectedIndex = index; 50 | // this.onSectionSelect(this.props.sections[index], true); 51 | // } 52 | // } 53 | //}); 54 | //UIManager.findSubviewIn(e.target, rect, viewTag => { 55 | //this.onSectionSelect(view, true); 56 | //}) 57 | const targetY = ev.pageY; 58 | const { y, width, height } = this.measure; 59 | const index = (Math.floor(ev.locationY / height)); 60 | if (index >= this.props.sections.length || index < 0) { 61 | return; 62 | } 63 | 64 | if (this.lastSelectedIndex !== index && this.props.data[this.props.sections[index]].length) { 65 | this.lastSelectedIndex = index; 66 | this.onSectionSelect(this.props.sections[index], true); 67 | } 68 | } 69 | 70 | fixSectionItemMeasure() { 71 | const sectionItem = this.refs.sectionItem0; 72 | if (!sectionItem) { 73 | return; 74 | } 75 | this.measureTimer = setTimeout(() => { 76 | sectionItem.measure((x, y, width, height, pageX, pageY) => { 77 | //console.log([x, y, width, height, pageX, pageY]); 78 | this.measure = { 79 | y: pageY, 80 | width, 81 | height 82 | }; 83 | }) 84 | }, 0); 85 | } 86 | 87 | componentDidMount() { 88 | this.fixSectionItemMeasure(); 89 | } 90 | 91 | // fix bug when change data 92 | componentDidUpdate() { 93 | this.fixSectionItemMeasure(); 94 | } 95 | 96 | componentWillUnmount() { 97 | this.measureTimer && clearTimeout(this.measureTimer); 98 | } 99 | 100 | render() { 101 | const SectionComponent = this.props.component; 102 | const sections = this.props.sections.map((section, index) => { 103 | const title = this.props.getSectionListTitle ? 104 | this.props.getSectionListTitle(section) : 105 | section; 106 | 107 | const textStyle = this.props.data[section].length ? 108 | styles.text : 109 | styles.inactivetext; 110 | 111 | const child = SectionComponent ? 112 | : 116 | 118 | {title} 119 | ; 120 | 121 | //if(index){ 122 | return ( 123 | 124 | {child} 125 | 126 | ); 127 | //} 128 | //else{ 129 | // return ( 130 | // {console.log(e.nativeEvent.layout)}}> 132 | // {child} 133 | // 134 | // ); 135 | // 136 | //} 137 | }); 138 | 139 | return ( 140 | 147 | {sections} 148 | 149 | ); 150 | } 151 | } 152 | 153 | SectionList.propTypes = { 154 | 155 | /** 156 | * A component to render for each section item 157 | */ 158 | component: PropTypes.func, 159 | 160 | /** 161 | * Function to provide a title the section list items. 162 | */ 163 | getSectionListTitle: PropTypes.func, 164 | 165 | /** 166 | * Function to be called upon selecting a section list item 167 | */ 168 | onSectionSelect: PropTypes.func, 169 | 170 | /** 171 | * The sections to render 172 | */ 173 | sections: PropTypes.array.isRequired, 174 | 175 | /** 176 | * A style to apply to the section list container 177 | */ 178 | style: PropTypes.oneOfType([ 179 | PropTypes.number, 180 | PropTypes.object, 181 | ]), 182 | 183 | /** 184 | * Text font size 185 | */ 186 | fontStyle: PropTypes.oneOfType([ 187 | PropTypes.number, 188 | PropTypes.object, 189 | ]), 190 | }; 191 | 192 | const styles = StyleSheet.create({ 193 | container: { 194 | position: 'absolute', 195 | backgroundColor: 'transparent', 196 | alignItems:'flex-end', 197 | justifyContent:'flex-start', 198 | right: 5, 199 | top: 0, 200 | bottom: 0 201 | }, 202 | 203 | item: { 204 | padding: 0 205 | }, 206 | 207 | text: { 208 | fontWeight: '700', 209 | color: '#008fff' 210 | }, 211 | 212 | inactivetext: { 213 | fontWeight: '700', 214 | color: '#CCCCCC' 215 | } 216 | }); 217 | -------------------------------------------------------------------------------- /components/SelectableSectionsListView.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* jshint esnext: true */ 3 | 4 | import React, { Component } from 'react'; 5 | import PropTypes from 'prop-types'; 6 | import ReactNative, { 7 | ListView, 8 | StyleSheet, 9 | View, 10 | NativeModules, 11 | } from 'react-native'; 12 | import merge from 'merge'; 13 | 14 | import SectionHeader from './SectionHeader'; 15 | import SectionList from './SectionList'; 16 | import CellWrapper from './CellWrapper'; 17 | 18 | const { UIManager } = NativeModules; 19 | 20 | export default class SelectableSectionsListView extends Component { 21 | 22 | constructor(props, context) { 23 | super(props, context); 24 | 25 | this.state = { 26 | dataSource: new ListView.DataSource({ 27 | rowHasChanged: (row1, row2) => row1 !== row2, 28 | sectionHeaderHasChanged: (prev, next) => prev !== next 29 | }), 30 | offsetY: 0 31 | }; 32 | 33 | this.renderFooter = this.renderFooter.bind(this); 34 | this.renderHeader = this.renderHeader.bind(this); 35 | this.renderRow = this.renderRow.bind(this); 36 | this.renderSectionHeader = this.renderSectionHeader.bind(this); 37 | 38 | this.onScroll = this.onScroll.bind(this); 39 | this.onScrollAnimationEnd = this.onScrollAnimationEnd.bind(this); 40 | this.scrollToSection = this.scrollToSection.bind(this); 41 | 42 | // used for dynamic scrolling 43 | // always the first cell of a section keyed by section id 44 | this.cellTagMap = {}; 45 | this.sectionTagMap = {}; 46 | this.updateTagInCellMap = this.updateTagInCellMap.bind(this); 47 | this.updateTagInSectionMap = this.updateTagInSectionMap.bind(this); 48 | } 49 | 50 | componentWillMount() { 51 | this.calculateTotalHeight(); 52 | } 53 | 54 | componentDidMount() { 55 | // push measuring into the next tick 56 | setTimeout(() => { 57 | UIManager.measure(ReactNative.findNodeHandle(this.refs.view), (x,y,w,h) => { 58 | this.containerHeight = h; 59 | if (this.props.contentInset && this.props.data && this.props.data.length > 0) { 60 | this.scrollToSection(Object.keys(this.props.data)[0]); 61 | } 62 | }); 63 | }, 0); 64 | } 65 | 66 | componentWillReceiveProps(nextProps) { 67 | if (nextProps.data && nextProps.data !== this.props.data) { 68 | this.calculateTotalHeight(nextProps.data); 69 | } 70 | } 71 | 72 | calculateTotalHeight(data) { 73 | data = data || this.props.data; 74 | 75 | if (Array.isArray(data)) { 76 | return; 77 | } 78 | 79 | this.sectionItemCount = {}; 80 | this.totalHeight = Object.keys(data) 81 | .reduce((carry, key) => { 82 | var itemCount = data[key].length; 83 | carry += itemCount * this.props.cellHeight; 84 | carry += this.props.sectionHeaderHeight; 85 | 86 | this.sectionItemCount[key] = itemCount; 87 | 88 | return carry; 89 | }, 0); 90 | } 91 | 92 | updateTagInSectionMap(tag, section) { 93 | this.sectionTagMap[section] = tag; 94 | } 95 | 96 | updateTagInCellMap(tag, section) { 97 | this.cellTagMap[section] = tag; 98 | } 99 | 100 | scrollToSection(section) { 101 | let y = 0; 102 | let headerHeight = this.props.headerHeight || 0; 103 | y += headerHeight; 104 | 105 | if(this.props.contentInset) { 106 | y -= this.props.contentInset.top - headerHeight 107 | } 108 | 109 | if (!this.props.useDynamicHeights) { 110 | const cellHeight = this.props.cellHeight; 111 | let sectionHeaderHeight = this.props.sectionHeaderHeight; 112 | let keys = Object.keys(this.props.data); 113 | if (typeof(this.props.compareFunction) === "function") { 114 | keys = keys.sort(this.props.compareFunction); 115 | } 116 | const index = keys.indexOf(section); 117 | 118 | let numcells = 0; 119 | for (var i = 0; i < index; i++) { 120 | numcells += this.props.data[keys[i]].length; 121 | } 122 | 123 | sectionHeaderHeight = index * sectionHeaderHeight; 124 | y += numcells * cellHeight + sectionHeaderHeight; 125 | const maxY = this.totalHeight - this.containerHeight + headerHeight; 126 | y = y > maxY ? maxY : y; 127 | 128 | this.refs.listview.scrollTo({ x:0, y, animated: true }); 129 | } else { 130 | UIManager.measureLayout(this.cellTagMap[section], ReactNative.findNodeHandle(this.refs.listview), () => {}, (x, y, w, h) => { 131 | y = y - this.props.sectionHeaderHeight; 132 | this.refs.listview.scrollTo({ x:0, y, animated: true }); 133 | }); 134 | } 135 | 136 | this.props.onScrollToSection && this.props.onScrollToSection(section); 137 | } 138 | 139 | renderSectionHeader(sectionData, sectionId) { 140 | const updateTag = this.props.useDynamicHeights ? 141 | this.updateTagInSectionMap : 142 | null; 143 | 144 | const title = this.props.getSectionTitle ? 145 | this.props.getSectionTitle(sectionId) : 146 | sectionId; 147 | 148 | return ( 149 | 156 | ); 157 | } 158 | 159 | renderFooter() { 160 | const Footer = this.props.footer; 161 | return