Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Style Props Hook
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejolley committed May 2, 2023
1 parent 0b2a3a6 commit 128a8a9
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 3 deletions.
4 changes: 1 addition & 3 deletions assets/js/base/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ export * from './use-position-relative-to-viewport';
export * from './use-previous';
export * from './use-shallow-equal';
export * from './use-throw-error';
export * from './use-spacing-props';
export * from './use-typography-props';
export * from './use-color-props';
export * from './use-border-props';
export * from './use-is-mounted';
export * from './use-spoken-message';
export * from './use-style-props';
74 changes: 74 additions & 0 deletions assets/js/base/hooks/use-style-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { isObject } from '@woocommerce/types';
import { parseStyle } from '@woocommerce/base-utils';

/**
* Internal dependencies
*/
import { useTypographyProps } from './use-typography-props';
import {
getColorClassesAndStyles,
getBorderClassesAndStyles,
getSpacingClassesAndStyles,
WithStyle,
} from '../utils';

type StyleProps = {
className: string;
style: Record< string, unknown >;
};

/**
* Parse the style object saved with blocks and returns the CSS class names and inline styles.
*
* This hook (and its utilities) borrow functionality from the Gutenberg Block Editor package--something we don't want
* to import on the frontend.
*/
export const useStyleProps = ( attributes: unknown ): StyleProps => {
const attributesObject = isObject( attributes ) ? attributes : {};

const typographyProps = useTypographyProps( attributesObject );
const style = parseStyle( attributesObject.style );

const colorProps = getColorClassesAndStyles( {
style,
} as WithStyle );

const borderProps = getBorderClassesAndStyles( {
style,
} as WithStyle );

const spacingProps = getSpacingClassesAndStyles( {
style,
} as WithStyle );

/* TODO
const { borderColor } = attributesObject;
if ( borderColor ) {
const borderColorObject = getMultiOriginColor( {
colors,
namedColor: borderColor,
} );
borderProps.style.borderColor = borderColorObject.color;
}
*/

return {
className: classnames(
typographyProps.className,
colorProps.className,
borderProps.className,
spacingProps.className
),
style: {
...typographyProps.style,
...colorProps.style,
...borderProps.style,
...spacingProps.style,
},
};
};
163 changes: 163 additions & 0 deletions assets/js/base/utils/get-inline-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { kebabCase } from 'lodash';
import { getCSSRules } from '@wordpress/style-engine';
import { isObject } from '@woocommerce/types';

type StyleValue = string | NestedStyle;

interface NestedStyle {
[ key: string ]: StyleValue | undefined;
}

export type WithStyle = {
style: Record< string, NestedStyle > | undefined;
};

/**
* Returns the inline styles to add depending on the style object
*
* @param {Object} styles Styles configuration.
* @return {Object} Flattened CSS variables declaration.
*/
function getInlineStyles( styles = {} ) {
const output = {} as Record< string, unknown >;

getCSSRules( styles, { selector: '' } ).forEach( ( rule ) => {
output[ rule.key ] = rule.value;
} );

return output;
}

/**
* Get the classname for a given color.
*/
function getColorClassName(
colorContextName: string | undefined,
colorSlug: string | undefined
): string {
if ( ! colorContextName || ! colorSlug ) {
return '';
}
return `has-${ kebabCase( colorSlug ) }-${ colorContextName }`;
}

/**
* Generates a CSS class name consisting of all the applicable border color
* classes given the current block attributes.
*/
function getBorderClassName(
attributes: WithStyle & {
borderColor?: string;
}
) {
const { borderColor, style } = attributes;
const borderColorClass = borderColor
? getColorClassName( 'border-color', borderColor )
: '';

return classnames( {
'has-border-color': borderColor || style?.border?.color,
borderColorClass,
} );
}

function getGradientClassName( gradientSlug: string | undefined ) {
if ( ! gradientSlug ) {
return undefined;
}
return `has-${ gradientSlug }-gradient-background`;
}

/**
* Provides the CSS class names and inline styles for a block's color support
* attributes.
*
* @param {Object} attributes Block attributes.
*
* @return {Object} Color block support derived CSS classes & styles.
*/
export function getColorClassesAndStyles(
attributes: WithStyle & {
backgroundColor?: string | undefined;
textColor?: string | undefined;
gradient?: string | undefined;
}
) {
const { backgroundColor, textColor, gradient, style } = attributes;

// Collect color CSS classes.
const backgroundClass = getColorClassName(
'background-color',
backgroundColor
);
const textClass = getColorClassName( 'color', textColor );

const gradientClass = getGradientClassName( gradient );
const hasGradient = gradientClass || style?.color?.gradient;

// Determine color CSS class name list.
const className = classnames( textClass, gradientClass, {
// Don't apply the background class if there's a gradient.
[ backgroundClass ]: ! hasGradient && !! backgroundClass,
'has-text-color': textColor || style?.color?.text,
'has-background':
backgroundColor ||
style?.color?.background ||
gradient ||
style?.color?.gradient,
'has-link-color': isObject( style?.elements?.link )
? style?.elements?.link?.color
: undefined,
} );

// Collect inline styles for colors.
const colorStyles = style?.color || {};
const styleProp = getInlineStyles( { color: colorStyles } );

return {
className: className || undefined,
style: styleProp,
};
}

/**
* Provides the CSS class names and inline styles for a block's border support
* attributes.
*
* @param {Object} attributes Block attributes.
* @return {Object} Border block support derived CSS classes & styles.
*/
export function getBorderClassesAndStyles( attributes: WithStyle ) {
const border = attributes.style?.border || {};
const className = getBorderClassName( attributes );

return {
className: className || undefined,
style: getInlineStyles( { border } ),
};
}

/**
* Provides the CSS class names and inline styles for a block's spacing support
* attributes.
*
* @param {Object} attributes Block attributes.
*
* @return {Object} Spacing block support derived CSS classes & styles.
*/
export function getSpacingClassesAndStyles( attributes: WithStyle ) {
const { style } = attributes;

// Collect inline styles for spacing.
const spacingStyles = style?.spacing || {};
const styleProp = getInlineStyles( { spacing: spacingStyles } );

return {
className: undefined,
style: styleProp,
};
}
1 change: 1 addition & 0 deletions assets/js/base/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * from './camel-case-keys';
export * from './snake-case-keys';
export * from './debounce';
export * from './keyby';
export * from './get-inline-styles';
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const Edit = ( {
cartButtonLabel: content,
} );
} }
style={ blockProps.style }
/>
</div>
);
Expand Down

0 comments on commit 128a8a9

Please sign in to comment.