Skip to content

Instantly share code, notes, and snippets.

@dested
Created December 23, 2018 19:00
Show Gist options
  • Save dested/577f8d0edccbd0f2bef095b8758c531f to your computer and use it in GitHub Desktop.
Save dested/577f8d0edccbd0f2bef095b8758c531f to your computer and use it in GitHub Desktop.
import React from 'react';
import {StyleProp, TextStyle, View, ViewProps, ViewStyle} from 'react-native';
export interface WithClassProps {
classNames?: string[];
componentTree?: ComponentTree;
style?: StyleProp<TextStyle>;
}
type ComponentTree = {
classNames: string[];
parent?: ComponentTree;
};
const satisfiesStyle = (styleKey: string, componentTree: ComponentTree) => {
const keys = styleKey.split(' ');
let currentComponentTree = componentTree;
let i: number;
for (i = keys.length - 1; i >= 0; ) {
const key = keys[i];
if (!currentComponentTree) {
return false;
}
if (currentComponentTree.classNames && currentComponentTree.classNames.includes(key)) {
i--;
}
currentComponentTree = currentComponentTree.parent;
}
return i === -1;
};
const deriveStyles = (styles: any, componentTree: ComponentTree) => {
const dStyles: ViewStyle[] = [];
if (componentTree.classNames) {
for (const styleKey of Object.keys(styles)) {
if (satisfiesStyle(styleKey, componentTree)) {
dStyles.push((styles as any)[styleKey]);
}
}
}
return dStyles;
};
export function withClass<P extends ViewProps>(
WrappedComponent: React.ComponentClass<P> | React.FunctionComponent<P>
): React.ComponentClass<P & WithClassProps> {
return class extends React.Component<P & WithClassProps> {
constructor(props: P & WithClassProps) {
super(props);
this.state = {};
}
componentTree: ComponentTree;
componentWillMount() {
this.componentTree = {
parent: this.props.componentTree,
classNames: this.props.classNames,
};
}
componentDidMount() {
this.componentTree.parent = this.props.componentTree;
this.componentTree.classNames = this.props.classNames;
}
componentWillUpdate(nextProps: Readonly<P & WithClassProps>): void {
this.componentTree.classNames = nextProps.classNames;
}
componentWillUnmount() {}
render() {
const {style, children, ...props} = this.props;
return (
<CssContext.Consumer>
{state => (
<WrappedComponent
style={[this.componentTree.classNames && deriveStyles(state, this.componentTree), style]}
{...props}
>
{React.Children.map(children, a => React.cloneElement(a, {componentTree: this.componentTree}))}
</WrappedComponent>
)}
</CssContext.Consumer>
);
}
};
}
export const ViewWithClass = withClass(View);
const store: {styles: any} = {styles: null};
export const CssContext = React.createContext(store);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment