rmwc
Version:
A thin React wrapper for Material Design (Web) Components
118 lines (100 loc) • 2.99 kB
Flow
// @flow
import * as React from 'react';
import classNamesFunc from 'classnames';
import { parseThemeOptions } from './withTheme';
type SimpleTagFactoryT = {
tag?: string | React.ComponentType<any>,
classNames?: string | string[] | ((props: Object) => mixed),
defaultProps?: Object,
consumeProps?: string[],
wrap?: boolean,
displayName?: string,
className?: string
};
export type SimpleTagPropsT = {
tag?: string | React.ComponentType<any>,
className?: string,
wrap?: boolean,
elementRef?: any,
theme?: string | string[],
children?: React.Node,
apiRef?: <S>(api: S) => S
//$FlowFixMe
} & React.HTMLProps<any>;
export const simpleTag = ({
displayName = 'SimpleTag',
defaultProps = {},
consumeProps = [],
tag,
wrap: defaultWrap = false,
classNames
}: SimpleTagFactoryT) => {
const defaultTag = tag || 'div';
const S = class SimpleTag<P> extends React.Component<
P & SimpleTagPropsT & any
> {
static displayName = displayName;
static defaultProps = {
...defaultProps,
tag: defaultTag
};
static isSimpleTag = true;
render() {
const {
tag,
className,
wrap = false,
elementRef,
theme,
...rest
} = this.props;
// choose the tag we are going to render
const Component =
typeof defaultTag === 'function' && typeof tag === 'string'
? defaultTag
: tag || defaultTag;
// consume any props that shouldnt be passed along
const safeRest = { ...rest };
consumeProps.forEach(p => {
delete safeRest[p];
});
// sometimes we are extending a component, we can still honor a text based tag
if (typeof defaultTag === 'function' && typeof tag === 'string') {
safeRest.tag = tag;
}
// handle elementRefs
if (elementRef) {
// if the tag is a string, make our ref
// otherwise pass elementRef along
if (typeof Component === 'string') {
safeRest.ref = elementRef;
} else {
safeRest.elementRef = elementRef;
}
}
// generate the final classnames for the component
const safeClassNames = classNamesFunc(
className,
parseThemeOptions(theme || null),
typeof classNames === 'function' ? classNames(rest) : classNames
);
// handle wrapping components
if (wrap || defaultWrap) {
// sometimes we have undefined children
if (!rest.children) return null;
// make sure we delete our children here
// so we dont recursively clone ourselves
delete safeRest.children;
const child = React.Children.only(rest.children);
return React.cloneElement(child, {
...child.props,
...safeRest,
...{ className: safeClassNames }
});
}
// default return
return <Component className={safeClassNames} {...safeRest} />;
}
};
return S;
};