Skip to content

Instantly share code, notes, and snippets.

@apsavin
Created February 14, 2019 12:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apsavin/a84722e22c9e4419484cbda21575e6df to your computer and use it in GitHub Desktop.
Save apsavin/a84722e22c9e4419484cbda21575e6df to your computer and use it in GitHub Desktop.
Themes for isomorphic-style-loader
// @flow
import * as React from 'react';
import PropTypes from 'prop-types';
const contextTypes = {
insertCss: PropTypes.func,
};
type StylesContextType = KeyValueOfType<Object>;
const { Provider, Consumer } = React.createContext<StylesContextType>({});
const getThemeFromContext = <Theme>(id: string, context: StylesContextType): Theme | void => {
const theme = context[id];
if (!theme) {
return;
}
return (theme: any);
};
type StyleThemeResolverType<Theme, Props> = (Props, Theme | void) => Theme | void;
type StyleThemeParams<Theme, Props> = {
id: string,
styles: Array<Object>,
resolveTheme?: StyleThemeResolverType<Theme, Props>,
};
type StyleThemeProviderType<Theme, Props> = {
<Props>(Theme): (React.ComponentType<Props>) => React.ComponentType<Props>,
$$styleThemeId: string,
$$styleThemeStyles: Array<Object>,
$$styleThemeResolver: StyleThemeResolverType<Theme, Props>,
}
export function createStyleTheme<Theme, Props>(
{ id, styles, resolveTheme }: StyleThemeParams<Theme, Props>,
): StyleThemeProviderType<Theme, Props> {
const defaultResolver = (props, styleTheme) => styleTheme;
const resolver = resolveTheme || defaultResolver;
const styleThemeProvider = (theme) => (Component) => (props) => (
<Consumer>
{(context) => (
<Provider value={{ ...context, [id]: theme }}>
<Component {...props} />
</Provider>
)}
</Consumer>
);
styleThemeProvider.$$styleThemeId = id;
styleThemeProvider.$$styleThemeStyles = styles;
styleThemeProvider.$$styleThemeResolver = resolver;
return styleThemeProvider;
}
export type StyleThemePropType = {
s: Object,
};
type StylesType<Theme, Props> =
| CssModuleType
| StyleThemeProviderType<Theme, Props>;
function withStyles<Theme: Object, Props: Object>(styles: StylesType<Theme, Props>) {
let getStyles;
let resolveStyles: (Props, StylesContextType) => Theme | void;
if (typeof styles.$$styleThemeResolver === 'function') {
const resolver = styles.$$styleThemeResolver;
getStyles = () => styles.$$styleThemeStyles;
resolveStyles = (props, context) => {
const stylesTheme = getThemeFromContext(styles.$$styleThemeId, context);
return resolver(props, stylesTheme);
};
} else {
getStyles = () => [styles];
resolveStyles = () => {};
}
return function wrapWithStyles(
ComposedComponent: React.ComponentType<Props & StyleThemePropType>,
): React.ComponentType<Props> {
class WithStyles extends React.PureComponent<Props> {
removeCss: Function;
constructor(props, context) {
super(props, context);
this.removeCss = context.insertCss(...getStyles());
}
componentWillUnmount() {
if (this.removeCss) {
setTimeout(this.removeCss, 0);
}
}
render() {
return (
<Consumer>
{(context) => {
const s = resolveStyles(this.props, context);
return (
<ComposedComponent s={s} {...this.props} />
);
}}
</Consumer>
);
}
}
const displayName = ComposedComponent.displayName || ComposedComponent.name || '';
WithStyles.displayName = `WithStyles(${displayName})`;
WithStyles.contextTypes = contextTypes;
return WithStyles;
};
}
export default withStyles;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment