@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
186 lines (185 loc) • 9.39 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.useTheme = exports.BootstrapProvider = void 0;
const react_1 = __importStar(require("react"));
const react_native_1 = require("react-native");
const defaultTheme_1 = require("./defaultTheme");
const themeUtils_1 = require("./themeUtils");
const ThemeContext = (0, react_1.createContext)(undefined);
const BootstrapProvider = ({ children, theme: customTheme, mode: initialMode, followSystemTheme = true, enableAnimations = true, persistTheme = true, }) => {
const [mode, setModeState] = (0, react_1.useState)('light');
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
const [preferences, setPreferences] = (0, react_1.useState)(null);
const [isFollowingSystem, setIsFollowingSystem] = (0, react_1.useState)(followSystemTheme);
const [systemTheme, setSystemTheme] = (0, react_1.useState)(() => {
if (react_native_1.Platform.OS === 'web') {
return themeUtils_1.SystemTheme.getSystemTheme();
}
return react_native_1.Appearance.getColorScheme() === 'dark' ? 'dark' : 'light';
});
const initialized = (0, react_1.useRef)(false);
// Merge custom theme with default theme
const theme = react_1.default.useMemo(() => {
var _a, _b, _c;
if (!customTheme)
return defaultTheme_1.defaultTheme;
return Object.assign(Object.assign(Object.assign({}, defaultTheme_1.defaultTheme), customTheme), { colors: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.colors), customTheme.colors), darkColors: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.darkColors), customTheme.darkColors), spacing: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.spacing), customTheme.spacing), typography: Object.assign(Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.typography), customTheme.typography), { fontSizes: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.typography.fontSizes), (_a = customTheme.typography) === null || _a === void 0 ? void 0 : _a.fontSizes), fontWeights: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.typography.fontWeights), (_b = customTheme.typography) === null || _b === void 0 ? void 0 : _b.fontWeights), lineHeights: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.typography.lineHeights), (_c = customTheme.typography) === null || _c === void 0 ? void 0 : _c.lineHeights) }), borderRadius: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.borderRadius), customTheme.borderRadius), shadows: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.shadows), customTheme.shadows), breakpoints: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.breakpoints), customTheme.breakpoints), zIndex: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.zIndex), customTheme.zIndex), animation: Object.assign(Object.assign({}, defaultTheme_1.defaultTheme.animation), customTheme.animation) });
}, [customTheme]);
// Get current colors based on mode
const currentColors = react_1.default.useMemo(() => {
return mode === 'dark' ? theme.darkColors : theme.colors;
}, [theme, mode]);
// Initialize theme system
(0, react_1.useEffect)(() => {
if (initialized.current)
return;
initialized.current = true;
const initializeTheme = async () => {
try {
// Initialize theme manager
let finalMode = mode;
if (persistTheme) {
const savedMode = await themeUtils_1.ThemeStorage.loadThemeMode();
const savedPreferences = await themeUtils_1.ThemeStorage.loadPreferences();
if (savedMode) {
finalMode = savedMode;
}
else if (followSystemTheme && !initialMode) {
finalMode = systemTheme;
}
else if (initialMode) {
finalMode = initialMode;
}
setPreferences(savedPreferences);
}
else if (initialMode) {
finalMode = initialMode;
}
else if (followSystemTheme) {
finalMode = systemTheme;
}
// Initialize theme manager
await themeUtils_1.ThemeManager.initialize(followSystemTheme);
themeUtils_1.ThemeManager.setMode(finalMode, persistTheme);
setModeState(finalMode);
setIsFollowingSystem(themeUtils_1.ThemeManager.isFollowingSystem());
// Apply animations preferences
if (enableAnimations) {
themeUtils_1.ThemeAnimationUtils.respectReducedMotion();
}
}
catch (error) {
console.warn('Failed to initialize theme:', error);
}
finally {
setIsLoading(false);
}
};
initializeTheme();
}, []);
// Listen to theme manager changes
(0, react_1.useEffect)(() => {
const removeListener = themeUtils_1.ThemeManager.addListener((newMode) => {
setModeState(newMode);
setIsFollowingSystem(themeUtils_1.ThemeManager.isFollowingSystem());
});
return removeListener;
}, []);
// Listen to system theme changes (React Native)
(0, react_1.useEffect)(() => {
if (react_native_1.Platform.OS !== 'web') {
const subscription = react_native_1.Appearance.addChangeListener((preferences) => {
const newSystemTheme = preferences.colorScheme === 'dark' ? 'dark' : 'light';
setSystemTheme(newSystemTheme);
if (isFollowingSystem && followSystemTheme) {
themeUtils_1.ThemeManager.setMode(newSystemTheme, false);
}
});
return () => subscription === null || subscription === void 0 ? void 0 : subscription.remove();
}
return undefined;
}, [isFollowingSystem, followSystemTheme]);
// Listen to system theme changes (Web)
(0, react_1.useEffect)(() => {
if (react_native_1.Platform.OS === 'web') {
const removeListener = themeUtils_1.SystemTheme.addSystemThemeListener((newSystemTheme) => {
setSystemTheme(newSystemTheme);
if (isFollowingSystem && followSystemTheme) {
themeUtils_1.ThemeManager.setMode(newSystemTheme, false);
}
});
return removeListener || undefined;
}
return undefined;
}, [isFollowingSystem, followSystemTheme]);
// Enhanced setMode function
const setMode = react_1.default.useCallback(async (newMode) => {
if (enableAnimations) {
themeUtils_1.ThemeAnimationUtils.createThemeTransition();
}
await themeUtils_1.ThemeManager.setMode(newMode, persistTheme);
}, [enableAnimations, persistTheme]);
// Enhanced toggle function
const toggleMode = react_1.default.useCallback(async () => {
if (enableAnimations) {
themeUtils_1.ThemeAnimationUtils.createThemeTransition();
}
await themeUtils_1.ThemeManager.toggleMode();
}, [enableAnimations]);
const contextValue = {
theme,
mode,
setMode,
toggleMode,
currentColors,
isLoading,
preferences,
isFollowingSystem,
systemTheme,
};
return (<ThemeContext.Provider value={contextValue}>
{children}
</ThemeContext.Provider>);
};
exports.BootstrapProvider = BootstrapProvider;
const useTheme = () => {
const context = (0, react_1.useContext)(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a BootstrapProvider');
}
return context;
};
exports.useTheme = useTheme;