@audira/carbon-react-native
Version:
Build React Native apps with component and shared patterns using Carbon
148 lines (147 loc) • 5.34 kB
JavaScript
"use strict";
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Animated, Easing, View } from 'react-native';
import { Motion } from '@audira/carbon-react-native-elements';
import { CommonStyleSheet, FlexStyleSheet } from "../../_internal/style-sheets/index.js";
import { jsx as _jsx } from "react/jsx-runtime";
export const Collapsible = /*#__PURE__*/forwardRef(function ({
defaultOpen,
open: openProp,
motion = {
toOpen: {
duration: Motion.Duration.fast_02,
easing: Easing.bezier(Motion.Easing.entrance.productive.x1, Motion.Easing.entrance.productive.y1, Motion.Easing.entrance.productive.x2, Motion.Easing.entrance.productive.y2)
},
toClose: {
duration: Motion.Duration.fast_02,
easing: Easing.bezier(Motion.Easing.exit.productive.x1, Motion.Easing.exit.productive.y1, Motion.Easing.exit.productive.x2, Motion.Easing.exit.productive.y2)
}
},
children,
style,
contentContainerStyle,
onToggle,
onOpened,
onClosed,
...props
}, forwardedRef) {
const viewRef = useRef(null),
ref = useRef({
positionView: defaultOpen ?? openProp ? 'relative' : 'absolute',
contentHeight: 0,
openSelf: !!defaultOpen
}),
[openSelf, setOpenSelf] = useState(ref.current.openSelf),
/**
* Absolute position is required to keep content being rendered as it is when the container is not open (zero height)
*
* We need 'relative' position when the collapsible is initially opened due to heightAnimated initial value is zero and only will be calculated after content is rendered
*
* This state will be changed once only from 'relative' to 'absolute'
*/
[positionView, setPositionView] = useState(ref.current.positionView),
heightAnimated = useRef(new Animated.Value(0)),
/**
* - 0 -> Closed
* - 1 -> Opened
*/
contentContainerAnimated = useRef(new Animated.Value(0)),
controlled = typeof openProp === 'boolean',
open = controlled ? !!openProp : openSelf,
setPositionViewToAbsolute = useCallback(() => {
if (ref.current.positionView === 'relative') {
ref.current.positionView = 'absolute';
setPositionView('absolute');
}
}, []),
onLayoutContent = useCallback(({
nativeEvent
}) => {
if (ref.current.contentHeight !== nativeEvent.layout.height) {
ref.current.contentHeight = nativeEvent.layout.height;
if (open) {
Animated.timing(heightAnimated.current, {
toValue: ref.current.contentHeight,
duration: 0,
useNativeDriver: false
}).start(setPositionViewToAbsolute);
}
}
}, [heightAnimated, open, setPositionViewToAbsolute]);
useEffect(() => {
if (ref.current.contentHeight) {
if (open) {
Animated.timing(heightAnimated.current, {
toValue: ref.current.contentHeight,
useNativeDriver: false,
duration: motion.toOpen.duration,
easing: motion.toOpen.easing
}).start(onOpened);
Animated.timing(contentContainerAnimated.current, {
toValue: 1,
duration: motion.toOpen.duration,
easing: motion.toOpen.easing,
useNativeDriver: false
}).start();
} else {
Animated.timing(heightAnimated.current, {
toValue: 0,
duration: motion.toClose.duration,
easing: motion.toClose.easing,
useNativeDriver: false
}).start(onClosed);
Animated.timing(contentContainerAnimated.current, {
toValue: 0,
duration: motion.toClose.duration,
easing: motion.toClose.easing,
useNativeDriver: false
}).start();
}
onToggle?.(open);
}
}, [open, heightAnimated, contentContainerAnimated, motion, onToggle, onOpened, onClosed]);
useImperativeHandle(forwardedRef, () => {
return Object.assign(viewRef.current, {
setOpen(value) {
if (!controlled) {
if (typeof value === 'boolean') {
ref.current.openSelf = value;
} else {
ref.current.openSelf = value(ref.current.openSelf);
}
setOpenSelf(ref.current.openSelf);
}
}
});
}, [controlled]);
return /*#__PURE__*/_jsx(Animated.View, {
...props,
style: [CommonStyleSheet.overflow_hidden, positionView === 'absolute' ? {
height: heightAnimated.current
} : null, style],
ref: viewRef,
children: /*#__PURE__*/_jsx(View, {
style: [CommonStyleSheet.w_full, {
position: positionView
}],
children: /*#__PURE__*/_jsx(Animated.View, {
style: [FlexStyleSheet.flex_initial, {
opacity: contentContainerAnimated.current.interpolate({
inputRange: contentContainerInterpolationRange,
outputRange: [0, 1]
}),
transform: [{
translateY: contentContainerAnimated.current.interpolate({
inputRange: contentContainerInterpolationRange,
outputRange: [-12, 0]
})
}]
}, contentContainerStyle],
onLayout: onLayoutContent,
children: children
})
})
});
});
const contentContainerInterpolationRange = [0, 1];
//# sourceMappingURL=Collapsible.js.map