import React, { Component, createRef } from 'react';
import {
Dimensions,
StyleSheet,
View,
TouchableWithoutFeedback,
} from 'react-native';
import Option from './option';
const window = Dimensions.get('window');
interface SelectProps {
width?: number;
height?: number;
optionListRef: () => any;
onSelect?: (value: any) => void;
onOpen?: (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void;
defaultValue?: string;
style?: any;
styleOption?: any;
styleText?: any;
placeholder?: string;
value?: string;
optionListProps?: any;
optionListExtraWidth?: number;
optionListLeftOffset?: number;
optionListTopOffset?: number;
children?: React.ReactNode;
}
interface SelectState {
defaultValue: string;
value: string;
placeholder: string | undefined;
}
const styles = StyleSheet.create({
container: {
borderColor: '#BDBDC1',
borderWidth: 2 / window.scale,
},
});
export default class Select extends Component<SelectProps, SelectState> {
static defaultProps = {
width: 200,
height: 40,
onSelect: () => {},
optionListProps: {},
};
private pageX: number;
private pageY: number;
private selectRef: React.RefObject<View>;
constructor(props: SelectProps) {
super(props);
this.selectRef = createRef<View>();
this.pageX = 0;
this.pageY = 0;
let defaultValue = props.defaultValue || this.getDefaultValue();
this.state = {
defaultValue: defaultValue,
value: props.placeholder ? props.value || '' : defaultValue,
placeholder: props.placeholder,
};
}
componentDidUpdate(prevProps: SelectProps) {
if (prevProps.value !== this.props.value) {
this.setState(
{
value:
this.props.value !== undefined
? this.props.value
: this.props.placeholder
? ''
: this.state.defaultValue,
},
() => this.checkChildrenProps(prevProps, this.props)
);
} else {
this.checkChildrenProps(prevProps, this.props);
}
}
getDefaultValue = () => {
const { children } = this.props;
if (Array.isArray(children)) {
if ((children as React.ReactNode[])[0]) {
return (children as React.ReactNode[])[0]!.props!.children;
} else {
return '';
}
} else {
return (children as any).props.children;
}
};
checkChildrenProps = (props: SelectProps, nextProps: SelectProps) => {
if (props.children !== nextProps.children) {
if (nextProps.children && (nextProps.children as React.ReactNode[]).length) {
const childrenArray = React.Children.toArray(nextProps.children);
const findValueInData =
childrenArray &&
childrenArray.find(
(child: any) => child.props && child.props.children === this.state.value
);
if (!findValueInData) {
this.setState({
value: nextProps.placeholder ? '' : this.state.defaultValue,
});
}
} else {
this.setState({
value: nextProps.placeholder ? this.state.value : this.state.defaultValue,
});
}
}
};
reset = () => {
const { placeholder } = this.props;
this.setState({
value: placeholder ? '' : this.state.defaultValue,
});
};
_currentPosition = (pageX: number, pageY: number) => {
const { height } = this.props;
this.pageX = pageX;
this.pageY = pageY + (height || 40);
};
show = () => {
this._onPress();
};
* Measure the Select component's absolute position using ref.measure()
* (Fabric-compatible replacement for UIManager.measure).
* Then open the dropdown.
*/
_measureAndOpen = () => {
const measureTarget = this.selectRef.current;
if (measureTarget && typeof measureTarget.measure === 'function') {
let measured = false;
const fallbackTimer = setTimeout(() => {
if (!measured) {
measured = true;
this._openDropdown();
if (this.props.onOpen) {
this.props.onOpen(0, 0, 0, 0, this.pageX, this.pageY);
}
}
}, 200);
measureTarget.measure(
(x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
if (measured) return;
measured = true;
clearTimeout(fallbackTimer);
this._currentPosition(pageX, pageY);
this._openDropdown();
if (this.props.onOpen) {
this.props.onOpen(x, y, width, height, pageX, pageY);
}
}
);
} else {
this._openDropdown();
if (this.props.onOpen) {
this.props.onOpen(0, 0, 0, 0, this.pageX, this.pageY);
}
}
};
_openDropdown = () => {
const {
optionListRef,
children,
onSelect,
width,
height,
optionListExtraWidth,
optionListLeftOffset,
optionListTopOffset,
optionListProps,
} = this.props;
const { topOffset = 0, leftOffset = 0, extraWidth = 0 } = optionListProps || {};
if (!(children && (children as React.ReactNode[]).length)) {
return false;
}
if (typeof optionListRef !== 'function') {
return false;
}
const optionList = optionListRef();
if (!optionList || typeof optionList._show !== 'function') {
return false;
}
const effectiveWidth = (width || 200) + extraWidth;
const effectiveHeight = height || 40;
optionList._show(
children as React.ReactNode[],
this.pageX + leftOffset,
this.pageY + topOffset,
effectiveWidth,
effectiveHeight,
(item: any, value: any = item) => {
if (item) {
this.setState(
{
value: item,
},
() => onSelect && onSelect(value)
);
} else {
if (onSelect) {
onSelect(null);
}
}
},
optionListProps
);
};
_onPress = () => {
this._measureAndOpen();
};
render() {
const {
width,
height,
style,
styleOption,
styleText,
placeholder,
} = this.props;
const dimensions = { width, height };
return (
<TouchableWithoutFeedback onPress={this._onPress}>
<View
ref={this.selectRef}
style={[styles.container, style, dimensions, { height: '100%' }]}>
<Option style={styleOption} styleText={styleText}>
{this.state.value || placeholder}
</Option>
</View>
</TouchableWithoutFeedback>
);
}
}