Skip to content

Commit

Permalink
Support FlatList (#318)
Browse files Browse the repository at this point in the history
* use FlatList instead of View, add prettier

* fix keyExtractor call in renderItem()

* add TS definitions & changes, add keyExtractor to README.md

* add numColumns option

* extract container to _renderContainer & add switch for FlatList

* add keyExtractor to View rendering, too & add renderAsFlatList to README

* add renderAsFlatList to Accordion.d.ts

* add definitions from DefinitelyTyped

* add renderAsFlatList switch to example app

* add extraData to rerender when activeStates change

* add small change (value as string) to App, else everything is working

* Delete conflicting prettierrc

* Remove unused references to numColumns

* Revert breaking change of key type

Co-authored-by: Joel Arvidsson <joel.arvidsson@klarna.com>
  • Loading branch information
timhagn and oblador authored Apr 29, 2021
1 parent 72b6b09 commit 3f92942
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 36 deletions.
19 changes: 17 additions & 2 deletions Accordion.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export interface AccordionProps<T> {
*/
onChange(indexes: number[]): void;

/**
* Used to extract a unique key for a given item at the specified index. Key is used for caching
* and as the react key to track item re-ordering. The default extractor checks `item.key`, then
* falls back to using the index, like React does.
*/
keyExtractor?: (item: T, index: number) => number | string;

/**
* Controls whether user can interact with accordion
*/
Expand All @@ -73,9 +80,10 @@ export interface AccordionProps<T> {
expandMultiple?: boolean;

/**
* Control which indices in the sections array are currently open. If empty, closes all sections.
* Control which indices from keyEctractor in the sections array are currently
* open. If empty, closes all sections.
*/
activeSections: number[];
activeSections: number[] | string[];

/**
* The color of the underlay that will show through when tapping on headers.
Expand Down Expand Up @@ -126,6 +134,13 @@ export interface AccordionProps<T> {
* Optional styling for the Accordion container
*/
containerStyle?: StyleProp<ViewStyle>;

/**
* Render the Accordion as a FlatList. Defaults to false for legacy behavior.
*
* @default false
*/
renderAsFlatList?: boolean;
}

export default class Accordion<T> extends React.Component<AccordionProps<T>> {}
99 changes: 65 additions & 34 deletions Accordion.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { View, TouchableHighlight } from 'react-native';
import { View, TouchableHighlight, FlatList } from 'react-native';
import Collapsible from './Collapsible';

const COLLAPSIBLE_PROPS = [
Expand All @@ -20,9 +20,11 @@ export default class Accordion extends Component {
expandFromBottom: false,
expandMultiple: false,
touchableComponent: TouchableHighlight,
keyExtractor: (item, index) => index,
renderSectionTitle: () => null,
onAnimationEnd: () => null,
sectionContainerStyle: {},
renderAsFlatList: false,
};

_toggleSection(section) {
Expand All @@ -45,6 +47,44 @@ export default class Accordion extends Component {
}
}

_renderContainer = (section, key, renderCollapsible) => {
const {
activeSections,
sectionContainerStyle,
expandFromBottom,
sections,
underlayColor,
touchableProps,
touchableComponent: Touchable,
renderHeader,
renderFooter,
renderSectionTitle,
} = this.props;
return (
<View key={key} style={sectionContainerStyle}>
{renderSectionTitle(section, key, activeSections.includes(key))}

{expandFromBottom && renderCollapsible(section, key)}

<Touchable
onPress={() => this._toggleSection(key)}
underlayColor={underlayColor}
{...touchableProps}
accessibilityState={{
expanded: activeSections.includes(key),
}}
>
{renderHeader(section, key, activeSections.includes(key), sections)}
</Touchable>

{!expandFromBottom && renderCollapsible(section, key)}

{renderFooter &&
renderFooter(section, key, activeSections.includes(key), sections)}
</View>
);
};

render() {
const {
activeSections,
Expand All @@ -63,6 +103,8 @@ export default class Accordion extends Component {
renderFooter,
renderSectionTitle,
disabled,
renderAsFlatList,
keyExtractor,
...restProps
} = this.props;

Expand All @@ -87,41 +129,30 @@ export default class Accordion extends Component {
</Collapsible>
);

if (renderAsFlatList) {
return (
<FlatList
style={containerStyle}
data={sections}
extraData={activeSections}
nestedScrollEnabled={true}
keyExtractor={keyExtractor}
renderItem={({ item, index }) => {
const section = item;
const key = keyExtractor(item, index);
return this._renderContainer(section, key, renderCollapsible);
}}
{...viewProps}
/>
);
}

return (
<View style={containerStyle} {...viewProps}>
{sections.map((section, key) => (
<View key={key} style={sectionContainerStyle}>
{renderSectionTitle(section, key, activeSections.includes(key))}

{expandFromBottom && renderCollapsible(section, key)}

<Touchable
onPress={() => this._toggleSection(key)}
underlayColor={underlayColor}
{...touchableProps}
accessibilityState={{
expanded: activeSections.includes(key),
}}
>
{renderHeader(
section,
key,
activeSections.includes(key),
sections
)}
</Touchable>

{!expandFromBottom && renderCollapsible(section, key)}

{renderFooter &&
renderFooter(
section,
key,
activeSections.includes(key),
sections
)}
</View>
))}
{sections.map((section, index) => {
const key = keyExtractor(section, index);
return this._renderContainer(section, key, renderCollapsible);
})}
</View>
);
}
Expand Down
1 change: 1 addition & 0 deletions Example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export default class App extends Component {
renderContent={this.renderContent}
duration={400}
onChange={this.setSections}
renderAsFlatList={false}
/>
</ScrollView>
</View>
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import Accordion from 'react-native-collapsible/Accordion';
| **`renderFooter(content, index, isActive, sections)`** | A function that should return a renderable representing the footer |
| **`renderSectionTitle(content, index, isActive)`** | A function that should return a renderable representing the title of the section outside the touchable element |
| **`onChange(indexes)`** | A function that is called when the currently active section(s) are updated. |
| **`keyExtractor(item, index)`** | Used to extract a unique key for a given item at the specified index. |
| **`activeSections`** | Control which indices in the `sections` array are currently open. If empty, closes all sections. |
| **`underlayColor`** | The color of the underlay that will show through when tapping on headers. Defaults to black. |
| **`touchableComponent`** | The touchable component used in the Accordion. Defaults to `TouchableHighlight` |
Expand All @@ -77,6 +78,7 @@ import Accordion from 'react-native-collapsible/Accordion';
| **`expandMultiple`** | Allow more than one section to be expanded. Defaults to false. |
| **`sectionContainerStyle`** | Optional styling for the section container. |
| **`containerStyle`** | Optional styling for the Accordion container. |
| **`renderAsFlatList`** | Optional rendering as FlatList (defaults to false). |

## Demo

Expand Down

0 comments on commit 3f92942

Please sign in to comment.