Skip to content

Commit

Permalink
fix unit conversion issues and other ui improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
madhusudhand committed May 2, 2024
1 parent 736f876 commit dabd706
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 90 deletions.
92 changes: 92 additions & 0 deletions packages/edit-site/src/components/global-styles/shadow-utils.js
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -50,7 +50,8 @@ import {
getShadowParts,
shadowStringToObject,
shadowObjectToString,
} from './utils';
CUSTOM_VALUE_SETTINGS,
} from './shadow-utils';

const { useGlobalSetting } = unlock( blockEditorPrivateApis );

Expand All @@ -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 ) {
Expand Down Expand Up @@ -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 (
<Dropdown
Expand Down Expand Up @@ -281,7 +291,7 @@ function ShadowItem( { shadow, onChange, canRemove, onRemove } ) {
<div className="block-editor-panel-color-gradient-settings__dropdown-content">
<ShadowPopover
shadowObj={ shadowObj }
onChange={ onChange }
onChange={ onShadowChange }
/>
</div>
</DropdownContentWrapper>
Expand All @@ -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 (
Expand Down Expand Up @@ -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 (
<VStack justify={ 'flex-start' }>
Expand All @@ -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"
/>
</HStack>
{ useInput ? (
{ isCustomInput ? (
<UnitControl
label={ label }
hideLabelFromVision
value={ value }
onChange={ onChange }
onChange={ onValueChange }
/>
) : (
<RangeControl
value={ value }
onChange={ onChange }
value={ parsedQuantity ?? 0 }
onChange={ sliderOnChange }
withInputField={ false }
min={ 0 }
max={ 10 }
max={
CUSTOM_VALUE_SETTINGS[ parsedUnit ?? 'px' ]?.max ?? 10
}
step={
CUSTOM_VALUE_SETTINGS[ parsedUnit ?? 'px' ]?.step ?? 0.1
}
/>
) }
</VStack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
import { getShadowParts, shadowStringToObject } from '../utils';
import { getShadowParts, shadowStringToObject } from '../shadow-utils';

const colorFormats = {
named: 'red',
Expand Down
65 changes: 0 additions & 65 deletions packages/edit-site/src/components/global-styles/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit dabd706

Please sign in to comment.