import * as React from 'react' import * as cx from 'classnames' import { dasherize, flatten, eventValue } from '../utils' import { Breakpoint, FlexProps, FlexConfig, FlexConfigKey, FlexShortcutProp, FlexShortcutProps, FlexShortcutPropsKey, } from '../interfaces' import { BREAKPOINTS, FLEX_WIDTH_CONFIG, FLEX_SHORTCUT_PROPS, } from '../constants' const styles = require('../../src/styles/components/flex.scss') export abstract class Flex extends React.Component { public static defaultProps: FlexProps = {} /** * Basically we are normalising class for comparison of values so we don't get overlaps */ private static classAndNormalised(key: string, width: Breakpoint, value: string): string[] { return [ styles[`${dasherize(key)}-${width}-${value}`], `${dasherize(key)}-${width}`, ] } protected defaultClasses: string[] = [] protected shortcutProps: FlexShortcutProps = FLEX_SHORTCUT_PROPS private flexClassesCache: string[] public componentWillMount() { this.flexClassesCache = this.flexClasses(this.props) } public componentWillUpdate(nextProps: any) { this.updateFlexClasses(nextProps) } public render(): JSX.Element { return (
{this.props.children}
) } protected baseProps(): object { const { id, children, style, onClick, tooltip, tooltipPos, } = (this.props as FlexProps) return { id, onClick, style, 'className': this.classNames(), 'data-tooltip': tooltip, 'data-tooltip-pos': tooltipPos, } } protected classNames(): string { const props = (this.props as FlexProps) // Use classnames to build up a string of classnames return cx( this.defaultClasses, styles.container, ...this.flexClassesCache, { [styles.fullWidth]: props.fullWidth, [styles.expandLeft]: props.expand === 'left', [styles.expandRight]: props.expand === 'right', }, props.className, ) } private updateFlexClasses(nextProps: FlexProps) { const props = (this.props as FlexProps) const shortcutPropsUpdated = Object.keys(FLEX_SHORTCUT_PROPS).reduce( (acc, key: keyof FlexProps) => acc || nextProps[key] !== props[key], false, ) const breakpointsUpdated = Object.keys(BREAKPOINTS).reduce( (acc, key: keyof FlexProps) => acc || nextProps[key] !== props[key], false, ) if (shortcutPropsUpdated || breakpointsUpdated) { this.flexClassesCache = this.flexClasses(nextProps) } } private flexClasses(props: FlexProps): string[] { // Build a map of classes keyed by their normal name const shortcutClasses = flatten(Object.keys(this.shortcutProps) .filter((prop: FlexShortcutPropsKey) => props[prop] !== undefined) .map((prop: FlexShortcutPropsKey) => this.classesFromProp(prop, (props[prop] as string)), ), ) .reduce( (dst, [cls, nrmCls]) => ({ ...dst, [nrmCls]: cls }), {}, ) // Reduce override classes into the initial map, overriding based on normalised name const classes = flatten(BREAKPOINTS.map(width => FLEX_WIDTH_CONFIG .filter((key: FlexConfigKey) => props[width] !== undefined && (props[width] as any)[key] !== undefined) .map((key: FlexConfigKey) => this.classFromOverride(props, width, key)), )) .reduce( (dst, [cls, nrmCls]) => ({ ...dst, [nrmCls]: cls }), shortcutClasses, ) return Object.values(classes) } private classFromOverride(props: FlexProps, width: Breakpoint, key: FlexConfigKey): string[] { return Flex.classAndNormalised(key, width, ((props[width] as FlexConfig)[key] as any)) } private classesFromProp(prop: FlexShortcutPropsKey, value: string): string[][] { const { key, propIsValue } = this.shortcutProps[prop] return propIsValue ? [Flex.classAndNormalised(key, (value as Breakpoint), dasherize(prop))] : [Flex.classAndNormalised(key, BREAKPOINTS[0], value)] } }