optimizely-oui
Version:
Optimizely's Component Library.
59 lines (49 loc) • 1.87 kB
JavaScript
import PropTypes from "prop-types";
import forwardRef from "@restart/context/forwardRef";
import React, { useContext } from "react";
const ThemeContext = React.createContext(new Map());
const { Consumer, Provider } = ThemeContext;
class ThemeProvider extends React.Component {
static propTypes = {
children: PropTypes.node,
prefixes: PropTypes.object.isRequired,
};
constructor(...args) {
super(...args);
this.prefixes = new Map();
Object.keys(this.props.prefixes).forEach(key => {
this.prefixes.set(key, this.props.prefixes[key]);
});
}
render() {
return <Provider value={this.prefixes}>{this.props.children}</Provider>;
}
}
export function useBootstrapPrefix(prefix, defaultPrefix) {
const prefixes = useContext(ThemeContext);
return prefix || prefixes.get(defaultPrefix) || defaultPrefix;
}
function createBootstrapComponent(Component, opts) {
if (typeof opts === "string") {
opts = { prefix: opts };
}
const isClassy = Component.prototype && Component.prototype.isReactComponent;
// If it's a functional component make sure we don't break it with a ref
const { prefix, forwardRefAs = isClassy ? "ref" : "innerRef" } = opts;
return forwardRef(
({ ...props }, ref) => {
props[forwardRefAs] = ref;
const prefixes = useContext(ThemeContext);
return (
<Component
{...props}
// eslint-disable-next-line react/prop-types
bsPrefix={props.bsPrefix || prefixes.get(prefix) || prefix}
/>
);
},
{ displayName: `Bootstrap(${Component.displayName || Component.name})` }
);
}
export { createBootstrapComponent, Consumer as ThemeConsumer };
export default ThemeProvider;