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

192 lines (191 loc) 8.3 kB
"use strict"; 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Button = void 0; const react_1 = __importDefault(require("react")); const react_native_1 = require("react-native"); const ThemeProvider_1 = require("../theme/ThemeProvider"); const validation_1 = require("../utils/validation"); const Button = (_a) => { var { variant = 'primary', size = 'default', disabled = false, loading = false, block = false, active = false, pill = false, gradient = false, children, style, textStyle, accessibilityLabel, accessibilityHint, accessibilityRole = 'button' } = _a, props = __rest(_a, ["variant", "size", "disabled", "loading", "block", "active", "pill", "gradient", "children", "style", "textStyle", "accessibilityLabel", "accessibilityHint", "accessibilityRole"]); const { theme, currentColors } = (0, ThemeProvider_1.useTheme)(); const errorHandler = (0, validation_1.createErrorHandler)('Button'); // Validate variant react_1.default.useEffect(() => { if (!(0, validation_1.isValidButtonVariant)(variant)) { (0, validation_1.warn)(`Invalid button variant: "${variant}". Using "primary" as fallback.`); } }, [variant]); const safeVariant = (0, validation_1.isValidButtonVariant)(variant) ? variant : 'primary'; const isOutline = safeVariant.startsWith('outline-'); const baseVariant = isOutline ? safeVariant.replace('outline-', '') : safeVariant; const getButtonStyles = () => { const baseStyles = { paddingHorizontal: theme.spacing[3], paddingVertical: theme.spacing[2], borderRadius: pill ? theme.borderRadius.full : theme.borderRadius.base, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', borderWidth: variant === 'link' ? 0 : 1, minHeight: size === 'lg' ? 48 : size === 'sm' ? 32 : 40, }; // Size styles switch (size) { case 'sm': baseStyles.paddingHorizontal = theme.spacing[2]; baseStyles.paddingVertical = theme.spacing[1]; break; case 'lg': baseStyles.paddingHorizontal = theme.spacing[4]; baseStyles.paddingVertical = theme.spacing[3]; break; } // Block style if (block) { baseStyles.width = '100%'; } // Variant styles if (variant === 'link') { baseStyles.backgroundColor = 'transparent'; baseStyles.paddingHorizontal = 0; baseStyles.paddingVertical = 0; baseStyles.minHeight = undefined; } else if (isOutline) { baseStyles.backgroundColor = 'transparent'; baseStyles.borderColor = currentColors[baseVariant] || currentColors.primary; // Active state for outline buttons if (active) { baseStyles.backgroundColor = currentColors[baseVariant] || currentColors.primary; } } else { const bgColor = currentColors[baseVariant] || currentColors.primary; baseStyles.backgroundColor = bgColor; baseStyles.borderColor = bgColor; // Gradient effect (only on solid buttons) if (gradient && variant !== 'light' && variant !== 'dark') { // React Native doesn't support gradients natively, so we'll simulate with shadow baseStyles.shadowColor = bgColor; baseStyles.shadowOffset = { width: 0, height: 2 }; baseStyles.shadowOpacity = 0.25; baseStyles.shadowRadius = 4; baseStyles.elevation = 4; } } // Active state styles if (active && !isOutline && variant !== 'link') { baseStyles.opacity = 0.85; } // Disabled styles if (disabled || loading) { baseStyles.opacity = 0.6; } return baseStyles; }; const getTextStyles = () => { const baseTextStyles = { fontSize: theme.typography.fontSizes.base, fontWeight: theme.typography.fontWeights.medium, }; // Size styles switch (size) { case 'sm': baseTextStyles.fontSize = theme.typography.fontSizes.sm; break; case 'lg': baseTextStyles.fontSize = theme.typography.fontSizes.lg; break; } // Color styles if (variant === 'link') { baseTextStyles.color = currentColors.primary; baseTextStyles.textDecorationLine = 'underline'; } else if (isOutline) { baseTextStyles.color = currentColors[baseVariant] || currentColors.primary; // Active state for outline buttons if (active) { baseTextStyles.color = currentColors.white; if (baseVariant === 'light' || baseVariant === 'warning' || baseVariant === 'yellow') { baseTextStyles.color = currentColors.dark; } } } else { // Determine text color based on variant const lightTextVariants = ['light', 'warning', 'yellow']; if (lightTextVariants.includes(baseVariant)) { baseTextStyles.color = currentColors.dark; } else { baseTextStyles.color = currentColors.white; } } // Disabled text styles if (disabled) { baseTextStyles.opacity = 0.65; } return baseTextStyles; }; const buttonStyles = getButtonStyles(); const buttonTextStyles = getTextStyles(); // Prepare accessibility state const accessibilityState = { disabled: disabled || loading, busy: loading, selected: active, }; // Auto-generate accessibility label if not provided const defaultAccessibilityLabel = react_1.default.useMemo(() => { if (typeof children === 'string') { return children; } return 'Button'; }, [children]); // Safe render with error handling try { return (<react_native_1.TouchableOpacity style={[buttonStyles, (0, validation_1.safeStyle)(style)]} disabled={disabled || loading} accessibilityRole={accessibilityRole} accessibilityLabel={accessibilityLabel || defaultAccessibilityLabel} accessibilityHint={accessibilityHint} accessibilityState={accessibilityState} {...props}> {loading && (<react_native_1.ActivityIndicator size="small" color={buttonTextStyles.color} style={{ marginRight: theme.spacing[1] }} accessibilityLabel="Loading"/>)} <react_native_1.Text style={[buttonTextStyles, textStyle]}> {children} </react_native_1.Text> </react_native_1.TouchableOpacity>); } catch (error) { errorHandler(error); // Fallback UI return (<react_native_1.TouchableOpacity style={[styles.errorButton, (0, validation_1.safeStyle)(style)]} disabled accessibilityRole="button" accessibilityLabel="Button unavailable"> <react_native_1.Text style={styles.errorText}>Button Error</react_native_1.Text> </react_native_1.TouchableOpacity>); } }; exports.Button = Button; const styles = react_native_1.StyleSheet.create({ errorButton: { backgroundColor: '#f8d7da', borderColor: '#f5c6cb', borderWidth: 1, padding: 12, borderRadius: 6, alignItems: 'center', }, errorText: { color: '#721c24', fontSize: 14, }, });