// Global Imports
import * as React from "react";
import {
Modal,
View,
FlatList,
KeyboardAvoidingView,
NativeSyntheticEvent,
NativeScrollEvent,
Platform,
SafeAreaView,
TouchableOpacity,
BackHandler,
} from "react-native";
// Local Imports
import {
AlphabetComponent,
ListItemComponent,
SearchComponent,
ScrollToTopComponent,
SelectBoxComponent,
} from "@Components";
import { IModalProps, IModalListInDto, IModalState } from "@Interfaces";
import { ModalStyles, CommonStyle } from "@Styles";
import { generateAlphabet, getFilteredData, getIndex } from "@Helpers";
export class ModalComponent extends React.PureComponent<
IModalProps,
IModalState
> {
private flatListRef = null;
private backHandler = null;
private numToRender: number = 20;
public state: IModalState = {
modalVisible: false,
searchText: "",
stickyBottomButton: false,
selectedAlpha: null,
selectedObject: {} as IModalListInDto,
};
public static defaultProps = {
showToTopButton: true,
modalAnimationType: "slide",
showAlphabeticalIndex: false,
searchInputTextColor: "#252525",
autoGenerateAlphabeticalIndex: false,
sortingLanguage: "tr",
removeClippedSubviews: false,
selectPlaceholderText: "Choose one...",
searchPlaceholderText: "Search...",
autoSort: false,
items: [],
disabled: false,
requireSelection: false,
};
private viewabilityConfig: {
minimumViewTime: number;
waitForInteraction: boolean;
viewAreaCoveragePercentThreshold: number;
};
constructor(props: IModalProps) {
super(props);
this._onViewableItemsChanged = this._onViewableItemsChanged.bind(this);
this.viewabilityConfig = {
minimumViewTime: 500,
waitForInteraction: true,
viewAreaCoveragePercentThreshold: 95,
};
this.backHandler = BackHandler.addEventListener(
"hardwareBackPress",
() => {
this._onClose();
return true;
}
);
}
private _clearComponent(): void {
this.setState({
stickyBottomButton: false,
searchText: "",
selectedAlpha: null,
});
}
public clearComponent(): void {
this._clearComponent();
}
public componentDidMount(): void {
const {
autoGenerateAlphabeticalIndex,
alphabeticalIndexChars,
items,
sortingLanguage,
} = this.props;
if (autoGenerateAlphabeticalIndex) {
this.setState({
alphabeticalIndexChars: generateAlphabet(
items,
sortingLanguage
),
});
} else if (alphabeticalIndexChars) {
this.setState({
alphabeticalIndexChars,
});
}
}
private _openModal(): void {
const {
items,
autoGenerateAlphabeticalIndex,
disabled,
sortingLanguage,
} = this.props;
if (autoGenerateAlphabeticalIndex) {
this.setState({
alphabeticalIndexChars: generateAlphabet(
items,
sortingLanguage
),
});
}
if (items.length > 0 && !disabled) {
this.setState({
modalVisible: true,
});
}
}
public openModal(): void {
this._openModal();
}
public render(): JSX.Element {
const {
autoSort,
modalAnimationType,
onClosed,
showAlphabeticalIndex,
searchInputTextColor,
keyExtractor,
showToTopButton,
onEndReached,
removeClippedSubviews,
FlatListProps,
selectPlaceholderText,
searchPlaceholderText,
SearchInputProps,
selected,
disabled,
items,
requireSelection,
renderSelectView,
ModalProps,
backButtonDisabled,
renderSearch,
} = this.props;
const {
modalVisible,
alphabeticalIndexChars,
stickyBottomButton,
selectedAlpha,
selectedObject,
searchText,
} = this.state;
return (
onClosed}
{...ModalProps}
>
{renderSearch ? (
renderSearch(
this.onClose.bind(this),
this.onBackRequest.bind(this)
)
) : (
this.setText(text)}
backButtonDisabled={backButtonDisabled}
{...SearchInputProps}
/>
)}
(this.flatListRef = ref)}
keyExtractor={
keyExtractor
? keyExtractor
: (item, index) => index.toString()
}
data={getFilteredData(
items,
autoSort,
searchText
)}
renderItem={({ item, index }) =>
this.renderItem(item, index)
}
onScroll={
showToTopButton &&
this.onScrolling.bind(this)
}
initialNumToRender={this.numToRender}
keyboardShouldPersistTaps={"always"}
keyboardDismissMode={"interactive"}
onEndReached={onEndReached}
maxToRenderPerBatch={20}
legacyImplementation={false}
updateCellsBatchingPeriod={50}
removeClippedSubviews={
removeClippedSubviews
}
viewabilityConfig={this.viewabilityConfig}
getItemLayout={(_, index) => ({
length: CommonStyle.BTN_HEIGHT,
offset: CommonStyle.BTN_HEIGHT * index,
index,
})}
onViewableItemsChanged={
this._onViewableItemsChanged
}
{...FlatListProps}
/>
this.setAlphabet(alphabet)
}
alphabets={alphabeticalIndexChars}
selectedAlpha={selectedAlpha}
/>
);
}
private _onViewableItemsChanged({ viewableItems }): void {
if (viewableItems && viewableItems[0]) {
const firstLetter = viewableItems[0].item.Name.charAt(0);
this.setState({
selectedAlpha: firstLetter,
});
}
}
private _onClose(): void {
const { onClosed, onSelected, requireSelection, selected } = this.props;
const { modalVisible, selectedObject } = this.state;
if (
requireSelection &&
selectedObject &&
![selectedObject.Id] &&
selected &&
![selected.Id]
)
return;
if (!requireSelection) {
onSelected({} as IModalListInDto);
}
this.setState({
selectedObject: {} as IModalListInDto,
modalVisible: !modalVisible,
});
this.clearComponent();
this.backHandler && this.backHandler.remove();
if (onClosed) {
onClosed();
}
}
public onClose(): void {
this._onClose();
}
private _onBackRequest(): void {
const { onBackButtonPressed } = this.props;
const { modalVisible } = this.state;
this.setState({
modalVisible: !modalVisible,
});
this.clearComponent();
if (onBackButtonPressed) {
onBackButtonPressed();
}
}
public onBackRequest(): void {
this._onBackRequest();
}
private _scrollToUp(): void {
if (this.flatListRef) {
this.setState(
{
selectedAlpha: null,
},
() => {
this.flatListRef.scrollToOffset({
animated: true,
offset: 0,
});
}
);
}
}
public scrollToUp(): void {
this._scrollToUp();
}
private _onScrolling(e: NativeSyntheticEvent): void {
const { contentOffset } = e.nativeEvent;
if (contentOffset.y > 100) {
this.setState({
stickyBottomButton: true,
});
} else {
this.setState({
stickyBottomButton: false,
});
}
}
public onScrolling(e: NativeSyntheticEvent): void {
this._onScrolling(e);
}
private _renderItem(item: IModalListInDto, index: number): JSX.Element {
const { selected, renderListItem } = this.props;
return (
(renderListItem && (
this.onSelectMethod(item)}
>
{renderListItem(selected, item)}
)) || (
)
);
}
public renderItem(item: IModalListInDto, index: number): JSX.Element {
return this._renderItem(item, index);
}
private _setText(text: string): void {
this.setState({
searchText: text,
});
}
public setText(text: string): void {
this._setText(text);
}
private _onSelectMethod(key: IModalListInDto): IModalListInDto | void {
const { onSelected } = this.props;
this.setState({
modalVisible: false,
selectedObject: key as IModalListInDto,
});
this.clearComponent();
if (key && ![key.Id]) {
return onSelected({} as IModalListInDto);
}
return onSelected(key);
}
public onSelectMethod(key: IModalListInDto): IModalListInDto | void {
return this._onSelectMethod(key);
}
private _setAlphabet(alphabet: string): void {
this.setState(
{
selectedAlpha: alphabet,
},
() => {
const list = getFilteredData(
this.props.items,
this.props.autoSort,
this.state.searchText
);
const findIndex = getIndex(
alphabet,
this.props.items,
this.props.autoSort,
this.state.searchText
);
if (
findIndex >= 0 &&
findIndex <= list.length - this.numToRender / 2
) {
setTimeout(() => {
this.flatListRef.scrollToIndex({
animated: true,
index: findIndex,
viewPosition: 0,
});
}, 100);
} else {
this.flatListRef.scrollToEnd();
}
}
);
}
public setAlphabet(alphabet: string): void {
this._setAlphabet(alphabet);
}
}