From dabd706a37ae9a4ff6d1dc8cec5fac4efb8f7641 Mon Sep 17 00:00:00 2001 From: madhusudhand Date: Thu, 2 May 2024 17:24:50 +0530 Subject: [PATCH] fix unit conversion issues and other ui improvements --- .../components/global-styles/shadow-utils.js | 92 +++++++++++++++++++ .../global-styles/shadows-edit-panel.js | 77 +++++++++++----- .../{utils.spec.js => shadow-utils.spec.js} | 2 +- .../src/components/global-styles/utils.js | 65 ------------- 4 files changed, 146 insertions(+), 90 deletions(-) create mode 100644 packages/edit-site/src/components/global-styles/shadow-utils.js rename packages/edit-site/src/components/global-styles/test/{utils.spec.js => shadow-utils.spec.js} (98%) diff --git a/packages/edit-site/src/components/global-styles/shadow-utils.js b/packages/edit-site/src/components/global-styles/shadow-utils.js new file mode 100644 index 00000000000000..2af6a05d38a1b5 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/shadow-utils.js @@ -0,0 +1,92 @@ +export const CUSTOM_VALUE_SETTINGS = { + px: { max: 20, step: 1 }, + '%': { max: 100, step: 1 }, + vw: { max: 100, step: 1 }, + vh: { max: 100, step: 1 }, + em: { max: 10, step: 0.1 }, + rm: { max: 10, step: 0.1 }, + svw: { max: 100, step: 1 }, + lvw: { max: 100, step: 1 }, + dvw: { max: 100, step: 1 }, + svh: { max: 100, step: 1 }, + lvh: { max: 100, step: 1 }, + dvh: { max: 100, step: 1 }, + vi: { max: 100, step: 1 }, + svi: { max: 100, step: 1 }, + lvi: { max: 100, step: 1 }, + dvi: { max: 100, step: 1 }, + vb: { max: 100, step: 1 }, + svb: { max: 100, step: 1 }, + lvb: { max: 100, step: 1 }, + dvb: { max: 100, step: 1 }, + vmin: { max: 100, step: 1 }, + svmin: { max: 100, step: 1 }, + lvmin: { max: 100, step: 1 }, + dvmin: { max: 100, step: 1 }, + vmax: { max: 100, step: 1 }, + svmax: { max: 100, step: 1 }, + lvmax: { max: 100, step: 1 }, + dvmax: { max: 100, step: 1 }, +}; + +export function getShadowParts( shadow ) { + const shadowValues = shadow.match( /(?:[^,(]|\([^)]*\))+/g ) || []; + return shadowValues.map( ( value ) => value.trim() ); +} + +export function shadowStringToObject( shadowValue ) { + const defaultShadow = { + x: '0', + y: '0', + blur: '0', + spread: '0', + color: '#000', + inset: false, + }; + + // Step 1: Check for "none" keyword + if ( shadowValue.includes( 'none' ) ) { + return defaultShadow; + } + + // Step 2: Extract length values (x, y, blur, spread) from shadow string + const lengthsRegex = + /(?:^|\s)(-?\d*\.?\d+(?:px|%|in|cm|mm|em|rem|ex|pt|pc|vh|vw|vmin|vmax|ch|lh)?)(?=\s|$)(?![^(]*\))/g; + const matches = shadowValue.match( lengthsRegex ) || []; + const lengths = matches.slice( 0, 4 ); + + // Step 3: Check if there are at least 2 length values (x, y are required for string to be valid shadow) + if ( lengths.length < 2 ) { + return defaultShadow; + } + + // Step 4: Check for `inset` + const inset = shadowValue.includes( 'inset' ); + + // Step 5. Strip lengths and inset from shadow string, leaving just color. + let colorString = shadowValue.replace( lengthsRegex, '' ).trim(); + if ( inset ) { + colorString = colorString.replace( 'inset', '' ).trim(); + } + + // Step 6. Create and return parsed shadow object. + const [ x, y, blur, spread ] = lengths; + return { + x: x?.trim(), + y: y?.trim(), + blur: blur?.trim() || defaultShadow.blur, + spread: spread?.trim() || defaultShadow.spread, + inset, + color: colorString || defaultShadow.color, + }; +} + +export function shadowObjectToString( shadowObj ) { + const shadowString = `${ shadowObj.x || '0px' } ${ shadowObj.y || '0px' } ${ + shadowObj.blur || '0px' + } ${ shadowObj.spread || '0px' }`; + + return `${ shadowObj.inset ? 'inset' : '' } ${ shadowString } ${ + shadowObj.color || '' + }`.trim(); +} diff --git a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js index b2c5c5a59bf742..b79bd18512b713 100644 --- a/packages/edit-site/src/components/global-styles/shadows-edit-panel.js +++ b/packages/edit-site/src/components/global-styles/shadows-edit-panel.js @@ -13,6 +13,7 @@ import { __experimentalItemGroup as ItemGroup, __experimentalHeading as Heading, __experimentalUnitControl as UnitControl, + __experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue, __experimentalGrid as Grid, __experimentalDropdownContentWrapper as DropdownContentWrapper, __experimentalUseNavigator as useNavigator, @@ -36,8 +37,7 @@ import { settings, moreVertical, } from '@wordpress/icons'; -import { useState, useMemo } from '@wordpress/element'; -import { debounce } from '@wordpress/compose'; +import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -50,7 +50,8 @@ import { getShadowParts, shadowStringToObject, shadowObjectToString, -} from './utils'; + CUSTOM_VALUE_SETTINGS, +} from './shadow-utils'; const { useGlobalSetting } = unlock( blockEditorPrivateApis ); @@ -76,16 +77,19 @@ export default function ShadowsEditPanel() { const [ shadows, setShadows ] = useGlobalSetting( `shadow.presets.${ category }` ); - const selectedShadow = useMemo( - () => ( shadows || [] ).find( ( shadow ) => shadow.slug === slug ), - [ shadows, slug ] + const [ selectedShadow, setSelectedShadow ] = useState( () => + ( shadows || [] ).find( ( shadow ) => shadow.slug === slug ) ); - const onShadowChange = debounce( ( shadow ) => { - const updatedShadow = { ...( selectedShadow || {} ), shadow }; - setShadows( - shadows.map( ( s ) => ( s.slug === slug ? updatedShadow : s ) ) + + const onShadowChange = ( shadow ) => { + setSelectedShadow( { ...selectedShadow, shadow } ); + const updatedShadows = shadows.map( ( s ) => + s.slug === slug ? { ...selectedShadow, shadow } : s ); - }, 100 ); + // TODO: this call make the app slow + // may be requestAnimationFrame ?? + setShadows( updatedShadows ); + }; const onMenuClick = ( action ) => { switch ( action ) { @@ -227,7 +231,13 @@ function ShadowItem( { shadow, onChange, canRemove, onRemove } ) { offset: 36, shift: true, }; - const shadowObj = shadowStringToObject( shadow ); + const [ shadowObj, setShadowObj ] = useState( + shadowStringToObject( shadow ) + ); + const onShadowChange = ( newShadow ) => { + setShadowObj( newShadow ); + onChange( shadowObjectToString( newShadow ) ); + }; return ( @@ -303,11 +313,12 @@ function ShadowPopover( { shadowObj, onChange } ) { const enableAlpha = true; const onShadowChange = ( key, value ) => { - onChange( shadowObjectToString( shadow ) ); - setShadow( { + const newShadow = { ...shadow, [ key ]: value, - } ); + }; + setShadow( newShadow ); + onChange( newShadow ); }; return ( @@ -375,7 +386,20 @@ function ShadowPopover( { shadowObj, onChange } ) { } function ShadowInputControl( { label, value, onChange } ) { - const [ useInput, setUseInput ] = useState( false ); + const [ isCustomInput, setIsCustomInput ] = useState( false ); + const [ parsedQuantity, parsedUnit ] = + parseQuantityAndUnitFromRawValue( value ); + + const sliderOnChange = ( next ) => { + onChange( + next !== undefined ? [ next, parsedUnit || 'px' ].join( '' ) : '0px' + ); + }; + const onValueChange = ( next ) => { + const isNumeric = next !== undefined && ! isNaN( parseFloat( next ) ); + const nextValue = isNumeric ? next : '0px'; + onChange( nextValue ); + }; return ( @@ -385,26 +409,31 @@ function ShadowInputControl( { label, value, onChange } ) { label={ __( 'Use custom size' ) } icon={ settings } onClick={ () => { - setUseInput( ! useInput ); + setIsCustomInput( ! isCustomInput ); } } - isPressed={ useInput } + isPressed={ isCustomInput } size="small" /> - { useInput ? ( + { isCustomInput ? ( ) : ( ) } diff --git a/packages/edit-site/src/components/global-styles/test/utils.spec.js b/packages/edit-site/src/components/global-styles/test/shadow-utils.spec.js similarity index 98% rename from packages/edit-site/src/components/global-styles/test/utils.spec.js rename to packages/edit-site/src/components/global-styles/test/shadow-utils.spec.js index 5855da1b2154c4..6f82cc10d53d76 100644 --- a/packages/edit-site/src/components/global-styles/test/utils.spec.js +++ b/packages/edit-site/src/components/global-styles/test/shadow-utils.spec.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getShadowParts, shadowStringToObject } from '../utils'; +import { getShadowParts, shadowStringToObject } from '../shadow-utils'; const colorFormats = { named: 'red', diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js index 4910bb5e23c285..7e7948147b170a 100644 --- a/packages/edit-site/src/components/global-styles/utils.js +++ b/packages/edit-site/src/components/global-styles/utils.js @@ -47,68 +47,3 @@ export function getFontFamilies( themeJson ) { return [ bodyFontFamily, headingFontFamily ]; } - -export function getShadowParts( shadow ) { - const shadowValues = shadow.match( /(?:[^,(]|\([^)]*\))+/g ) || []; - return shadowValues.map( ( value ) => value.trim() ); -} - -export function shadowStringToObject( shadowValue ) { - const defaultShadow = { - x: '0', - y: '0', - blur: '0', - spread: '0', - color: '#000', - inset: false, - }; - - // Step 1: Check for "none" keyword - if ( shadowValue.includes( 'none' ) ) { - return defaultShadow; - } - - // Step 2: Extract length values (x, y, blur, spread) from shadow string - const lengthsRegex = - /(?:^|\s)(-?\d*\.?\d+(?:px|%|in|cm|mm|em|rem|ex|pt|pc|vh|vw|vmin|vmax|ch|lh)?)(?=\s|$)(?![^(]*\))/g; - const matches = shadowValue.match( lengthsRegex ) || []; - const lengths = matches.slice( 0, 4 ); - - // Step 3: Check if there are at least 2 length values (x, y are required for string to be valid shadow) - if ( lengths.length < 2 ) { - return defaultShadow; - } - - // Step 4: Check for `inset` - const inset = shadowValue.includes( 'inset' ); - - // Step 5. Strip lengths and inset from shadow string, leaving just color. - let colorString = shadowValue.replace( lengthsRegex, '' ).trim(); - if ( inset ) { - colorString = colorString.replace( 'inset', '' ).trim(); - } - - // Step 6. Create and return parsed shadow object. - const [ x, y, blur, spread ] = lengths; - return { - x: x?.trim(), - y: y?.trim(), - blur: blur?.trim() || defaultShadow.blur, - spread: spread?.trim() || defaultShadow.spread, - inset, - color: colorString || defaultShadow.color, - }; -} - -export function shadowObjectToString( shadowObj ) { - const shadowString = `${ shadowObj.x || 0 }px ${ shadowObj.y || 0 }px ${ - shadowObj.blur || 0 - }px ${ shadowObj.spread || 0 }px`; - if ( shadowObj.color ) { - return `${ shadowString } ${ shadowObj.color }`; - } - if ( shadowObj.inset ) { - return `inset ${ shadowString }`; - } - return shadowString; -}