import deepmerge from 'deepmerge'; import { useSelect } from '@wordpress/data'; import { useCallback } from '@wordpress/element'; import { storeName, TypographyProperties } from '../store'; import { useEmailTheme } from './use-email-theme'; interface ElementProperties { typography: TypographyProperties; } export interface StyleProperties { spacing?: { padding: { top: string; right: string; bottom: string; left: string; }; blockGap: string; }; typography?: TypographyProperties; color?: { background: string; text: string; }; elements?: Record< string, ElementProperties >; } interface EmailStylesData { styles: StyleProperties; defaultStyles: StyleProperties; updateStyleProp: ( path, newValue ) => void; updateStyles: ( newStyles: StyleProperties ) => void; } /** * Immutably sets a value inside an object. Like `lodash#set`, but returning a * new object. Treats nullish initial values as empty objects. Clones any * nested objects. Supports arrays, too. * * @param setObject * @param {number|string|Array} setPath Path in the object to modify. * @param {*} value New value to set. * @return {Object} Cloned object with the new value set. */ export function setImmutably( setObject, setPath, value ): typeof setObject { // Normalize path const path = Array.isArray( setPath ) ? [ ...setPath ] : [ setPath ]; // Shallowly clone the base of the object const object = Array.isArray( setObject ) ? [ ...setObject ] : { ...setObject }; const leaf = path.pop(); // Traverse object from root to leaf, shallowly cloning at each level let prev = object; path.forEach( ( key ) => { const lvl = prev[ key ]; prev[ key ] = Array.isArray( lvl ) ? [ ...lvl ] : { ...lvl }; prev = prev[ key ]; } ); prev[ leaf ] = value; return object; } export const useEmailStyles = (): EmailStylesData => { const { templateTheme, updateTemplateTheme } = useEmailTheme(); // This is email level styling stored in post meta. const styles = templateTheme?.styles; // Default styles from theme.json. const { styles: defaultStyles } = useSelect( ( select ) => ( { styles: select( storeName ).getStyles(), } ) ); // Update email styles. const updateStyles = useCallback( ( newStyles ) => { const newTheme = { ...templateTheme, styles: newStyles, }; updateTemplateTheme( newTheme ); }, [ updateTemplateTheme, templateTheme ] ); // Update an email style prop. const updateStyleProp = useCallback( ( path, newValue ) => { const newTheme = setImmutably( templateTheme, [ 'styles', ...path ], newValue ); updateTemplateTheme( newTheme ); }, [ updateTemplateTheme, templateTheme ] ); return { styles: deepmerge.all( [ defaultStyles || {}, styles || {} ] ), defaultStyles, updateStyleProp, updateStyles, }; };