@whitemordred/react-native-bootstrap5
Version:
A complete React Native library that replicates Bootstrap 5.3 with 100% feature parity, full theming support, CSS variables, and dark/light mode
273 lines (271 loc) • 9.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useOffcanvas = exports.Offcanvas = exports.OffcanvasTitle = exports.OffcanvasBody = exports.OffcanvasHeader = void 0;
const react_1 = __importStar(require("react"));
const react_native_1 = require("react-native");
const ThemeProvider_1 = require("../theme/ThemeProvider");
const OffcanvasHeader = ({ children, style, closeButton = true, onClose, }) => {
const { theme } = (0, ThemeProvider_1.useTheme)();
return (<react_native_1.View style={[styles.header, { borderBottomColor: theme.colors.gray[200] }, style]}>
<react_native_1.View style={styles.headerContent}>
{children}
</react_native_1.View>
{closeButton && (<react_native_1.TouchableOpacity onPress={onClose} style={styles.closeButton}>
<react_native_1.Text style={[styles.closeText, { color: theme.colors.dark }]}>×</react_native_1.Text>
</react_native_1.TouchableOpacity>)}
</react_native_1.View>);
};
exports.OffcanvasHeader = OffcanvasHeader;
const OffcanvasBody = ({ children, style }) => {
return (<react_native_1.View style={[styles.body, style]}>
{children}
</react_native_1.View>);
};
exports.OffcanvasBody = OffcanvasBody;
const OffcanvasTitle = ({ children, style }) => {
const { theme } = (0, ThemeProvider_1.useTheme)();
return (<react_native_1.Text style={[styles.title, { color: theme.colors.dark }, style]}>
{children}
</react_native_1.Text>);
};
exports.OffcanvasTitle = OffcanvasTitle;
const Offcanvas = ({ visible, onClose, placement = 'start', backdrop = true, keyboard = true, scroll = true, children, style, }) => {
const [slideAnim] = (0, react_1.useState)(new react_native_1.Animated.Value(0));
const [backdropAnim] = (0, react_1.useState)(new react_native_1.Animated.Value(0));
const { theme } = (0, ThemeProvider_1.useTheme)();
const { width, height } = react_native_1.Dimensions.get('window');
(0, react_1.useEffect)(() => {
if (visible) {
react_native_1.Animated.parallel([
react_native_1.Animated.timing(slideAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
react_native_1.Animated.timing(backdropAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
]).start();
}
else {
react_native_1.Animated.parallel([
react_native_1.Animated.timing(slideAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}),
react_native_1.Animated.timing(backdropAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}),
]).start();
}
}, [visible]);
const getOffcanvasStyle = () => {
const baseStyle = {
position: 'absolute',
backgroundColor: theme.colors.white,
elevation: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.46,
shadowRadius: 11.14,
};
const transform = [];
switch (placement) {
case 'start':
baseStyle.left = 0;
baseStyle.top = 0;
baseStyle.bottom = 0;
baseStyle.width = Math.min(width * 0.75, 400);
transform.push({
translateX: slideAnim.interpolate({
inputRange: [0, 1],
outputRange: [-baseStyle.width, 0],
}),
});
break;
case 'end':
baseStyle.right = 0;
baseStyle.top = 0;
baseStyle.bottom = 0;
baseStyle.width = Math.min(width * 0.75, 400);
transform.push({
translateX: slideAnim.interpolate({
inputRange: [0, 1],
outputRange: [baseStyle.width, 0],
}),
});
break;
case 'top':
baseStyle.top = 0;
baseStyle.left = 0;
baseStyle.right = 0;
baseStyle.height = Math.min(height * 0.75, 600);
transform.push({
translateY: slideAnim.interpolate({
inputRange: [0, 1],
outputRange: [-baseStyle.height, 0],
}),
});
break;
case 'bottom':
baseStyle.bottom = 0;
baseStyle.left = 0;
baseStyle.right = 0;
baseStyle.height = Math.min(height * 0.75, 600);
transform.push({
translateY: slideAnim.interpolate({
inputRange: [0, 1],
outputRange: [baseStyle.height, 0],
}),
});
break;
}
return Object.assign(Object.assign({}, baseStyle), { transform });
};
const handleBackdropPress = () => {
if (backdrop === true) {
onClose();
}
};
const handleKeyboard = (event) => {
if (keyboard && event.nativeEvent.key === 'Escape') {
onClose();
}
};
if (!visible) {
return null;
}
return (<react_native_1.Modal transparent visible={visible} animationType="none" onRequestClose={onClose}>
<react_native_1.View style={styles.container}>
{/* Backdrop */}
{backdrop && (<react_native_1.Animated.View style={[
styles.backdrop,
{
opacity: backdropAnim,
},
]}>
<react_native_1.TouchableOpacity style={react_native_1.StyleSheet.absoluteFill} onPress={handleBackdropPress} activeOpacity={1}/>
</react_native_1.Animated.View>)}
{/* Offcanvas */}
<react_native_1.Animated.View style={[getOffcanvasStyle(), style]}>
<react_native_1.SafeAreaView style={styles.safeArea}>
{react_1.default.Children.map(children, (child, index) => {
if (react_1.default.isValidElement(child) && child.type === exports.OffcanvasHeader) {
return react_1.default.cloneElement(child, {
onClose: child.props.onClose || onClose,
});
}
return child;
})}
</react_native_1.SafeAreaView>
</react_native_1.Animated.View>
</react_native_1.View>
</react_native_1.Modal>);
};
exports.Offcanvas = Offcanvas;
// Hook for managing offcanvas
const useOffcanvas = () => {
const [visible, setVisible] = (0, react_1.useState)(false);
const [placement, setPlacement] = (0, react_1.useState)('start');
const show = (newPlacement) => {
if (newPlacement) {
setPlacement(newPlacement);
}
setVisible(true);
};
const hide = () => {
setVisible(false);
};
const toggle = (newPlacement) => {
if (visible) {
hide();
}
else {
show(newPlacement);
}
};
return {
visible,
placement,
show,
hide,
toggle,
};
};
exports.useOffcanvas = useOffcanvas;
const styles = react_native_1.StyleSheet.create({
container: {
flex: 1,
},
backdrop: Object.assign(Object.assign({}, react_native_1.StyleSheet.absoluteFillObject), { backgroundColor: 'rgba(0, 0, 0, 0.5)' }),
safeArea: {
flex: 1,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 16,
borderBottomWidth: 1,
minHeight: 60,
},
headerContent: {
flex: 1,
},
title: {
fontSize: 20,
fontWeight: '600',
},
closeButton: {
padding: 8,
marginLeft: 16,
},
closeText: {
fontSize: 24,
fontWeight: 'bold',
},
body: {
flex: 1,
padding: 16,
},
});
exports.default = exports.Offcanvas;