react-native-autocomplete-dropdown
Version:
Dropdown Item picker with search and autocomplete (typeahead) functionality for react native
134 lines (133 loc) • 4.26 kB
JavaScript
;
import React, { useCallback, useRef, useState, useEffect } from 'react';
import { StyleSheet, View } from 'react-native';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export const AutocompleteDropdownContext = /*#__PURE__*/React.createContext({
content: undefined,
setContent: () => null,
direction: undefined,
setDirection: () => null,
activeInputContainerRef: undefined,
activeControllerRef: undefined,
controllerRefs: undefined
});
export const AutocompleteDropdownContextProvider = ({
headerOffset = 0,
children
}) => {
const [content, setContent] = useState();
const [direction, setDirection] = useState(undefined);
const [show, setShow] = useState(false);
const [dropdownHeight, setDropdownHeight] = useState(0);
const [inputMeasurements, setInputMeasurements] = useState();
const [opacity, setOpacity] = useState(0);
const [contentStyles, setContentStyles] = useState(undefined);
const activeInputContainerRef = useRef(null);
const wrapperRef = useRef(null);
const activeControllerRef = useRef(null);
const controllerRefs = useRef([]);
const positionTrackingIntervalRef = useRef(undefined);
useEffect(() => {
if (!inputMeasurements?.height) {
setOpacity(0);
return;
}
if (dropdownHeight && direction === 'up') {
setContentStyles({
bottom: inputMeasurements.bottomY + 5 + headerOffset,
top: undefined,
left: inputMeasurements.x,
width: inputMeasurements.width
});
setOpacity(1);
} else if (direction === 'down') {
setContentStyles({
top: inputMeasurements.topY + inputMeasurements.height + 5 + headerOffset,
bottom: undefined,
left: inputMeasurements.x,
width: inputMeasurements.width
});
setOpacity(1);
}
}, [direction, dropdownHeight, headerOffset, inputMeasurements]);
const recalculatePosition = useCallback((showAfterCalculation = false) => {
activeInputContainerRef?.current?.measure((x, y, width, height, inputPageX, inputPageY) => {
wrapperRef.current?.measure((wrapperX, wrapperY, wrapperW, wrapperH, wrapperPageX, wrapperPageY) => {
const currentMeasurement = {
width,
height,
x: inputPageX,
topY: inputPageY - wrapperPageY,
bottomY: wrapperH - inputPageY + wrapperPageY
};
setInputMeasurements(prev => JSON.stringify(prev) === JSON.stringify(currentMeasurement) ? prev : currentMeasurement);
showAfterCalculation && setShow(true);
});
});
}, []);
useEffect(() => {
if (content) {
recalculatePosition(true);
} else {
setInputMeasurements(undefined);
setDropdownHeight(0);
setOpacity(0);
setContentStyles(undefined);
setShow(false);
}
}, [content, recalculatePosition]);
useEffect(() => {
if (show && !!opacity) {
positionTrackingIntervalRef.current = setInterval(() => {
requestAnimationFrame(() => {
recalculatePosition();
});
}, 16);
} else {
clearInterval(positionTrackingIntervalRef.current);
}
return () => {
clearInterval(positionTrackingIntervalRef.current);
};
}, [recalculatePosition, opacity, show]);
const onLayout = useCallback(e => {
setDropdownHeight(e.nativeEvent.layout.height);
}, []);
return /*#__PURE__*/_jsxs(AutocompleteDropdownContext.Provider, {
value: {
content,
setContent,
activeInputContainerRef,
direction,
setDirection,
activeControllerRef,
controllerRefs
},
children: [/*#__PURE__*/_jsx(View, {
ref: wrapperRef,
style: styles.clickOutsideHandlerArea,
onTouchEnd: () => {
activeControllerRef.current?.close();
activeControllerRef.current?.blur();
},
children: children
}), !!content && show && /*#__PURE__*/_jsx(View, {
onLayout: onLayout,
style: {
...styles.wrapper,
opacity,
...contentStyles
},
children: content
})]
});
};
const styles = StyleSheet.create({
clickOutsideHandlerArea: {
flex: 1
},
wrapper: {
position: 'absolute'
}
});
//# sourceMappingURL=AutocompleteDropdownContext.js.map