@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
JavaScript
;
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,
},
});