Updated to latest code
This commit is contained in:
155
src/CustomListView.tsx
Normal file
155
src/CustomListView.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import { PureComponent, ReactNode, createElement, createRef } from "react";
|
||||
import { View, FlatList, ScrollView, TouchableOpacity, Text, ViewStyle } from "react-native";
|
||||
import { ObjectItem } from "mendix";
|
||||
import { Big } from "big.js"
|
||||
|
||||
import { CustomListViewProps } from "../typings/CustomListViewProps"
|
||||
import { Style, mergeNativeStyles } from '@mendix/pluggable-widgets-tools';
|
||||
|
||||
let clickTimer: number;
|
||||
|
||||
export interface CustomStyle extends Style {
|
||||
footer: ViewStyle
|
||||
}
|
||||
|
||||
const defaultStyle: CustomStyle = {
|
||||
footer: {
|
||||
marginBottom: 300,
|
||||
}
|
||||
};
|
||||
|
||||
interface State {
|
||||
clickDisabled?: boolean;
|
||||
scrollIndex: number;
|
||||
}
|
||||
|
||||
export class CustomListView extends PureComponent<CustomListViewProps<CustomStyle>, State> {
|
||||
private readonly styles = mergeNativeStyles(defaultStyle, this.props.style);
|
||||
constructor(props: CustomListViewProps<CustomStyle>) {
|
||||
super(props)
|
||||
this.state = {
|
||||
clickDisabled: false,
|
||||
scrollIndex: Number(this.props.scrollItem?.displayValue),
|
||||
}
|
||||
}
|
||||
renderFlatListHandler = this.renderFlatList.bind(this);
|
||||
renderScrollViewHandler = this.renderScrollView.bind(this);
|
||||
onClickHandler = this.onClick.bind(this);
|
||||
renderEmptyHandler = this.renderEmpty.bind(this);
|
||||
renderFooterHandler = this.renderFooter.bind(this);
|
||||
flatListRef = createRef<FlatList<any>>();
|
||||
|
||||
render(): ReactNode {
|
||||
const { scrollView } = this.props;
|
||||
return (
|
||||
<View>{scrollView ? <this.renderScrollViewHandler /> : <this.renderFlatListHandler />}</View>
|
||||
)
|
||||
}
|
||||
|
||||
renderFlatList() {
|
||||
const { ds, windowSize, initialNumToRender, removeClippedSubviews, maxNumberToRenderPerBatch, cellBatchingSize, useItemLayout, itemSize } = this.props;
|
||||
const size = Number(itemSize)
|
||||
return (
|
||||
<View>
|
||||
{useItemLayout ?
|
||||
<FlatList
|
||||
getItemLayout={(data, index) => ({
|
||||
length: size,
|
||||
offset: size * index,
|
||||
index,
|
||||
data
|
||||
})}
|
||||
ref={this.flatListRef}
|
||||
data={ds?.items}
|
||||
renderItem={this.renderItem}
|
||||
windowSize={windowSize}
|
||||
initialNumToRender={initialNumToRender}
|
||||
removeClippedSubviews={removeClippedSubviews}
|
||||
ListEmptyComponent={this.renderEmptyHandler()}
|
||||
maxToRenderPerBatch={maxNumberToRenderPerBatch}
|
||||
ListFooterComponent={this.renderFooterHandler()}
|
||||
/>
|
||||
:
|
||||
<FlatList
|
||||
data={ds?.items}
|
||||
renderItem={this.renderItem}
|
||||
windowSize={windowSize}
|
||||
initialNumToRender={initialNumToRender}
|
||||
removeClippedSubviews={removeClippedSubviews}
|
||||
ListEmptyComponent={this.renderEmptyHandler()}
|
||||
maxToRenderPerBatch={maxNumberToRenderPerBatch}
|
||||
ListFooterComponent={this.renderFooterHandler()}
|
||||
updateCellsBatchingPeriod={cellBatchingSize}
|
||||
/>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderScrollView() {
|
||||
const { ds, container } = this.props;
|
||||
return (
|
||||
<View>
|
||||
<ScrollView>
|
||||
{ds.items?.map((item) => <View key={item.id}>{container(item)}</View>)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderItem = ({ item, index }: { item: ObjectItem, index: number }) => {
|
||||
const { container, useItemLayout, itemSize } = this.props;
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity onPress={() => this.onClickHandler(item, index)} disabled={this.state.clickDisabled}>
|
||||
<View key={item.id} style={useItemLayout ? { height: Number(itemSize) } : null}>{container(item)}</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
return <View style={this.styles.footer}></View>
|
||||
}
|
||||
|
||||
scrollToOffset = (index: number) => {
|
||||
const { itemSize } = this.props
|
||||
this.flatListRef.current?.scrollToOffset({ animated: true, offset: index * Number(itemSize) })
|
||||
this.props.scrollToItem?.setValue(false);
|
||||
}
|
||||
|
||||
renderEmpty() {
|
||||
const { emptyMessage } = this.props;
|
||||
return <View><Text>{emptyMessage}</Text></View>
|
||||
}
|
||||
|
||||
onClick(item: ObjectItem, index: number) {
|
||||
const { onClick, scrollItem } = this.props;
|
||||
const actionValue = onClick!(item);
|
||||
if (!this.state.clickDisabled) {
|
||||
this.setState({ clickDisabled: true });
|
||||
actionValue.execute();
|
||||
scrollItem?.setValue(new Big(index));
|
||||
clickTimer = setTimeout(() => {
|
||||
this.setState({ clickDisabled: false });
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { scrollToItem, useItemLayout } = this.props
|
||||
if (useItemLayout) {
|
||||
setTimeout(() => {
|
||||
if (useItemLayout) {
|
||||
scrollToItem?.value ? this.scrollToOffset(this.state.scrollIndex) : null;
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { scrollToItem } = this.props;
|
||||
clearTimeout(clickTimer)
|
||||
scrollToItem?.setValue(false)
|
||||
}
|
||||
}
|
||||
78
src/CustomListView.xml
Normal file
78
src/CustomListView.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<widget id="incentro.customlistview.CustomListView" pluginWidget="true" needsEntityContext="true" offlineCapable="true"
|
||||
supportedPlatform="Native"
|
||||
xmlns="http://www.mendix.com/widget/1.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd">
|
||||
<name>Custom ListView</name>
|
||||
<description>Custom Listview</description>
|
||||
<icon/>
|
||||
<properties>
|
||||
<propertyGroup caption="General">
|
||||
<property key="ds" type="datasource" required="true" isList="true">
|
||||
<caption>Datasource</caption>
|
||||
<description>List of items</description>
|
||||
</property>
|
||||
<property key="container" type="widgets" dataSource="ds" required="true">
|
||||
<caption>Container</caption>
|
||||
<description></description>
|
||||
</property>
|
||||
<property key="scrollView" type="boolean" defaultValue="false">
|
||||
<caption>Scroll View</caption>
|
||||
<description>Use a scrollview instead of a flatlist. Flatlist functions will not work.</description>
|
||||
</property>
|
||||
|
||||
</propertyGroup>
|
||||
<propertyGroup caption="Flatlist">
|
||||
<property key="onClick" type="action" required="false" dataSource="ds">
|
||||
<caption>On click</caption>
|
||||
<description>Action to perform when listview item is pressed.</description>
|
||||
</property>
|
||||
<property key="emptyMessage" type="string" defaultValue="Empty">
|
||||
<caption>Empty message</caption>
|
||||
<description>Message to show when list is empty.</description>
|
||||
</property>
|
||||
<property key="scrollToItem" type="attribute" required="false">
|
||||
<caption>Scroll to item</caption>
|
||||
<description>Attribute boolean to activate automatic scroll. Scroll item must also be filled!</description>
|
||||
<attributeTypes>
|
||||
<attributeType name="Boolean"/>
|
||||
</attributeTypes>
|
||||
</property>
|
||||
<property key="scrollItem" type="attribute" required="false">
|
||||
<caption>Scroll item</caption>
|
||||
<description>Contains the index for where to scroll when scroll to item is set to true.</description>
|
||||
<attributeTypes>
|
||||
<attributeType name="Integer"/>
|
||||
</attributeTypes>
|
||||
</property>
|
||||
<property key="windowSize" type="integer" defaultValue="21">
|
||||
<caption>Window size</caption>
|
||||
<description>The number passed here is a measurement unit where 1 is equivalent to your viewport height.</description>
|
||||
</property>
|
||||
<property key="initialNumToRender" type="integer" defaultValue="10">
|
||||
<caption>Initial nr. of items</caption>
|
||||
<description>Set initial number of items to render when component mounts.</description>
|
||||
</property>
|
||||
<property key="maxNumberToRenderPerBatch" type="integer" defaultValue="5">
|
||||
<caption>Max. render per batch</caption>
|
||||
<description>Set number of items to render while scrolling.</description>
|
||||
</property>
|
||||
<property key="cellBatchingSize" type="integer" defaultValue="100">
|
||||
<caption>Batching delay</caption>
|
||||
<description>Set delay in milliseconds between batch renders. </description>
|
||||
</property>
|
||||
<property key="removeClippedSubviews" type="boolean" defaultValue="false">
|
||||
<caption>Remove clipped subviews</caption>
|
||||
<description>Unmounts components that are outside the window.</description>
|
||||
</property>
|
||||
<property key="useItemLayout" type="boolean" defaultValue="false">
|
||||
<caption>Use getItemLayout</caption>
|
||||
<description>Use flatlist getItemLayout for scrolling performance. Needs a fixed item height, which can be altered below.</description>
|
||||
</property>
|
||||
<property key="itemSize" type="decimal" defaultValue="50">
|
||||
<caption>Item height</caption>
|
||||
<description>Set item height.</description>
|
||||
</property>
|
||||
</propertyGroup>
|
||||
</properties>
|
||||
</widget>
|
||||
11
src/package.xml
Normal file
11
src/package.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<package xmlns="http://www.mendix.com/package/1.0/">
|
||||
<clientModule name="CustomListView" version="1.0.0" xmlns="http://www.mendix.com/clientModule/1.0/">
|
||||
<widgetFiles>
|
||||
<widgetFile path="CustomListView.xml"/>
|
||||
</widgetFiles>
|
||||
<files>
|
||||
<file path="incentro/customlistview"/>
|
||||
</files>
|
||||
</clientModule>
|
||||
</package>
|
||||
Reference in New Issue
Block a user