UNPKG

@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

323 lines (321 loc) 13 kB
"use strict"; 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; }; })(); var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Popover = exports.Tooltip = void 0; const react_1 = __importStar(require("react")); const react_native_1 = require("react-native"); const ThemeProvider_1 = require("../theme/ThemeProvider"); const Tooltip = ({ children, content, placement = 'top', trigger = 'hover', visible: controlledVisible, onVisibilityChange, style, tooltipStyle, textStyle, backgroundColor, textColor, delay = 500, }) => { const [visible, setVisible] = (0, react_1.useState)(false); const [childLayout, setChildLayout] = (0, react_1.useState)({ x: 0, y: 0, width: 0, height: 0 }); const [tooltipLayout, setTooltipLayout] = (0, react_1.useState)({ width: 0, height: 0 }); const childRef = (0, react_1.useRef)(null); const timeoutRef = (0, react_1.useRef)(); const { theme } = (0, ThemeProvider_1.useTheme)(); const isVisible = controlledVisible !== undefined ? controlledVisible : visible; const showTooltip = () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } timeoutRef.current = setTimeout(() => { setVisible(true); onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(true); }, delay); }; const hideTooltip = () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } setVisible(false); onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(false); }; const toggleTooltip = () => { if (isVisible) { hideTooltip(); } else { showTooltip(); } }; const measureChild = () => { if (childRef.current) { childRef.current.measure((x, y, width, height, pageX, pageY) => { setChildLayout({ x: pageX, y: pageY, width, height }); }); } }; const getTooltipPosition = () => { const { width: screenWidth, height: screenHeight } = react_native_1.Dimensions.get('window'); const margin = 10; let top = 0; let left = 0; switch (placement) { case 'top': top = childLayout.y - tooltipLayout.height - margin; left = childLayout.x + (childLayout.width - tooltipLayout.width) / 2; break; case 'bottom': top = childLayout.y + childLayout.height + margin; left = childLayout.x + (childLayout.width - tooltipLayout.width) / 2; break; case 'left': top = childLayout.y + (childLayout.height - tooltipLayout.height) / 2; left = childLayout.x - tooltipLayout.width - margin; break; case 'right': top = childLayout.y + (childLayout.height - tooltipLayout.height) / 2; left = childLayout.x + childLayout.width + margin; break; } // Ensure tooltip stays within screen bounds if (left < margin) left = margin; if (left + tooltipLayout.width > screenWidth - margin) { left = screenWidth - tooltipLayout.width - margin; } if (top < margin) top = margin; if (top + tooltipLayout.height > screenHeight - margin) { top = screenHeight - tooltipLayout.height - margin; } return { top, left }; }; const getArrowPosition = () => { const arrowSize = 8; switch (placement) { case 'top': return { position: 'absolute', bottom: -arrowSize, left: '45%', borderLeftWidth: arrowSize / 2, borderRightWidth: arrowSize / 2, borderTopWidth: arrowSize, borderLeftColor: 'transparent', borderRightColor: 'transparent', borderTopColor: backgroundColor || theme.colors.dark, }; case 'bottom': return { position: 'absolute', top: -arrowSize, left: '45%', borderLeftWidth: arrowSize / 2, borderRightWidth: arrowSize / 2, borderBottomWidth: arrowSize, borderLeftColor: 'transparent', borderRightColor: 'transparent', borderBottomColor: backgroundColor || theme.colors.dark, }; case 'left': return { position: 'absolute', right: -arrowSize, top: '45%', borderTopWidth: arrowSize / 2, borderBottomWidth: arrowSize / 2, borderLeftWidth: arrowSize, borderTopColor: 'transparent', borderBottomColor: 'transparent', borderLeftColor: backgroundColor || theme.colors.dark, }; case 'right': return { position: 'absolute', left: -arrowSize, top: '45%', borderTopWidth: arrowSize / 2, borderBottomWidth: arrowSize / 2, borderRightWidth: arrowSize, borderTopColor: 'transparent', borderBottomColor: 'transparent', borderRightColor: backgroundColor || theme.colors.dark, }; default: return {}; } }; const getTriggerProps = () => { switch (trigger) { case 'click': return { onPress: () => { measureChild(); toggleTooltip(); }, }; case 'focus': return { onFocus: () => { measureChild(); showTooltip(); }, onBlur: hideTooltip, }; default: // hover return { onPressIn: () => { measureChild(); showTooltip(); }, onPressOut: hideTooltip, }; } }; const tooltipPosition = getTooltipPosition(); return (<> <react_native_1.TouchableOpacity style={style} activeOpacity={trigger === 'hover' ? 0.8 : 1} {...getTriggerProps()} onLayout={() => measureChild()}> {children} </react_native_1.TouchableOpacity> <react_native_1.Modal transparent visible={isVisible} animationType="fade" onRequestClose={hideTooltip}> <react_native_1.TouchableOpacity style={styles.overlay} activeOpacity={1} onPress={trigger === 'click' ? hideTooltip : undefined}> <react_native_1.View style={[ styles.tooltip, { backgroundColor: backgroundColor || theme.colors.dark, top: tooltipPosition.top, left: tooltipPosition.left, }, tooltipStyle, ]} onLayout={(event) => { const { width, height } = event.nativeEvent.layout; setTooltipLayout({ width, height }); }}> {typeof content === 'string' ? (<react_native_1.Text style={[styles.tooltipText, { color: textColor || theme.colors.white }, textStyle]}> {content} </react_native_1.Text>) : (content)} <react_native_1.View style={getArrowPosition()}/> </react_native_1.View> </react_native_1.TouchableOpacity> </react_native_1.Modal> </>); }; exports.Tooltip = Tooltip; const Popover = (_a) => { var { title, dismissible = true, variant = 'light' } = _a, tooltipProps = __rest(_a, ["title", "dismissible", "variant"]); const { theme } = (0, ThemeProvider_1.useTheme)(); const variantColors = theme.colors[variant]; const backgroundColor = typeof variantColors === 'string' ? variantColors : (variantColors === null || variantColors === void 0 ? void 0 : variantColors[500]) || theme.colors.light; const textColor = variant === 'light' ? theme.colors.dark : theme.colors.white; const content = (<react_native_1.View style={styles.popoverContent}> {title && (<react_native_1.View style={[styles.popoverHeader, { borderBottomColor: theme.colors.gray[200] }]}> <react_native_1.Text style={[styles.popoverTitle, { color: textColor }]}> {title} </react_native_1.Text> {dismissible && (<react_native_1.TouchableOpacity onPress={() => { var _a; return (_a = tooltipProps.onVisibilityChange) === null || _a === void 0 ? void 0 : _a.call(tooltipProps, false); }} style={styles.popoverClose}> <react_native_1.Text style={[styles.popoverCloseText, { color: textColor }]}>×</react_native_1.Text> </react_native_1.TouchableOpacity>)} </react_native_1.View>)} <react_native_1.View style={styles.popoverBody}> {typeof tooltipProps.content === 'string' ? (<react_native_1.Text style={[styles.popoverText, { color: textColor }]}> {tooltipProps.content} </react_native_1.Text>) : (tooltipProps.content)} </react_native_1.View> </react_native_1.View>); return (<exports.Tooltip {...tooltipProps} content={content} backgroundColor={backgroundColor} textColor={textColor} trigger="click" tooltipStyle={react_native_1.StyleSheet.flatten([styles.popover, tooltipProps.tooltipStyle])}/>); }; exports.Popover = Popover; const styles = react_native_1.StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'transparent', }, tooltip: { position: 'absolute', paddingHorizontal: 8, paddingVertical: 6, borderRadius: 4, maxWidth: 250, elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 4, }, tooltipText: { fontSize: 14, textAlign: 'center', }, popover: { padding: 0, minWidth: 200, maxWidth: 300, }, popoverContent: { overflow: 'hidden', }, popoverHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, }, popoverTitle: { fontSize: 16, fontWeight: '600', flex: 1, }, popoverClose: { padding: 4, }, popoverCloseText: { fontSize: 20, fontWeight: 'bold', }, popoverBody: { paddingHorizontal: 16, paddingVertical: 12, }, popoverText: { fontSize: 14, lineHeight: 20, }, }); exports.default = exports.Tooltip;