// Global Imports import * as React from 'react'; import { Modal, View, FlatList, KeyboardAvoidingView, NativeSyntheticEvent, NativeScrollEvent, Platform, SafeAreaView, TouchableOpacity } 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 { private flatListRef = 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 } } 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(); 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); } }