Skip to content

Commit

Permalink
Merge pull request facebook#37 from BuckleTypes/sectionlist
Browse files Browse the repository at this point in the history
SectionList API
  • Loading branch information
saschatimme authored Jun 27, 2017
2 parents 75bfc23 + 5ca9819 commit 2baf1ea
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 231 deletions.
49 changes: 24 additions & 25 deletions RNTester/src/RNTesterExampleList.re
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,36 @@ let styles =
}
);

include
SectionList.CreateComponent {
type item = ExampleList.item;
};

let component = ReasonReact.statelessComponent "RNTesterExampleList";

let make = {
let renderItem onPress {item} =>
<TouchableHighlight onPress=(fun () => onPress item)>
<View style=styles##row>
<Text style=styles##rowTitleText> (ReasonReact.stringToElement item.title) </Text>
<Text style=styles##rowDetailText> (ReasonReact.stringToElement item.description) </Text>
</View>
</TouchableHighlight>;
let itemSeparatorComponent {highlighted} =>
<View
style=(
if highlighted {
styles##separatorHighlighted
} else {
styles##separator
}
)
/>;
fun ::components ::onPress _children => {
let renderItem onPress =>
SectionList.renderItem (
fun {item} =>
<TouchableHighlight onPress=(fun () => onPress item)>
<View style=styles##row>
<Text style=styles##rowTitleText>
(ReasonReact.stringToElement item.ExampleList.title)
</Text>
<Text style=styles##rowDetailText> (ReasonReact.stringToElement item.description) </Text>
</View>
</TouchableHighlight>
);

let itemSeparatorComponent =
SectionList.separatorComponent (
fun {highlighted} =>
<View style=(highlighted ? styles##separatorHighlighted : styles##separator) />
);

let make ::components ::onPress _children => {
let sections =
SectionList.sections [|SectionList.section data::components key::"components" ()|];
{
...component,
render: fun _state _self =>
<View style=styles##listContainer>
<SectionList
sections=[|SectionList.section data::components key::"components" ()|]
sections
renderItem=(renderItem onPress)
keyExtractor=(fun item _ => item.key)
itemSeparatorComponent
Expand Down
317 changes: 172 additions & 145 deletions src/components/sectionListRe.re
Original file line number Diff line number Diff line change
@@ -1,151 +1,178 @@
external view : ReasonReact.reactClass = "SectionList" [@@bs.module "react-native"];

module CreateComponent (Item: {type item;}) => {
type renderBag = {
item: Item.item,
index: int,
section,
separators: Js.t {. highlight : unit => unit, unhighlight : unit => unit}
type jsSection 'item =
Js.t {
.
data : array 'item,
key : Js.Undefined.t string,
renderItem : Js.Undefined.t (jsRenderBag 'item => ReasonReact.reactElement)
}
and section = {
data: array Item.item,
key: option string,
renderItem: option (renderBag => ReasonReact.reactElement)
and jsRenderBag 'item =
Js.t {
.
item : 'item,
index : int,
section : jsSection 'item,
separators : Js.t {. highlight : unit => unit, unhighlight : unit => unit}
};
type viewToken =
Js.t {
.
item : Item.item,
key : string,
index : Js.undefined int,
isViewable : Js.boolean,
section : section
};
type separatorProps = {
highlighted: bool,
leadingItem: option Item.item,
leadingSection: option section,
section,
trailingItem: option Item.item,
trailingSection: option section
};
let createRenderBag ::item ::index ::section ::separators => {item, index, section, separators};
let createSeparatorProps
::highlighted
::leadingItem
::leadingSection
::section
::trailingItem
::trailingSection => {
highlighted,
leadingItem,
leadingSection,
section,
trailingItem,
trailingSection

type jsSeparatorProps 'item =
Js.t {
.
highlighted : Js.boolean,
leadingItem : Js.Undefined.t 'item,
leadingSection : Js.Undefined.t (jsSection 'item),
section : jsSection 'item,
trailingItem : Js.Undefined.t 'item,
trailingSection : Js.Undefined.t (jsSection 'item)
};
module SectionList = {
let renderItemFromJS renderItem jsItems =>
renderItem (
createRenderBag
item::jsItems##item
index::jsItems##index
section::jsItems##section
separators::jsItems##separators
);
let section ::data ::key=? ::renderItem=? () => {data, key, renderItem};
let make:
sections::array section =>
renderItem::(renderBag => ReasonReact.reactElement) =>
keyExtractor::(Item.item => int => string) =>
itemSeparatorComponent::(separatorProps => ReasonReact.reactElement)? =>
listEmptyComponent::(unit => ReasonReact.reactElement)? =>
listFooterComponent::ReasonReact.reactElement? =>
listHeaderComponent::ReasonReact.reactElement? =>
sectionSeparatorComponent::(separatorProps => ReasonReact.reactElement)? =>
extraData::'extraData? =>
initialNumToRender::int? =>
onEndReached::Js.t {. distanceFromEnd : float}? =>
onEndReachedThreshold::float? =>
onViewableItemsChanged::Js.t {. viewableItems : array viewToken, changed : array viewToken}? =>
onRefresh::(unit => unit)? =>
refreshing::bool? =>
renderSectionHeader::(Js.t {. section : section} => ReasonReact.reactElement)? =>
renderSectionFooter::(Js.t {. section : section} => ReasonReact.reactElement)? =>
stickySectionHeadersEnabled::bool? =>
array ReasonReact.reactElement =>
ReasonReact.component ReasonReact.stateless =
fun ::sections
::renderItem
::keyExtractor
::itemSeparatorComponent=?
::listEmptyComponent=?
::listFooterComponent=?
::listHeaderComponent=?
::sectionSeparatorComponent=?
::extraData=?
::initialNumToRender=?
::onEndReached=?
::onEndReachedThreshold=?
::onViewableItemsChanged=?
::onRefresh=?
::refreshing=?
::renderSectionHeader=?
::renderSectionFooter=?
::stickySectionHeadersEnabled=? =>
ReasonReact.wrapJsForReason
reactClass::view
props::
Js.Undefined.(
{
"sections":
Array.map
(
fun {data, key, renderItem} => {
"data": data,
"key": from_opt key,
"renderItem": from_opt (UtilsRN.option_map renderItemFromJS renderItem)
}
)
sections,
"renderItem": renderItemFromJS renderItem,
"keyExtractor": keyExtractor,
"ItemSeparatorComponent":
from_opt (
UtilsRN.option_map
(
fun itemSeparatorComponent => {
let comp jsItems =>
itemSeparatorComponent (
createSeparatorProps
highlighted::(Js.to_bool jsItems##highlighted)
leadingItem::jsItems##leadingItem
leadingSection::jsItems##leadingSection
section::jsItems##section
trailingItem::jsItems##trailingItem
trailingSection::jsItems##trailingSection
);
comp
}
)
itemSeparatorComponent
),
"ListEmptyComponent": from_opt listEmptyComponent,
"ListFooterComponent": from_opt listFooterComponent,
"ListHeaderComponent": from_opt listHeaderComponent,
"SectionSeparatorComponent": from_opt sectionSeparatorComponent,
"extraData": from_opt extraData,
"initialNumToRender": from_opt initialNumToRender,
"onEndReached": from_opt onEndReached,
"onEndReachedThreshold": from_opt onEndReachedThreshold,
"onRefresh": from_opt onRefresh,
"onViewableItemsChanged": from_opt onViewableItemsChanged,
"refreshing": from_opt (UtilsRN.optBoolToOptJsBoolean refreshing),
"renderSectionHeader": from_opt renderSectionHeader,
"renderSectionFooter": from_opt renderSectionFooter,
"stickySectionHeadersEnabled":
from_opt (UtilsRN.optBoolToOptJsBoolean stickySectionHeadersEnabled)
}
);

type renderBag 'item = {
item: 'item,
index: int,
section: section 'item,
separators: Js.t {. highlight : unit => unit, unhighlight : unit => unit}
}
and section 'item = {
data: array 'item,
key: option string,
renderItem: option (renderBag 'item => ReasonReact.reactElement)
};

type separatorProps 'item = {
highlighted: bool,
leadingItem: option 'item,
leadingSection: option (section 'item),
section: section 'item,
trailingItem: option 'item,
trailingSection: option (section 'item)
};

type renderItem 'item = jsRenderBag 'item => ReasonReact.reactElement;

let jsSectionToSection jsSection => {
data: jsSection##data,
key: Js.Undefined.to_opt jsSection##key,
/** We set renderItem to None to avoid an infinite conversion loop */
renderItem: None
};

type sections 'item = array (jsSection 'item);

let renderItem (reRenderItem: renderBag 'item => ReasonReact.reactElement) :renderItem 'item =>
fun (jsRenderBag: jsRenderBag 'item) =>
reRenderItem {
item: jsRenderBag##item,
index: jsRenderBag##index,
section: jsSectionToSection jsRenderBag##section,
separators: jsRenderBag##separators
};

let section ::data ::key=? ::renderItem=? () => {data, key, renderItem};

let sections reSections :sections 'item =>
Array.map
(
fun reSection => {
"data": reSection.data,
"key": Js.Undefined.from_opt reSection.key,
"renderItem": Js.Undefined.from_opt (UtilsRN.option_map renderItem reSection.renderItem)
}
)
reSections;

type separatorComponent 'item = jsSeparatorProps 'item => ReasonReact.reactElement;

let separatorComponent
(reSeparatorComponent: separatorProps 'item => ReasonReact.reactElement)
:separatorComponent 'item =>
fun (jsSeparatorProps: jsSeparatorProps 'item) =>
reSeparatorComponent {
highlighted: Js.to_bool jsSeparatorProps##highlighted,
leadingItem: Js.Undefined.to_opt jsSeparatorProps##leadingItem,
leadingSection:
Js.Undefined.to_opt jsSeparatorProps##leadingSection |>
UtilsRN.option_map jsSectionToSection,
section: jsSectionToSection jsSeparatorProps##section,
trailingItem: Js.Undefined.to_opt jsSeparatorProps##trailingItem,
trailingSection:
Js.Undefined.to_opt jsSeparatorProps##trailingSection |>
UtilsRN.option_map jsSectionToSection
};

type viewToken 'item =
Js.t {
.
item : 'item,
key : string,
index : Js.undefined int,
isViewable : Js.boolean,
section : section 'item
};
};

let make:
sections::sections 'item =>
renderItem::renderItem 'item =>
keyExtractor::('item => int => string) =>
itemSeparatorComponent::separatorComponent 'item? =>
listEmptyComponent::(unit => ReasonReact.reactElement)? =>
listFooterComponent::ReasonReact.reactElement? =>
listHeaderComponent::ReasonReact.reactElement? =>
sectionSeparatorComponent::separatorComponent 'item? =>
extraData::'extraData? =>
initialNumToRender::int? =>
onEndReached::Js.t {. distanceFromEnd : float}? =>
onEndReachedThreshold::float? =>
onViewableItemsChanged::
Js.t {. viewableItems : array (viewToken 'item), changed : array (viewToken 'item)}? =>
onRefresh::(unit => unit)? =>
refreshing::bool? =>
renderSectionHeader::(Js.t {. section : section 'item} => ReasonReact.reactElement)? =>
renderSectionFooter::(Js.t {. section : section 'item} => ReasonReact.reactElement)? =>
stickySectionHeadersEnabled::bool? =>
array ReasonReact.reactElement =>
ReasonReact.component ReasonReact.stateless =
fun ::sections
::renderItem
::keyExtractor
::itemSeparatorComponent=?
::listEmptyComponent=?
::listFooterComponent=?
::listHeaderComponent=?
::sectionSeparatorComponent=?
::extraData=?
::initialNumToRender=?
::onEndReached=?
::onEndReachedThreshold=?
::onViewableItemsChanged=?
::onRefresh=?
::refreshing=?
::renderSectionHeader=?
::renderSectionFooter=?
::stickySectionHeadersEnabled=? =>
ReasonReact.wrapJsForReason
reactClass::view
props::
Js.Undefined.(
{
"sections": sections,
"renderItem": renderItem,
"keyExtractor": keyExtractor,
"ItemSeparatorComponent": from_opt itemSeparatorComponent,
"ListEmptyComponent": from_opt listEmptyComponent,
"ListFooterComponent": from_opt listFooterComponent,
"ListHeaderComponent": from_opt listHeaderComponent,
"SectionSeparatorComponent": from_opt sectionSeparatorComponent,
"extraData": from_opt extraData,
"initialNumToRender": from_opt initialNumToRender,
"onEndReached": from_opt onEndReached,
"onEndReachedThreshold": from_opt onEndReachedThreshold,
"onRefresh": from_opt onRefresh,
"onViewableItemsChanged": from_opt onViewableItemsChanged,
"refreshing": from_opt (UtilsRN.optBoolToOptJsBoolean refreshing),
"renderSectionHeader": from_opt renderSectionHeader,
"renderSectionFooter": from_opt renderSectionFooter,
"stickySectionHeadersEnabled":
from_opt (UtilsRN.optBoolToOptJsBoolean stickySectionHeadersEnabled)
}
);
Loading

0 comments on commit 2baf1ea

Please sign in to comment.