Split Box props

parent 8d5e5f36
import React, { createElement, forwardRef, memo } from 'react';
import React, {
HTMLAttributes,
createElement,
forwardRef,
memo,
Ref,
DetailedHTMLProps,
SVGProps,
ComponentType,
} from 'react';
import { useStyleSheet } from '../../hooks/useStyleSheet';
import { useBoxTransform, BoxTransforms } from './BoxTransforms';
import { BoxProps, consumeBoxProps } from './props';
import { useArrayLikeClassNameProp } from './useArrayLikeClassNameProp';
import { BoxProps, useBoxProps } from './props';
export const Box = memo(
forwardRef(function Box({ is = 'div', children, ...props }: BoxProps, ref) {
forwardRef(function Box<
E = HTMLOrSVGElement,
P = DetailedHTMLProps<HTMLAttributes<E>, E> | SVGProps<E>
>({ is, children, ...props }: BoxProps<P>, ref: Ref<E>) {
useStyleSheet();
let consumedProps = props as Record<string, unknown>;
const transformFn = useBoxTransform();
if (transformFn) {
consumedProps = transformFn(consumedProps);
}
consumedProps = consumeBoxProps(consumedProps);
consumedProps = useArrayLikeClassNameProp(consumedProps);
if (ref) {
consumedProps.ref = ref;
}
const element = createElement(is, consumedProps, children);
if (transformFn) {
return <BoxTransforms.Provider children={element} value={undefined} />;
}
return element;
const mappedProps = useBoxProps<P>(
transformFn ? transformFn(props) : props
);
const element = createElement(
is,
Object.assign(mappedProps, { ref }),
children
);
return transformFn ? (
<BoxTransforms.Provider children={element} value={undefined} />
) : (
element
);
})
);
Box.displayName = 'Box';
(Box as ComponentType).defaultProps = {
is: 'div',
};
export { default as AnimatedVisibility } from './AnimatedVisibility';
export { default as Flex } from './Flex';
export { default as Position, PositionAnimated } from './Position';
export { default as Scrollable } from './Scrollable';
export default memo(Box);
......@@ -39,17 +39,11 @@ export type PostAliasTypes = {
export const isPreAlias = (propName: string): propName is keyof PreAliasTypes =>
typeof preAliases[propName] !== 'undefined';
export const consumePreAlias = <
P extends {
className?:
| string
| ReturnType<typeof css>
| (string | ReturnType<typeof css>)[];
}
>(
export const consumePreAlias = <P>(
alias: keyof PreAliasTypes,
value: PreAliasTypes[keyof PreAliasTypes],
props: P
props: P,
classNames: (string | ReturnType<typeof css>)[]
): void => {
const effectivePropName = preAliases[alias];
......@@ -57,7 +51,7 @@ export const consumePreAlias = <
return;
}
consumeCssPropertyProp(effectivePropName, value, props);
consumeCssPropertyProp(effectivePropName, value, props, classNames);
};
export const isPostAlias = (
......@@ -65,17 +59,11 @@ export const isPostAlias = (
): propName is keyof PostAliasTypes =>
typeof postAliases[propName] !== 'undefined';
export const consumePostAlias = <
P extends {
className?:
| string
| ReturnType<typeof css>
| (string | ReturnType<typeof css>)[];
}
>(
export const consumePostAlias = <P>(
alias: keyof PostAliasTypes,
value: PostAliasTypes[keyof PostAliasTypes],
props: P
value: PostAliasTypes[keyof PostAliasTypes & keyof P],
props: P,
_classNames: (string | ReturnType<typeof css>)[]
): void => {
const effectivePropName = postAliases[alias];
......
import { css } from '@rocket.chat/css-in-js';
import { appendClassName } from '../../../helpers/appendClassName';
import { fromCamelToKebab } from '../../../helpers/fromCamelToKebab';
import * as styleTokens from '../../../styleTokens';
......@@ -138,17 +137,11 @@ export const isCssPropertyProp = (
): propName is keyof CssPropertyPropTypes =>
typeof cssPropertiesProps[propName] !== 'undefined';
export const consumeCssPropertyProp = <
P extends {
className?:
| string
| ReturnType<typeof css>
| (string | ReturnType<typeof css>)[];
}
>(
export const consumeCssPropertyProp = <P>(
propName: keyof CssPropertyPropTypes,
propValue: CssPropertyPropTypes[keyof CssPropertyPropTypes],
props: P
_props: P,
classNames: (string | ReturnType<typeof css>)[]
): void => {
const fn = cssPropertiesProps[propName];
const cssProperty = fromCamelToKebab(propName);
......@@ -161,5 +154,5 @@ export const consumeCssPropertyProp = <
${`${cssProperty}: ${cssValue} !important;`}
`;
props.className = appendClassName(props.className, style);
classNames.push(style);
};
/* eslint-disable prettier/prettier */
import { css } from '@rocket.chat/css-in-js';
import { AllHTMLAttributes, ElementType, SVGAttributes } from 'react';
import { ElementType, PropsWithChildren } from 'react';
import { prependClassName } from '../../../helpers/prependClassName';
import { useStyle } from '../../../hooks/useStyle';
import {
consumePostAlias,
consumePreAlias,
......@@ -28,83 +29,127 @@ export type BoxStylingProps = SpecialStylingPropTypes &
PreAliasTypes &
PostAliasTypes;
type BoxOnlyProps = {
type BoxOnlyProps<P extends { className?: string }> = {
animated?: boolean;
className?:
| string
| ReturnType<typeof css>
| (string | ReturnType<typeof css>)[];
is?: ElementType;
is?: ElementType<P>;
};
export type BoxProps = BoxStylingProps &
BoxOnlyProps &
Omit<AllHTMLAttributes<HTMLOrSVGElement>, keyof BoxOnlyProps> &
Omit<SVGAttributes<HTMLOrSVGElement>, keyof BoxOnlyProps>;
export const useBoxStylingProps = <P extends { className?: string }>(
props: BoxStylingProps & Omit<P, 'className' | keyof BoxStylingProps>
): P => {
const targetProps: Partial<P> = {};
const targetClassNames: (string | ReturnType<typeof css>)[] = [];
export const consumeBoxStylingProps = (
props: BoxProps
): Record<string, unknown> => {
for (const [propName, propValue] of Object.entries(props)) {
if (isPreAlias(propName)) {
delete props[propName];
consumePreAlias(propName, propValue, props);
consumePreAlias(propName, propValue, targetProps, targetClassNames);
continue;
}
if (isSpecialStylingProp(propName)) {
delete props[propName];
consumeSpecialStylingProp(propName, propValue, props);
consumeSpecialStylingProp(
propName,
propValue,
targetProps,
targetClassNames
);
continue;
}
if (isCssPropertyProp(propName)) {
delete props[propName];
consumeCssPropertyProp(propName, propValue, props);
consumeCssPropertyProp(
propName,
propValue,
targetProps,
targetClassNames
);
continue;
}
if (isPostAlias(propName)) {
delete props[propName];
consumePostAlias(propName, propValue, props);
consumePostAlias(propName, propValue, targetProps, targetClassNames);
continue;
}
targetProps[propName] = propValue;
}
const styles = targetClassNames.filter(
(value): value is ReturnType<typeof css> => typeof value === 'function'
);
const stylesClassName = useStyle(
css`
${styles}
`,
props
);
const classNames = targetClassNames.filter(
(value): value is string => typeof value === 'string'
);
if (stylesClassName) {
classNames.push(stylesClassName);
}
return props;
return Object.assign(targetProps, {
className: classNames.join(' '),
}) as P;
};
export const consumeBoxProps = (props: BoxProps): Record<string, unknown> => {
export type BoxProps<P> = PropsWithChildren<BoxStylingProps & BoxOnlyProps<P>> &
Omit<P, 'className' | keyof (BoxStylingProps & BoxOnlyProps<P>)>;
export const useBoxProps = <P extends { className?: string }>(
props: BoxProps<P>
): P => {
const targetProps: Partial<P> = {};
const targetClassNames: (string | ReturnType<typeof css>)[] = [
'rcx-box',
'rcx-box--full',
];
for (const [propName, propValue] of Object.entries(props)) {
if (isRcxProp(propName)) {
delete props[propName];
consumeRcxProp(propName, propValue, props);
consumeRcxProp(propName, propValue, targetProps, targetClassNames);
continue;
}
if (isPreAlias(propName)) {
delete props[propName];
consumePreAlias(propName, propValue, props);
consumePreAlias(propName, propValue, targetProps, targetClassNames);
continue;
}
if (isSpecialStylingProp(propName)) {
delete props[propName];
consumeSpecialStylingProp(propName, propValue, props);
consumeSpecialStylingProp(
propName,
propValue,
targetProps,
targetClassNames
);
continue;
}
if (isCssPropertyProp(propName)) {
delete props[propName];
consumeCssPropertyProp(propName, propValue, props);
consumeCssPropertyProp(
propName,
propValue,
targetProps,
targetClassNames
);
continue;
}
if (isPostAlias(propName)) {
delete props[propName];
consumePostAlias(propName, propValue, props);
consumePostAlias(propName, propValue, targetProps, targetClassNames);
continue;
}
targetProps[propName] = propValue;
}
if (props.animated) {
......@@ -114,5 +159,25 @@ export const consumeBoxProps = (props: BoxProps): Record<string, unknown> => {
props.className = prependClassName(props.className, 'rcx-box--full');
props.className = prependClassName(props.className, 'rcx-box');
return props;
const styles = targetClassNames.filter(
(value): value is ReturnType<typeof css> => typeof value === 'function'
);
const stylesClassName = useStyle(
css`
${styles}
`,
props
);
const classNames = targetClassNames.filter(
(value): value is string => typeof value === 'string'
);
if (stylesClassName) {
classNames.push(stylesClassName);
}
return Object.assign(targetProps, {
className: classNames.join(' '),
}) as P;
};
import { css } from '@rocket.chat/css-in-js';
import { prependClassName } from '../../../helpers/prependClassName';
export type RcxPropTypes = {
[propName in string]?: boolean | number | string;
};
export const isRcxProp = (propName: string): propName is keyof RcxPropTypes =>
export const isRcxProp = (propName: string): boolean =>
propName.slice(0, 4) === 'rcx-';
export const consumeRcxProp = <
......@@ -19,7 +17,8 @@ export const consumeRcxProp = <
>(
rcxClassName: keyof RcxPropTypes,
value: RcxPropTypes[keyof RcxPropTypes],
targetProps: P
_props: P,
classNames: (string | ReturnType<typeof css>)[]
): void => {
if (!value) {
return;
......@@ -27,5 +26,5 @@ export const consumeRcxProp = <
const newClassName =
value === true ? rcxClassName : `${rcxClassName}-${value}`;
targetProps.className = prependClassName(targetProps.className, newClassName);
classNames.push(newClassName);
};
import { appendClassName } from '../../../helpers/appendClassName';
import { css } from '@rocket.chat/css-in-js';
import { elevation } from '../../../styles/runtime/elevation';
import { fontScale } from '../../../styles/runtime/fontScales';
import { invisible } from '../../../styles/runtime/invisible';
......@@ -27,10 +28,11 @@ export const isSpecialStylingProp = (
propName: string
): propName is keyof SpecialStylingPropTypes => propName in specialStylingProps;
export const consumeSpecialStylingProp = (
export const consumeSpecialStylingProp = <P>(
propName: keyof SpecialStylingPropTypes,
propValue: SpecialStylingPropTypes[keyof SpecialStylingPropTypes],
props: Record<string, unknown>
_props: P,
classNames: (string | ReturnType<typeof css>)[]
): void => {
const fn = specialStylingProps[propName];
const style = fn(propValue as never);
......@@ -38,5 +40,5 @@ export const consumeSpecialStylingProp = (
return;
}
props.className = appendClassName(props.className, style);
classNames.push(style);
};
import { css } from '@rocket.chat/css-in-js';
import { appendClassName } from '../../helpers/appendClassName';
import { useStyle } from '../../hooks/useStyle';
import { BoxProps } from './props';
export const useArrayLikeClassNameProp = (
props: BoxProps
): Record<string, unknown> => {
const classNames = ([] as (
| undefined
| string
| ReturnType<typeof css>
)[]).concat(
props.className as
| undefined
| string
| ReturnType<typeof css>
| (undefined | string | ReturnType<typeof css>)[]
);
const cssFns = classNames.filter((value) => typeof value === 'function');
const stylesClassName = useStyle(
css`
${cssFns}
`,
props
);
const strings = classNames.filter((value) => typeof value === 'string');
props.className = strings.reduce(
(className, string) => appendClassName(className, string),
stylesClassName || ''
);
return props;
};
import { createElement } from 'react';
import { ComponentType, createElement, FC } from 'react';
import { consumeBoxStylingProps } from '../components/Box/props';
import { useArrayLikeClassNameProp } from '../components/Box/useArrayLikeClassNameProp';
import { BoxStylingProps, useBoxStylingProps } from '../components/Box/props';
import { useStyleSheet } from '../hooks/useStyleSheet';
export const withBoxStyling = (component) => {
const WithBoxStyling = ({ ...props }) => {
export const withBoxStyling = <P extends { className?: string }>(
component: ComponentType<P>
): ComponentType<
BoxStylingProps & Omit<P, 'className' | keyof BoxStylingProps>
> => {
const WithBoxStyling: FC<
BoxStylingProps & Omit<P, 'className' | keyof BoxStylingProps>
> = (props) => {
useStyleSheet();
props = consumeBoxStylingProps(props);
props = useArrayLikeClassNameProp(props);
const mappedProps = useBoxStylingProps<P>(props);
return createElement(component, props);
return createElement(component, mappedProps);
};
WithBoxStyling.displayName = `WithBoxStyling(${
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment