From fbe4c83f89e64fcfece7c26dedc41def10105a6a Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Fri, 7 Mar 2025 21:08:49 +0800 Subject: [PATCH 001/124] feat: size and spacing steps --- .../advanced-range-control/editor.scss | 25 ++++++ .../advanced-range-control/index.js | 90 ++++++++++++++++++- .../advanced-range-control/range-control.js | 2 + 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/components/advanced-range-control/editor.scss b/src/components/advanced-range-control/editor.scss index cf8dcd26fb..ef56a1ba1d 100644 --- a/src/components/advanced-range-control/editor.scss +++ b/src/components/advanced-range-control/editor.scss @@ -46,4 +46,29 @@ .components-number-control { width: unset; } + + // For displaying marks + .components-range-control__thumb-wrapper { + z-index: 2; + } + .components-range-control__mark { + background: var(--stk-skin-bg-default) !important; + z-index: 1; + transform: translateX(-50%); + width: 3px; + } + .components-range-control__root { + min-height: 35px; + } + .stk-range-control__custom-button { + margin-inline-start: 8px; + top: -1px; + position: relative; + } +} +.stk-range-control--with-marks.stk-range-control--mark-mode .components-range-control__wrapper { + margin-inline-end: 8px; +} +.stk-range-control--with-marks.stk-range-control--mark-mode .components-range-control__wrapper:first-child:last-child { + top: -1px; } diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 07031eeed9..8638507b3a 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -10,6 +10,7 @@ import { useAttributeName, useBlockAttributesContext, useBlockHoverState, + useBlockSetAttributesContext, useDeviceType, } from '~stackable/hooks' @@ -21,7 +22,10 @@ import { isEqual } from 'lodash' /** * WordPress dependencies */ -import { memo } from '@wordpress/element' +import { memo, useState } from '@wordpress/element' +import { Button } from '@wordpress/components' +import { settings } from '@wordpress/icons' +import { dispatch } from '@wordpress/data' const AdvancedRangeControl = props => { const [ value, onChange ] = useControlHandlers( props.attribute, props.responsive, props.hover, props.valueCallback, props.changeCallback ) @@ -30,6 +34,7 @@ const AdvancedRangeControl = props => { const deviceType = useDeviceType() const [ currentHoverState ] = useBlockHoverState() const hasUnits = !! props.units?.length + const setAttributes = useBlockSetAttributesContext() const unitAttrName = useAttributeName( `${ props.attribute }Unit`, props.responsive, props.hover ) const { unitAttribute, @@ -98,12 +103,28 @@ const AdvancedRangeControl = props => { placeholderRender = null } + // Is value at first render the same as a step value? + const isMarkValue = props.marks && true + const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) + // If this supports dynamic content, then the value should be saved as a String. // Important, the attribute type for this option should be a string. const _onChange = value => { const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange let newValue = props.isDynamic ? value.toString() : value + // Support for steps. For steps, the value is an index, but the actual value is in the marks. + if ( newValue !== '' && isMarkMode && props.marks ) { + // Extract the unit and value. + const markValue = props.marks[ value ]?.value || '0' + const [ _newValue, unit ] = extractNumberAndUnit( markValue ) + newValue = _newValue + + // Update the unit. + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: unit } ) + } + // On reset, allow overriding the value. if ( newValue === '' ) { const overrideValue = props.onOverrideReset?.() @@ -121,6 +142,45 @@ const AdvancedRangeControl = props => { onChange: _onChange, } ) + // Support for steps. Modify the props to make the range control show steps. + if ( props.marks && isMarkMode ) { + // Steps only have 1 increment values + propsToPass.min = 0 + propsToPass.min = 0 + propsToPass.max = props.marks.length - 1 + propsToPass.sliderMax = props.marks.length - 1 + propsToPass.step = 1 + + // Show the marks and labels + propsToPass.marks = props.marks.reduce( ( acc, mark, index ) => { + return [ + { + value: index, + label: undefined, + }, + ...acc, + ] + }, [] ) + propsToPass.renderTooltipContent = value => { + return props.marks[ value ]?.label || '' + } + + // Other necessary props for steps. + propsToPass.withInputField = false + } else { + propsToPass.marks = undefined + } + + if ( props.marks ) { + controlProps.className = controlProps.className || '' + controlProps.className += 'stk-range-control--with-marks' + controlProps.className += isMarkMode ? ' stk-range-control--mark-mode' : '' + } + + if ( isMarkMode ) { + controlProps.units = false + } + return ( { onChange={ _onChange } allowReset={ false } placeholderRender={ placeholderRender } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { + // Match the last characters that are not numbers. + const matches = value.match( /([\d.]+)(\D*)$/ ) + if ( ! matches ) { + return [ value, '' ] + } + return [ matches[ 1 ], matches[ 2 ] ] +} diff --git a/src/components/advanced-range-control/range-control.js b/src/components/advanced-range-control/range-control.js index 48ecc75fc4..cf137a20e4 100644 --- a/src/components/advanced-range-control/range-control.js +++ b/src/components/advanced-range-control/range-control.js @@ -43,6 +43,7 @@ const StackableRangeControl = memo( props => { isShiftStepEnabled, placeholderRender, defaultValue: _defaultValue, // Don't pass this. + children: _children, // Don't pass this. ...propsToPass } = props @@ -178,6 +179,7 @@ const StackableRangeControl = memo( props => { type="text" /> ) } + { _children } { allowReset && + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -412,7 +559,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderTop === 'undefined' ? propsToPass.placeholder : props.placeholderTop } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -457,7 +618,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderRight === 'undefined' ? propsToPass.placeholder : props.placeholderRight } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -502,7 +677,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderBottom === 'undefined' ? propsToPass.placeholder : props.placeholderBottom } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -547,7 +736,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderLeft === 'undefined' ? propsToPass.placeholder : props.placeholderLeft } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + Date: Tue, 18 Mar 2025 13:00:36 +0800 Subject: [PATCH 005/124] feat: apply preset controls to fontSize, padding, margin, height --- src/block-components/helpers/size/edit.js | 85 +++++++++++++++++++++++ src/block-components/typography/edit.js | 50 +++++++++++++ 2 files changed, 135 insertions(+) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 6c555fbb03..9b366e08af 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -14,6 +14,77 @@ import { useAttributeEditHandlers, useDeviceType } from '~stackable/hooks' */ import { __ } from '@wordpress/i18n' import { getAttributeNameFunc } from '~stackable/util' +import { useSetting } from '@wordpress/block-editor' + +const HEIGHT_PRESET = [ + { + value: '64px', + label: 'XS', + }, + { + value: '128px', + label: 'S', + }, + { + value: '192px', + label: 'M', + }, + { + value: '256px', + label: 'L', + }, + { + value: '384px', + label: 'XL', + }, + { + value: '512px', + label: '2XL', + }, + { + value: '640px', + label: '3XL', + }, + { + value: '100vh', + label: 'Full', + }, +] + +const SPACING_PRESET = [ + { + label: 'XS', + value: '4px', + }, + { + label: 'S', + value: '8px', + }, + { + label: 'M', + value: '16px', + }, + { + label: 'L', + value: '24px', + }, + { + label: 'XL', + value: '32px', + }, + { + label: '2XL', + value: '48px', + }, + { + label: '3XL', + value: '64px', + }, + { + label: '4XL', + value: '96px', + }, +] const Layout = props => { const deviceType = useDeviceType() @@ -30,6 +101,8 @@ const Layout = props => { labelVerticalAlign = __( 'Content Vertical Align', i18n ), } = props.labels + const heightMarks = HEIGHT_PRESET + return ( <> { props.hasMinHeight && { description: __( 'Adjusts the minimum allowable height of the block', i18n ), } } visualGuide={ props.visualGuide } + marks={ heightMarks } /> } { props.hasContentVerticalAlign && @@ -132,6 +206,15 @@ const Spacing = props => { ...props.visualGuide, highlight: 'margin', } + let spacingMarks = SPACING_PRESET + const spacingSizes = useSetting( 'spacing.spacingSizes' ) + + if ( Array.isArray( spacingSizes ) && spacingSizes.length > 0 ) { + spacingMarks = spacingSizes.map( size => ( { + value: size.size, + label: size.name, + } ) ) + } return ( <> @@ -150,6 +233,7 @@ const Spacing = props => { } } visualGuide={ paddingVisualGuide } placeholder={ props.paddingPlaceholder } + marks={ spacingMarks } /> { props.enableMargin && @@ -167,6 +251,7 @@ const Spacing = props => { description: __( 'Sets the block margin, i.e. the space outside the block between the block border and the next block.', i18n ), } } visualGuide={ marginVisualGuide } + marks={ spacingMarks } /> } diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 5a7642bb8b..72cc386d47 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -30,6 +30,7 @@ import { } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { applyFilters } from '@wordpress/hooks' +import { useSelect } from '@wordpress/data' const TYPOGRAPHY_SHADOWS = [ 'none', @@ -57,6 +58,45 @@ const GRADIENT_OPTIONS = [ }, ] +const SIZE_PRESET = [ + { + value: '0.75rem', + label: 'XS', + }, + { + value: '0.875rem', + label: 'S', + }, + { + value: '1rem', + label: 'M', + }, + { + value: '1.125rem', + label: 'L', + }, + { + value: '1.5rem', + label: 'XL', + }, + { + value: '2rem', + label: '2XL', + }, + { + value: '2.5rem', + label: '3XL', + }, + { + value: '3rem', + label: '4XL', + }, + { + value: '4rem', + label: '5XL', + }, +] + export const Controls = props => { const { hasAlign, @@ -100,6 +140,15 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) + const themeSettings = useSelect( select => select( 'core/block-editor' ).getSettings(), [] ) + let sizeMarks = SIZE_PRESET + if ( Array.isArray( themeSettings?.fontSizes ) && themeSettings.fontSizes.length > 0 ) { + sizeMarks = themeSettings.fontSizes.map( size => ( { + value: size.size, + label: size.name, + } ) ) + } + return ( <> { applyFilters( 'stackable.block-component.typography.before', null, props ) } @@ -268,6 +317,7 @@ export const Controls = props => { title: __( 'Font size', i18n ), description: __( 'Sets the size of text characters', i18n ), } } + marks={ sizeMarks } /> { hasColor && ( From d840726e2ce378106a3e374a0813d86b90d7c578 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 18 Mar 2025 17:04:53 +0800 Subject: [PATCH 006/124] feat: generate global css for presets --- plugin.php | 1 + .../global-settings/preset-controls/index.php | 95 ++++++++++++++++++ .../preset-controls/presets.json | 97 +++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 src/plugins/global-settings/preset-controls/index.php create mode 100644 src/plugins/global-settings/preset-controls/presets.json diff --git a/plugin.php b/plugin.php index dc46b8a181..51efaed859 100644 --- a/plugin.php +++ b/plugin.php @@ -230,6 +230,7 @@ function is_frontend() { require_once( plugin_dir_path( __FILE__ ) . 'src/dynamic-breakpoints.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/design-library/init.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/global-settings.php' ); +require_once( plugin_dir_path( __FILE__ ) . 'src/plugins/global-settings/preset-controls/index.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/custom-block-styles.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/css-optimize.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/compatibility/index.php' ); diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php new file mode 100644 index 0000000000..95bb0515d7 --- /dev/null +++ b/src/plugins/global-settings/preset-controls/index.php @@ -0,0 +1,95 @@ +stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); + $this->theme_presets = $this->load_presets( get_template_directory() . '/theme.json' ); + // Register our settings. + // add_action( 'register_stackable_global_settings', array( $this, 'register_preset_controls' ) ); + + add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); + } + + /** + * Register the settings we need for preset controls + * + * @return void + */ + // public function register_preset_controls() { + + // } + + public function sanitize_array_setting( $input ) { + return ! is_array( $input ) ? array( array() ) : $input; + } + + private function load_presets( $json_path ) { + if ( file_exists( $json_path ) ) { + $json_data = file_get_contents( $json_path ); + $decoded_data = json_decode( $json_data, true ); + return $decoded_data[ 'settings' ] ?? []; + } + return []; + } + + // Generate CSS variables based on preset type (e.g., fontSizes, spacing) + public function generate_css_variables( $presets, $prefix ) { + $css = ""; + foreach ( $presets as $preset ) { + $slug = $preset[ 'slug' ]; + $size = $preset[ 'size' ]; + $css .= "--stk--preset--$prefix--$slug: $size;\n"; + } + + return $css; + } + + /** + * Add our global preset control styles. + * + * @param String $current_css + * @return String + */ + public function add_preset_controls_styles( $current_css ) { + $presets = $this->stackable_presets; + + if ( isset ( $this->theme_presets ) ) { + $presets = $this->theme_presets; + } + + $generated_css = ":root {\n"; + $generated_css .= $this->generate_css_variables( $presets[ 'spacing' ][ 'spacingSizes' ], 'spacing' ); + $generated_css .= $this->generate_css_variables( $presets[ 'typography' ][ 'fontSizes' ], 'font-size' ); + $generated_css .= "}\n"; + + if ( ! $generated_css ) { + return $current_css; + } + + $current_css .= $generated_css; + return apply_filters( 'stackable_frontend_css' , $current_css ); + } + } + + new Stackable_Size_And_Spacing_Preset_Controls(); +} \ No newline at end of file diff --git a/src/plugins/global-settings/preset-controls/presets.json b/src/plugins/global-settings/preset-controls/presets.json new file mode 100644 index 0000000000..2d5a3b737a --- /dev/null +++ b/src/plugins/global-settings/preset-controls/presets.json @@ -0,0 +1,97 @@ +{ + "settings": { + "spacing": { + "spacingSizes": [ + { + "label": "XS", + "size": "0.25rem", + "slug": "x-small" + }, + { + "label": "S", + "size": "0.5rem", + "slug": "small" + }, + { + "label": "M", + "size": "1rem", + "slug": "medium" + }, + { + "label": "L", + "size": "1.5rem", + "slug": "large" + }, + { + "label": "XL", + "size": "2rem", + "slug": "x-large" + }, + { + "label": "2XL", + "size": "3rem", + "slug": "xx-large" + }, + { + "label": "3XL", + "size": "4rem", + "slug": "xxx-large" + }, + { + "label": "4XL", + "size": "6rem", + "slug": "xxxx-large" + } + ] + }, + "typography": { + "fontSizes": [ + { + "label": "XS", + "size": "0.75rem", + "slug": "x-small" + }, + { + "label": "S", + "size": "0.875rem", + "slug": "small" + }, + { + "label": "M", + "size": "1rem", + "slug": "medium" + }, + { + "label": "L", + "size": "1.125rem", + "slug": "large" + }, + { + "label": "XL", + "size": "1.5rem", + "slug": "x-large" + }, + { + "label": "2XL", + "size": "2rem", + "slug": "xx-large" + }, + { + "label": "3XL", + "size": "2.5rem", + "slug": "xxx-large" + }, + { + "label": "4XL", + "size": "3rem", + "slug": "xxxx-large" + }, + { + "label": "5XL", + "size": "4rem", + "slug": "xxxxx-large" + } + ] + } + } +} From 6169719b8ea3332318d2765425556000deabbb5a Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Fri, 7 Mar 2025 21:08:49 +0800 Subject: [PATCH 007/124] feat: size and spacing steps --- .../advanced-range-control/editor.scss | 25 ++++++ .../advanced-range-control/index.js | 90 ++++++++++++++++++- .../advanced-range-control/range-control.js | 2 + 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/components/advanced-range-control/editor.scss b/src/components/advanced-range-control/editor.scss index cf8dcd26fb..ef56a1ba1d 100644 --- a/src/components/advanced-range-control/editor.scss +++ b/src/components/advanced-range-control/editor.scss @@ -46,4 +46,29 @@ .components-number-control { width: unset; } + + // For displaying marks + .components-range-control__thumb-wrapper { + z-index: 2; + } + .components-range-control__mark { + background: var(--stk-skin-bg-default) !important; + z-index: 1; + transform: translateX(-50%); + width: 3px; + } + .components-range-control__root { + min-height: 35px; + } + .stk-range-control__custom-button { + margin-inline-start: 8px; + top: -1px; + position: relative; + } +} +.stk-range-control--with-marks.stk-range-control--mark-mode .components-range-control__wrapper { + margin-inline-end: 8px; +} +.stk-range-control--with-marks.stk-range-control--mark-mode .components-range-control__wrapper:first-child:last-child { + top: -1px; } diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 07031eeed9..8638507b3a 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -10,6 +10,7 @@ import { useAttributeName, useBlockAttributesContext, useBlockHoverState, + useBlockSetAttributesContext, useDeviceType, } from '~stackable/hooks' @@ -21,7 +22,10 @@ import { isEqual } from 'lodash' /** * WordPress dependencies */ -import { memo } from '@wordpress/element' +import { memo, useState } from '@wordpress/element' +import { Button } from '@wordpress/components' +import { settings } from '@wordpress/icons' +import { dispatch } from '@wordpress/data' const AdvancedRangeControl = props => { const [ value, onChange ] = useControlHandlers( props.attribute, props.responsive, props.hover, props.valueCallback, props.changeCallback ) @@ -30,6 +34,7 @@ const AdvancedRangeControl = props => { const deviceType = useDeviceType() const [ currentHoverState ] = useBlockHoverState() const hasUnits = !! props.units?.length + const setAttributes = useBlockSetAttributesContext() const unitAttrName = useAttributeName( `${ props.attribute }Unit`, props.responsive, props.hover ) const { unitAttribute, @@ -98,12 +103,28 @@ const AdvancedRangeControl = props => { placeholderRender = null } + // Is value at first render the same as a step value? + const isMarkValue = props.marks && true + const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) + // If this supports dynamic content, then the value should be saved as a String. // Important, the attribute type for this option should be a string. const _onChange = value => { const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange let newValue = props.isDynamic ? value.toString() : value + // Support for steps. For steps, the value is an index, but the actual value is in the marks. + if ( newValue !== '' && isMarkMode && props.marks ) { + // Extract the unit and value. + const markValue = props.marks[ value ]?.value || '0' + const [ _newValue, unit ] = extractNumberAndUnit( markValue ) + newValue = _newValue + + // Update the unit. + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: unit } ) + } + // On reset, allow overriding the value. if ( newValue === '' ) { const overrideValue = props.onOverrideReset?.() @@ -121,6 +142,45 @@ const AdvancedRangeControl = props => { onChange: _onChange, } ) + // Support for steps. Modify the props to make the range control show steps. + if ( props.marks && isMarkMode ) { + // Steps only have 1 increment values + propsToPass.min = 0 + propsToPass.min = 0 + propsToPass.max = props.marks.length - 1 + propsToPass.sliderMax = props.marks.length - 1 + propsToPass.step = 1 + + // Show the marks and labels + propsToPass.marks = props.marks.reduce( ( acc, mark, index ) => { + return [ + { + value: index, + label: undefined, + }, + ...acc, + ] + }, [] ) + propsToPass.renderTooltipContent = value => { + return props.marks[ value ]?.label || '' + } + + // Other necessary props for steps. + propsToPass.withInputField = false + } else { + propsToPass.marks = undefined + } + + if ( props.marks ) { + controlProps.className = controlProps.className || '' + controlProps.className += 'stk-range-control--with-marks' + controlProps.className += isMarkMode ? ' stk-range-control--mark-mode' : '' + } + + if ( isMarkMode ) { + controlProps.units = false + } + return ( { onChange={ _onChange } allowReset={ false } placeholderRender={ placeholderRender } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { + // Match the last characters that are not numbers. + const matches = value.match( /([\d.]+)(\D*)$/ ) + if ( ! matches ) { + return [ value, '' ] + } + return [ matches[ 1 ], matches[ 2 ] ] +} diff --git a/src/components/advanced-range-control/range-control.js b/src/components/advanced-range-control/range-control.js index 48ecc75fc4..cf137a20e4 100644 --- a/src/components/advanced-range-control/range-control.js +++ b/src/components/advanced-range-control/range-control.js @@ -43,6 +43,7 @@ const StackableRangeControl = memo( props => { isShiftStepEnabled, placeholderRender, defaultValue: _defaultValue, // Don't pass this. + children: _children, // Don't pass this. ...propsToPass } = props @@ -178,6 +179,7 @@ const StackableRangeControl = memo( props => { type="text" /> ) } + { _children } { allowReset && + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -414,7 +561,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderTop === 'undefined' ? propsToPass.placeholder : props.placeholderTop } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -459,7 +620,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderRight === 'undefined' ? propsToPass.placeholder : props.placeholderRight } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -504,7 +679,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderBottom === 'undefined' ? propsToPass.placeholder : props.placeholderBottom } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { props.isCorner ? : } { if ( currentHoverState !== 'normal' ) { @@ -549,7 +738,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderLeft === 'undefined' ? propsToPass.placeholder : props.placeholderLeft } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + Date: Tue, 18 Mar 2025 13:00:36 +0800 Subject: [PATCH 011/124] feat: apply preset controls to fontSize, padding, margin, height --- src/block-components/helpers/size/edit.js | 85 +++++++++++++++++++++++ src/block-components/typography/edit.js | 50 +++++++++++++ 2 files changed, 135 insertions(+) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 6c555fbb03..9b366e08af 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -14,6 +14,77 @@ import { useAttributeEditHandlers, useDeviceType } from '~stackable/hooks' */ import { __ } from '@wordpress/i18n' import { getAttributeNameFunc } from '~stackable/util' +import { useSetting } from '@wordpress/block-editor' + +const HEIGHT_PRESET = [ + { + value: '64px', + label: 'XS', + }, + { + value: '128px', + label: 'S', + }, + { + value: '192px', + label: 'M', + }, + { + value: '256px', + label: 'L', + }, + { + value: '384px', + label: 'XL', + }, + { + value: '512px', + label: '2XL', + }, + { + value: '640px', + label: '3XL', + }, + { + value: '100vh', + label: 'Full', + }, +] + +const SPACING_PRESET = [ + { + label: 'XS', + value: '4px', + }, + { + label: 'S', + value: '8px', + }, + { + label: 'M', + value: '16px', + }, + { + label: 'L', + value: '24px', + }, + { + label: 'XL', + value: '32px', + }, + { + label: '2XL', + value: '48px', + }, + { + label: '3XL', + value: '64px', + }, + { + label: '4XL', + value: '96px', + }, +] const Layout = props => { const deviceType = useDeviceType() @@ -30,6 +101,8 @@ const Layout = props => { labelVerticalAlign = __( 'Content Vertical Align', i18n ), } = props.labels + const heightMarks = HEIGHT_PRESET + return ( <> { props.hasMinHeight && { description: __( 'Adjusts the minimum allowable height of the block', i18n ), } } visualGuide={ props.visualGuide } + marks={ heightMarks } /> } { props.hasContentVerticalAlign && @@ -132,6 +206,15 @@ const Spacing = props => { ...props.visualGuide, highlight: 'margin', } + let spacingMarks = SPACING_PRESET + const spacingSizes = useSetting( 'spacing.spacingSizes' ) + + if ( Array.isArray( spacingSizes ) && spacingSizes.length > 0 ) { + spacingMarks = spacingSizes.map( size => ( { + value: size.size, + label: size.name, + } ) ) + } return ( <> @@ -150,6 +233,7 @@ const Spacing = props => { } } visualGuide={ paddingVisualGuide } placeholder={ props.paddingPlaceholder } + marks={ spacingMarks } /> { props.enableMargin && @@ -167,6 +251,7 @@ const Spacing = props => { description: __( 'Sets the block margin, i.e. the space outside the block between the block border and the next block.', i18n ), } } visualGuide={ marginVisualGuide } + marks={ spacingMarks } /> } diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 9c10a370bd..484b89ee99 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -30,6 +30,7 @@ import { } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { applyFilters } from '@wordpress/hooks' +import { useSelect } from '@wordpress/data' const TYPOGRAPHY_SHADOWS = [ 'none', @@ -57,6 +58,45 @@ const GRADIENT_OPTIONS = [ }, ] +const SIZE_PRESET = [ + { + value: '0.75rem', + label: 'XS', + }, + { + value: '0.875rem', + label: 'S', + }, + { + value: '1rem', + label: 'M', + }, + { + value: '1.125rem', + label: 'L', + }, + { + value: '1.5rem', + label: 'XL', + }, + { + value: '2rem', + label: '2XL', + }, + { + value: '2.5rem', + label: '3XL', + }, + { + value: '3rem', + label: '4XL', + }, + { + value: '4rem', + label: '5XL', + }, +] + export const Controls = props => { const { hasAlign, @@ -100,6 +140,15 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) + const themeSettings = useSelect( select => select( 'core/block-editor' ).getSettings(), [] ) + let sizeMarks = SIZE_PRESET + if ( Array.isArray( themeSettings?.fontSizes ) && themeSettings.fontSizes.length > 0 ) { + sizeMarks = themeSettings.fontSizes.map( size => ( { + value: size.size, + label: size.name, + } ) ) + } + return ( <> { applyFilters( 'stackable.block-component.typography.before', null, props ) } @@ -269,6 +318,7 @@ export const Controls = props => { title: __( 'Font size', i18n ), description: __( 'Sets the size of text characters', i18n ), } } + marks={ sizeMarks } /> { hasColor && ( From 74eedd5157e6f7129b1bb23fae7b0a95d368cb13 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 18 Mar 2025 17:04:53 +0800 Subject: [PATCH 012/124] feat: generate global css for presets --- plugin.php | 1 + .../global-settings/preset-controls/index.php | 95 ++++++++++++++++++ .../preset-controls/presets.json | 97 +++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 src/plugins/global-settings/preset-controls/index.php create mode 100644 src/plugins/global-settings/preset-controls/presets.json diff --git a/plugin.php b/plugin.php index a98a074b94..003614c40f 100644 --- a/plugin.php +++ b/plugin.php @@ -233,6 +233,7 @@ function is_frontend() { require_once( plugin_dir_path( __FILE__ ) . 'src/global-settings.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/plugins/global-settings/spacing-and-borders/index.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/plugins/global-settings/buttons-and-icons/index.php' ); +require_once( plugin_dir_path( __FILE__ ) . 'src/plugins/global-settings/preset-controls/index.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/custom-block-styles.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/css-optimize.php' ); require_once( plugin_dir_path( __FILE__ ) . 'src/compatibility/index.php' ); diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php new file mode 100644 index 0000000000..95bb0515d7 --- /dev/null +++ b/src/plugins/global-settings/preset-controls/index.php @@ -0,0 +1,95 @@ +stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); + $this->theme_presets = $this->load_presets( get_template_directory() . '/theme.json' ); + // Register our settings. + // add_action( 'register_stackable_global_settings', array( $this, 'register_preset_controls' ) ); + + add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); + } + + /** + * Register the settings we need for preset controls + * + * @return void + */ + // public function register_preset_controls() { + + // } + + public function sanitize_array_setting( $input ) { + return ! is_array( $input ) ? array( array() ) : $input; + } + + private function load_presets( $json_path ) { + if ( file_exists( $json_path ) ) { + $json_data = file_get_contents( $json_path ); + $decoded_data = json_decode( $json_data, true ); + return $decoded_data[ 'settings' ] ?? []; + } + return []; + } + + // Generate CSS variables based on preset type (e.g., fontSizes, spacing) + public function generate_css_variables( $presets, $prefix ) { + $css = ""; + foreach ( $presets as $preset ) { + $slug = $preset[ 'slug' ]; + $size = $preset[ 'size' ]; + $css .= "--stk--preset--$prefix--$slug: $size;\n"; + } + + return $css; + } + + /** + * Add our global preset control styles. + * + * @param String $current_css + * @return String + */ + public function add_preset_controls_styles( $current_css ) { + $presets = $this->stackable_presets; + + if ( isset ( $this->theme_presets ) ) { + $presets = $this->theme_presets; + } + + $generated_css = ":root {\n"; + $generated_css .= $this->generate_css_variables( $presets[ 'spacing' ][ 'spacingSizes' ], 'spacing' ); + $generated_css .= $this->generate_css_variables( $presets[ 'typography' ][ 'fontSizes' ], 'font-size' ); + $generated_css .= "}\n"; + + if ( ! $generated_css ) { + return $current_css; + } + + $current_css .= $generated_css; + return apply_filters( 'stackable_frontend_css' , $current_css ); + } + } + + new Stackable_Size_And_Spacing_Preset_Controls(); +} \ No newline at end of file diff --git a/src/plugins/global-settings/preset-controls/presets.json b/src/plugins/global-settings/preset-controls/presets.json new file mode 100644 index 0000000000..2d5a3b737a --- /dev/null +++ b/src/plugins/global-settings/preset-controls/presets.json @@ -0,0 +1,97 @@ +{ + "settings": { + "spacing": { + "spacingSizes": [ + { + "label": "XS", + "size": "0.25rem", + "slug": "x-small" + }, + { + "label": "S", + "size": "0.5rem", + "slug": "small" + }, + { + "label": "M", + "size": "1rem", + "slug": "medium" + }, + { + "label": "L", + "size": "1.5rem", + "slug": "large" + }, + { + "label": "XL", + "size": "2rem", + "slug": "x-large" + }, + { + "label": "2XL", + "size": "3rem", + "slug": "xx-large" + }, + { + "label": "3XL", + "size": "4rem", + "slug": "xxx-large" + }, + { + "label": "4XL", + "size": "6rem", + "slug": "xxxx-large" + } + ] + }, + "typography": { + "fontSizes": [ + { + "label": "XS", + "size": "0.75rem", + "slug": "x-small" + }, + { + "label": "S", + "size": "0.875rem", + "slug": "small" + }, + { + "label": "M", + "size": "1rem", + "slug": "medium" + }, + { + "label": "L", + "size": "1.125rem", + "slug": "large" + }, + { + "label": "XL", + "size": "1.5rem", + "slug": "x-large" + }, + { + "label": "2XL", + "size": "2rem", + "slug": "xx-large" + }, + { + "label": "3XL", + "size": "2.5rem", + "slug": "xxx-large" + }, + { + "label": "4XL", + "size": "3rem", + "slug": "xxxx-large" + }, + { + "label": "5XL", + "size": "4rem", + "slug": "xxxxx-large" + } + ] + } + } +} From 9c366009ada045dd4da39c5f8bed35f34f873b4e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 20 Mar 2025 10:10:34 +0800 Subject: [PATCH 013/124] fix: apply generated css --- src/block-components/helpers/size/edit.js | 53 +++---------------- src/block-components/typography/edit.js | 53 ++----------------- .../advanced-range-control/index.js | 2 +- src/components/block-css/index.js | 6 ++- src/components/four-range-control/index.js | 2 +- src/hooks/index.js | 1 + src/hooks/use-preset-controls.js | 23 ++++++++ .../global-settings/preset-controls/index.php | 11 ++-- .../preset-controls/presets.json | 34 ++++++------ src/util/styles/style-object.js | 6 ++- 10 files changed, 67 insertions(+), 124 deletions(-) create mode 100644 src/hooks/use-preset-controls.js diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 9b366e08af..0c5e727718 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -7,14 +7,15 @@ import { AdvancedRangeControl, FourRangeControl, } from '~stackable/components' -import { useAttributeEditHandlers, useDeviceType } from '~stackable/hooks' +import { + useAttributeEditHandlers, useDeviceType, usePresetControls, +} from '~stackable/hooks' /** * WordPress dependencies */ import { __ } from '@wordpress/i18n' import { getAttributeNameFunc } from '~stackable/util' -import { useSetting } from '@wordpress/block-editor' const HEIGHT_PRESET = [ { @@ -51,41 +52,6 @@ const HEIGHT_PRESET = [ }, ] -const SPACING_PRESET = [ - { - label: 'XS', - value: '4px', - }, - { - label: 'S', - value: '8px', - }, - { - label: 'M', - value: '16px', - }, - { - label: 'L', - value: '24px', - }, - { - label: 'XL', - value: '32px', - }, - { - label: '2XL', - value: '48px', - }, - { - label: '3XL', - value: '64px', - }, - { - label: '4XL', - value: '96px', - }, -] - const Layout = props => { const deviceType = useDeviceType() @@ -206,15 +172,8 @@ const Spacing = props => { ...props.visualGuide, highlight: 'margin', } - let spacingMarks = SPACING_PRESET - const spacingSizes = useSetting( 'spacing.spacingSizes' ) - if ( Array.isArray( spacingSizes ) && spacingSizes.length > 0 ) { - spacingMarks = spacingSizes.map( size => ( { - value: size.size, - label: size.name, - } ) ) - } + const presetMarks = usePresetControls( [ 'spacing', 'spacingSizes' ] ) return ( <> @@ -233,7 +192,7 @@ const Spacing = props => { } } visualGuide={ paddingVisualGuide } placeholder={ props.paddingPlaceholder } - marks={ spacingMarks } + marks={ presetMarks } /> { props.enableMargin && @@ -251,7 +210,7 @@ const Spacing = props => { description: __( 'Sets the block margin, i.e. the space outside the block between the block border and the next block.', i18n ), } } visualGuide={ marginVisualGuide } - marks={ spacingMarks } + marks={ presetMarks } /> } diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 484b89ee99..131b077b63 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -3,7 +3,7 @@ */ import { unescape } from 'lodash' import { i18n } from 'stackable' -import { useAttributeEditHandlers } from '~stackable/hooks' +import { useAttributeEditHandlers, usePresetControls } from '~stackable/hooks' import { AlignButtonsControl, AdvancedRangeControl, @@ -30,7 +30,6 @@ import { } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { applyFilters } from '@wordpress/hooks' -import { useSelect } from '@wordpress/data' const TYPOGRAPHY_SHADOWS = [ 'none', @@ -58,45 +57,6 @@ const GRADIENT_OPTIONS = [ }, ] -const SIZE_PRESET = [ - { - value: '0.75rem', - label: 'XS', - }, - { - value: '0.875rem', - label: 'S', - }, - { - value: '1rem', - label: 'M', - }, - { - value: '1.125rem', - label: 'L', - }, - { - value: '1.5rem', - label: 'XL', - }, - { - value: '2rem', - label: '2XL', - }, - { - value: '2.5rem', - label: '3XL', - }, - { - value: '3rem', - label: '4XL', - }, - { - value: '4rem', - label: '5XL', - }, -] - export const Controls = props => { const { hasAlign, @@ -140,14 +100,7 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) - const themeSettings = useSelect( select => select( 'core/block-editor' ).getSettings(), [] ) - let sizeMarks = SIZE_PRESET - if ( Array.isArray( themeSettings?.fontSizes ) && themeSettings.fontSizes.length > 0 ) { - sizeMarks = themeSettings.fontSizes.map( size => ( { - value: size.size, - label: size.name, - } ) ) - } + const presetMarks = usePresetControls( [ 'typography', 'fontSizes' ] ) return ( <> @@ -318,7 +271,7 @@ export const Controls = props => { title: __( 'Font size', i18n ), description: __( 'Sets the size of text characters', i18n ), } } - marks={ sizeMarks } + marks={ presetMarks } /> { hasColor && ( diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index aea49a695e..8a9b990dab 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -259,7 +259,7 @@ export default memo( AdvancedRangeControl, isEqual ) const extractNumberAndUnit = value => { // Match the last characters that are not numbers. const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches ) { + if ( ! matches || value.startsWith( 'var(--stk' ) ) { return [ value, '' ] } return [ matches[ 1 ], matches[ 2 ] ] diff --git a/src/components/block-css/index.js b/src/components/block-css/index.js index 53d6b4be46..232b687cef 100644 --- a/src/components/block-css/index.js +++ b/src/components/block-css/index.js @@ -182,8 +182,10 @@ const BlockCss = props => { } if ( unit ) { // Note: this will only work for non-objects. - // If the value is `auto`, don't add units. - value = value === 'auto' ? value : `${ value }${ unit }` + // If the value is `auto` or a CSS variable, don't add units. + if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var(--stk' ) ) ) ) { + value = `${ value }${ unit }` + } } if ( format !== '%s' && format !== '' ) { value = sprintf( diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 97ba67113a..ffd1ed4e01 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -59,7 +59,7 @@ const isEqualInitial = ( props, value, firstValue ) => { const extractNumberAndUnit = value => { // Match the last characters that are not numbers. const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches ) { + if ( ! matches || value.startsWith( 'var(--stk' ) ) { return [ value, '' ] } return [ matches[ 1 ], matches[ 2 ] ] diff --git a/src/hooks/index.js b/src/hooks/index.js index c81e9991ed..25eb202a20 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -19,3 +19,4 @@ export * from './use-raf-state' export * from './use-raf-memo' export * from './use-raf-effect' export * from './use-global-block-layout-defaults' +export * from './use-preset-controls' diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js new file mode 100644 index 0000000000..2c46c467d0 --- /dev/null +++ b/src/hooks/use-preset-controls.js @@ -0,0 +1,23 @@ +import { useSettings } from '@wordpress/block-editor' +import DEFAULT_PRESETS from '~stackable/plugins/global-settings/preset-controls/presets.json' + +const PRESET_PREFIX = { + 'spacing.spacingSizes': 'spacing-size', + 'typography.fontSizes': 'font-size', +} + +export const usePresetControls = properties => { + const property = properties.join( '.' ) + const [ themePreset ] = useSettings( property ) + + const presets = Array.isArray( themePreset ) && themePreset.length > 0 + ? themePreset + : properties.reduce( ( acc, key ) => acc?.[ key ], DEFAULT_PRESETS.settings ) + + const prefix = PRESET_PREFIX[ property ] + + return presets.map( preset => ( { + label: preset.name, + value: `var(--stk--preset--${ prefix }--${ preset.slug }, ${ preset.size })`, + } ) ) +} diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 95bb0515d7..a2c71376e9 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -23,11 +23,12 @@ class Stackable_Size_And_Spacing_Preset_Controls { */ function __construct() { $this->stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); - $this->theme_presets = $this->load_presets( get_template_directory() . '/theme.json' ); + $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); // Register our settings. // add_action( 'register_stackable_global_settings', array( $this, 'register_preset_controls' ) ); add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); + add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); } /** @@ -73,12 +74,13 @@ public function generate_css_variables( $presets, $prefix ) { public function add_preset_controls_styles( $current_css ) { $presets = $this->stackable_presets; - if ( isset ( $this->theme_presets ) ) { - $presets = $this->theme_presets; + if ( isset( $this->theme_presets ) ) { + $presets[ 'spacing' ][ 'spacingSizes' ] = $this->theme_presets[ 'spacing' ][ 'spacingSizes' ][ 'theme' ] ?? $presets[ 'spacing' ][ 'spacingSizes' ]; + $presets[ 'typography' ][ 'fontSizes' ] = $this->theme_presets[ 'typography' ][ 'fontSizes' ][ 'theme' ] ?? $presets[ 'typography' ][ 'fontSizes' ]; } $generated_css = ":root {\n"; - $generated_css .= $this->generate_css_variables( $presets[ 'spacing' ][ 'spacingSizes' ], 'spacing' ); + $generated_css .= $this->generate_css_variables( $presets[ 'spacing' ][ 'spacingSizes' ], 'spacing-size' ); $generated_css .= $this->generate_css_variables( $presets[ 'typography' ][ 'fontSizes' ], 'font-size' ); $generated_css .= "}\n"; @@ -87,6 +89,7 @@ public function add_preset_controls_styles( $current_css ) { } $current_css .= $generated_css; + return apply_filters( 'stackable_frontend_css' , $current_css ); } } diff --git a/src/plugins/global-settings/preset-controls/presets.json b/src/plugins/global-settings/preset-controls/presets.json index 2d5a3b737a..553bcc1328 100644 --- a/src/plugins/global-settings/preset-controls/presets.json +++ b/src/plugins/global-settings/preset-controls/presets.json @@ -3,42 +3,42 @@ "spacing": { "spacingSizes": [ { - "label": "XS", + "name": "XS", "size": "0.25rem", "slug": "x-small" }, { - "label": "S", + "name": "S", "size": "0.5rem", "slug": "small" }, { - "label": "M", + "name": "M", "size": "1rem", "slug": "medium" }, { - "label": "L", + "name": "L", "size": "1.5rem", "slug": "large" }, { - "label": "XL", + "name": "XL", "size": "2rem", "slug": "x-large" }, { - "label": "2XL", + "name": "2XL", "size": "3rem", "slug": "xx-large" }, { - "label": "3XL", + "name": "3XL", "size": "4rem", "slug": "xxx-large" }, { - "label": "4XL", + "name": "4XL", "size": "6rem", "slug": "xxxx-large" } @@ -47,47 +47,47 @@ "typography": { "fontSizes": [ { - "label": "XS", + "name": "XS", "size": "0.75rem", "slug": "x-small" }, { - "label": "S", + "name": "S", "size": "0.875rem", "slug": "small" }, { - "label": "M", + "name": "M", "size": "1rem", "slug": "medium" }, { - "label": "L", + "name": "L", "size": "1.125rem", "slug": "large" }, { - "label": "XL", + "name": "XL", "size": "1.5rem", "slug": "x-large" }, { - "label": "2XL", + "name": "2XL", "size": "2rem", "slug": "xx-large" }, { - "label": "3XL", + "name": "3XL", "size": "2.5rem", "slug": "xxx-large" }, { - "label": "4XL", + "name": "4XL", "size": "3rem", "slug": "xxxx-large" }, { - "label": "5XL", + "name": "5XL", "size": "4rem", "slug": "xxxxx-large" } diff --git a/src/util/styles/style-object.js b/src/util/styles/style-object.js index 53b1eaaf8e..28c18f445d 100644 --- a/src/util/styles/style-object.js +++ b/src/util/styles/style-object.js @@ -335,8 +335,10 @@ class StyleObject { } if ( unit ) { // Note: this will only work for non-objects. - // If the value is `auto`, don't add units. - value = value === 'auto' ? value : `${ value }${ unit }` + // If the value is `auto` or a CSS variable, don't add units. + if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var(--stk' ) ) ) ) { + value = `${ value }${ unit }` + } } if ( format !== '%s' && format !== '' ) { value = sprintf( From 787a54afedd007cd566ec8c6048f343488cc9f80 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 23 Mar 2025 20:39:24 +0800 Subject: [PATCH 014/124] fix: make fontSizes string to store CSS variables --- src/block-components/typography/attributes.js | 2 +- src/components/advanced-range-control/index.js | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/block-components/typography/attributes.js b/src/block-components/typography/attributes.js index 9013f62d4d..9e8c87512f 100644 --- a/src/block-components/typography/attributes.js +++ b/src/block-components/typography/attributes.js @@ -3,7 +3,7 @@ import { deprecatedAddAttributes } from './deprecated' const typographyAttributes = { fontSize: { stkResponsive: true, - type: 'number', + type: 'string', default: '', stkUnits: 'px', }, diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 8a9b990dab..ff2277433d 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -113,11 +113,11 @@ const AdvancedRangeControl = props => { } const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) - // If this supports dynamic content, then the value should be saved as a String. + // If this supports dynamic content and can have CSS variables, the value should be saved as a String. // Important, the attribute type for this option should be a string. const _onChange = value => { const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange - let newValue = props.isDynamic ? value.toString() : value + let newValue = props.isDynamic || props.hasCSSVariableValue ? value.toString() : value // On reset, allow overriding the value. if ( newValue === '' ) { @@ -172,7 +172,7 @@ const AdvancedRangeControl = props => { } // We need to change the way we handle the value and onChange if we're doing marks - let rangeValue = propsToPass.isDynamic ? parseFloat( derivedValue ) : derivedValue + let rangeValue = propsToPass.isDynamic || props.hasCSSVariableValue ? parseFloat( derivedValue ) : derivedValue let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { @@ -190,8 +190,10 @@ const AdvancedRangeControl = props => { const newValue = _newValue // Update the unit. - dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() - setAttributes( { [ unitAttrName ]: unit } ) + if ( unit ) { + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: unit } ) + } _onChange( newValue ) } @@ -250,6 +252,7 @@ AdvancedRangeControl.defaultProps = { marks: undefined, // [{ value: '14px', label: 'S' }, { value: '16px', label: 'M' }] allowCustom: true, + hasCSSVariableValue: false, } export default memo( AdvancedRangeControl, isEqual ) From 8254dd1c6c6bbc12fc25d858fae5b336b07eb5cc Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 23 Mar 2025 22:31:59 +0800 Subject: [PATCH 015/124] fix: add default to prioritization --- .../global-settings/preset-controls/index.php | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index a2c71376e9..769effb621 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -15,17 +15,17 @@ */ class Stackable_Size_And_Spacing_Preset_Controls { - public $stackable_presets; public $theme_presets; + public $default_presets; + public $stackable_presets; /** * Initialize */ function __construct() { - $this->stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); - // Register our settings. - // add_action( 'register_stackable_global_settings', array( $this, 'register_preset_controls' ) ); + $this->default_presets = WP_Theme_JSON_Resolver::get_core_data()->get_settings(); + $this->stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); @@ -53,7 +53,13 @@ private function load_presets( $json_path ) { return []; } - // Generate CSS variables based on preset type (e.g., fontSizes, spacing) + /** + * Generate CSS variables based on preset type (e.g., fontSizes, spacing) + * + * @param array $presests + * @param array $prefix + * @return mixed + */ public function generate_css_variables( $presets, $prefix ) { $css = ""; foreach ( $presets as $preset ) { @@ -65,6 +71,17 @@ public function generate_css_variables( $presets, $prefix ) { return $css; } + /** + * Get the value from an array deeply with an array of keys + * + * @param array $array + * @param array $keys + * @return mixed + */ + public function deepGet( $array, $keys ) { + return array_reduce( $keys, fn( $value, $key ) => $value[ $key ] ?? null, $array ); + } + /** * Add our global preset control styles. * @@ -72,16 +89,32 @@ public function generate_css_variables( $presets, $prefix ) { * @return String */ public function add_preset_controls_styles( $current_css ) { - $presets = $this->stackable_presets; - - if ( isset( $this->theme_presets ) ) { - $presets[ 'spacing' ][ 'spacingSizes' ] = $this->theme_presets[ 'spacing' ][ 'spacingSizes' ][ 'theme' ] ?? $presets[ 'spacing' ][ 'spacingSizes' ]; - $presets[ 'typography' ][ 'fontSizes' ] = $this->theme_presets[ 'typography' ][ 'fontSizes' ][ 'theme' ] ?? $presets[ 'typography' ][ 'fontSizes' ]; - } - + $preset_keys = array( + 'spacing-size' => array( 'spacing', 'spacingSizes' ), + 'font-size' => array( 'typography', 'fontSizes' ), + ); + $generated_css = ":root {\n"; - $generated_css .= $this->generate_css_variables( $presets[ 'spacing' ][ 'spacingSizes' ], 'spacing-size' ); - $generated_css .= $this->generate_css_variables( $presets[ 'typography' ][ 'fontSizes' ], 'font-size' ); + + foreach ( $preset_keys as $key => $value ) { + if ( ! empty( $this->deepGet( $this->theme_presets, $value )[ 'theme' ] ) ) { + $generated_css .= $this->generate_css_variables( + $this->deepGet( $this->theme_presets, $value )[ 'theme' ], + $key, + ); + } elseif ( ! empty( $this->deepGet( $this->default_presets, $value )[ 'default' ] ) ) { + $generated_css .= $this->generate_css_variables( + $this->deepGet( $this->default_presets, $value )[ 'default' ], + $key, + ); + } else { + $generated_css .= $this->generate_css_variables( + $this->deepGet( $this->stackable_presets, $value ), + $key, + ); + } + } + $generated_css .= "}\n"; if ( ! $generated_css ) { From cf47f720b8505be83005038412209318637db35e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 25 Mar 2025 11:59:14 +0800 Subject: [PATCH 016/124] feat: support for custom presets --- src/block-components/helpers/size/edit.js | 2 +- src/block-components/typography/edit.js | 2 +- src/hooks/use-preset-controls.js | 49 ++++++++++++++----- .../global-settings/preset-controls/index.php | 22 ++++----- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 0c5e727718..eeb1ca9a31 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -173,7 +173,7 @@ const Spacing = props => { highlight: 'margin', } - const presetMarks = usePresetControls( [ 'spacing', 'spacingSizes' ] ) + const presetMarks = usePresetControls( 'spacingSizes' ) return ( <> diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 131b077b63..946aa68f4e 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -100,7 +100,7 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) - const presetMarks = usePresetControls( [ 'typography', 'fontSizes' ] ) + const presetMarks = usePresetControls( 'fontSizes' ) return ( <> diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index 2c46c467d0..dfcee10c66 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -1,23 +1,50 @@ import { useSettings } from '@wordpress/block-editor' +import { useSelect } from '@wordpress/data' import DEFAULT_PRESETS from '~stackable/plugins/global-settings/preset-controls/presets.json' -const PRESET_PREFIX = { - 'spacing.spacingSizes': 'spacing-size', - 'typography.fontSizes': 'font-size', +const PRESET_MAPPING = { + fontSizes: { + settings: [ 'typography', 'fontSizes' ], + prefix: 'font-size', + }, + spacingSizes: { + settings: [ 'spacing', 'spacingSizes' ], + prefix: 'spacing-size', + }, } -export const usePresetControls = properties => { - const property = properties.join( '.' ) - const [ themePreset ] = useSettings( property ) +export const usePresetControls = property => { + // Get the base presets + const [ themePresets ] = useSettings( PRESET_MAPPING[ property ].settings.join( '.' ) ) + const basePresets = Array.isArray( themePresets ) && themePresets.length > 0 + ? themePresets + : PRESET_MAPPING[ property ].settings.reduce( ( acc, key ) => acc?.[ key ], DEFAULT_PRESETS.settings ) - const presets = Array.isArray( themePreset ) && themePreset.length > 0 - ? themePreset - : properties.reduce( ( acc, key ) => acc?.[ key ], DEFAULT_PRESETS.settings ) + // Get the custom presets + const { customPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() + return { customPresets: { ..._customPresetControls }[ property ] ?? [] } + }, [] ) - const prefix = PRESET_PREFIX[ property ] + // Convert custom presets into a lookup object for fast access + const customMap = customPresets.reduce( ( acc, item ) => { + acc[ item.slug ] = item + return acc + }, {} ) - return presets.map( preset => ( { + // Merge base presets with custom presets (priority) + const mergedPresets = basePresets.map( baseItem => + customMap[ baseItem.slug ] + ? { ...baseItem, ...customMap[ baseItem.slug ] } + : baseItem + ) + + const prefix = PRESET_MAPPING[ property ].prefix + + return mergedPresets.map( preset => ( { label: preset.name, + slug: preset.slug, + size: preset.size, value: `var(--stk--preset--${ prefix }--${ preset.slug }, ${ preset.size })`, } ) ) } diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 769effb621..82ea41cb09 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -15,6 +15,7 @@ */ class Stackable_Size_And_Spacing_Preset_Controls { + public $custom_presets; public $theme_presets; public $default_presets; public $stackable_presets; @@ -23,6 +24,7 @@ class Stackable_Size_And_Spacing_Preset_Controls { * Initialize */ function __construct() { + $this->custom_presets = get_option( 'stackable_global_custom_preset_controls' ); $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); $this->default_presets = WP_Theme_JSON_Resolver::get_core_data()->get_settings(); $this->stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); @@ -31,16 +33,7 @@ function __construct() { add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); } - /** - * Register the settings we need for preset controls - * - * @return void - */ - // public function register_preset_controls() { - - // } - - public function sanitize_array_setting( $input ) { + public static function sanitize_array_setting( $input ) { return ! is_array( $input ) ? array( array() ) : $input; } @@ -72,7 +65,7 @@ public function generate_css_variables( $presets, $prefix ) { } /** - * Get the value from an array deeply with an array of keys + * Get the value from an array with an array of keys * * @param array $array * @param array $keys @@ -97,7 +90,12 @@ public function add_preset_controls_styles( $current_css ) { $generated_css = ":root {\n"; foreach ( $preset_keys as $key => $value ) { - if ( ! empty( $this->deepGet( $this->theme_presets, $value )[ 'theme' ] ) ) { + if ( ! empty( $this->deepGet( $this->custom_presets, [ $value[ 1 ] ] ) ) ) { + $generated_css .= $this->generate_css_variables( + $this->deepGet( $this->custom_presets, [ $value[ 1 ] ] ), + $key, + ); + }elseif ( ! empty( $this->deepGet( $this->theme_presets, $value )[ 'theme' ] ) ) { $generated_css .= $this->generate_css_variables( $this->deepGet( $this->theme_presets, $value )[ 'theme' ], $key, From 526115e8ba9607e1565dc218852099df48339713 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 25 Mar 2025 12:11:39 +0800 Subject: [PATCH 017/124] feat: dynamically add styles in editor --- src/components/pro-control/index.js | 6 +++ src/plugins/global-settings/editor-loader.js | 6 +++ src/plugins/global-settings/index.js | 1 + .../global-settings/preset-controls/index.js | 47 +++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 src/plugins/global-settings/preset-controls/index.js diff --git a/src/components/pro-control/index.js b/src/components/pro-control/index.js index 43103e4e95..6dedba0280 100644 --- a/src/components/pro-control/index.js +++ b/src/components/pro-control/index.js @@ -124,6 +124,12 @@ const LABELS = {
  • { __( 'Manage your custom Font Pairs', i18n ) }
  • , }, + 'preset-controls': { + title: __( 'Customize the Preset Controls', i18n ), + description:
      +
    • { __( 'Customize your own Preset Controls', i18n ) }
    • +
    , + }, } const ProControl = props => { diff --git a/src/plugins/global-settings/editor-loader.js b/src/plugins/global-settings/editor-loader.js index 4387159cc6..9e23a4bed1 100644 --- a/src/plugins/global-settings/editor-loader.js +++ b/src/plugins/global-settings/editor-loader.js @@ -9,6 +9,7 @@ import { GlobalColorStyles } from './colors' import { GlobalTypographyStyles } from './typography' import { GlobalSpacingAndBordersStyles } from './spacing-and-borders' import { GlobalButtonsAndIconsStyles } from './buttons-and-icons' +import { GlobalPresetControlsStyles } from './preset-controls' import './block-defaults' /** @@ -48,6 +49,7 @@ const GlobalSettingsLoader = () => { editorBody.appendChild( globalColorWrapper ) editorBody.appendChild( globalSpacingAndBorderWrapper ) editorBody.appendChild( globalButtonsAndIconsWrapper ) + editorBody.appendChild( globalPresetControlsWrapper ) } }, [ deviceType, editorDom ] ) @@ -62,17 +64,21 @@ const globalTypographyWrapper = document?.createElement( 'style' ) const globalColorWrapper = document?.createElement( 'style' ) const globalSpacingAndBorderWrapper = document?.createElement( 'style' ) const globalButtonsAndIconsWrapper = document?.createElement( 'style' ) +const globalPresetControlsWrapper = document?.createElement( 'style' ) globalTypographyWrapper?.setAttribute( 'id', 'stk-global-typography-styles' ) globalColorWrapper?.setAttribute( 'id', 'stk-global-color-styles' ) globalSpacingAndBorderWrapper?.setAttribute( 'id', 'stk-global-spacing-and-borders-styles' ) globalButtonsAndIconsWrapper?.setAttribute( 'id', 'stk-global-buttons-and-icons-styles' ) +globalPresetControlsWrapper?.setAttribute( 'id', 'stk-global-preset-controls-styles' ) domReady( () => { document?.body?.appendChild( globalTypographyWrapper ) document?.body?.appendChild( globalColorWrapper ) document?.body?.appendChild( globalSpacingAndBorderWrapper ) document?.body?.appendChild( globalButtonsAndIconsWrapper ) + document?.body?.appendChild( globalPresetControlsWrapper ) createRoot( globalTypographyWrapper ).render( ) createRoot( globalColorWrapper ).render( ) createRoot( globalSpacingAndBorderWrapper ).render( ) createRoot( globalButtonsAndIconsWrapper ).render( ) + createRoot( globalPresetControlsWrapper ).render( ) } ) diff --git a/src/plugins/global-settings/index.js b/src/plugins/global-settings/index.js index 0a7a91c070..c1ca1867e1 100644 --- a/src/plugins/global-settings/index.js +++ b/src/plugins/global-settings/index.js @@ -6,6 +6,7 @@ import './buttons-and-icons' import './spacing-and-borders' import './block-defaults' import './icon-library' +import './preset-controls' /** * External dependencies diff --git a/src/plugins/global-settings/preset-controls/index.js b/src/plugins/global-settings/preset-controls/index.js new file mode 100644 index 0000000000..1a042a4e91 --- /dev/null +++ b/src/plugins/global-settings/preset-controls/index.js @@ -0,0 +1,47 @@ +/** + * Internal dependencies + */ +import { GlobalPresetControlsStyles } from './editor-loader' + +/** + * External dependencies + */ +import { + i18n, showProNotice, isPro, +} from 'stackable' +import { + PanelAdvancedSettings, + ProControl, +} from '~stackable/components' + +/** + * WordPress dependencies + */ + +import { addFilter, applyFilters } from '@wordpress/hooks' +import { Fragment } from '@wordpress/element' +import { __ } from '@wordpress/i18n' + +export { GlobalPresetControlsStyles } + +if ( showProNotice || isPro ) { + addFilter( 'stackable.global-settings.inspector', 'stackable/preset-controls', output => { + return ( + + { output } + + + { ! isPro && } + { isPro && + applyFilters( 'stackable.global-settings.inspector.preset-controls.control', null ) + } + + + + ) + }, 12 ) +} From 47cd13c7be5d16ccad9d2e392ee4a9f88e033c93 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 25 Mar 2025 12:16:43 +0800 Subject: [PATCH 018/124] feat: dynamically add styles in editor with editor loader --- .../preset-controls/editor-loader.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/plugins/global-settings/preset-controls/editor-loader.js diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js new file mode 100644 index 0000000000..b2cc5dce9f --- /dev/null +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -0,0 +1,51 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data' +import { useEffect, useState } from '@wordpress/element' + +/** + * External dependencies + */ +import { compact } from 'lodash' + +const PRESET_MAPPING = { + fontSizes: { + prefix: 'font-size', + }, + spacingSizes: { + prefix: 'spacing-size', + }, +} + +const renderGlobalStyles = ( customPresets, setStyles ) => { + let css = '' + + Object.entries( customPresets ).forEach( ( [ key, presets ] ) => { + const styleRules = presets?.map( preset => { + return preset + ? `--stk--preset--${ PRESET_MAPPING[ key ]?.prefix }--${ preset?.slug || '' }: ${ preset?.size || '' };` + : '' + } ) + css += compact( styleRules ).join( '' ) + } ) + + css = `:root { ${ css } }` + setStyles( css ) +} + +export const GlobalPresetControlsStyles = () => { + const { customPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() + return { customPresets: { ..._customPresetControls } ?? [] } + }, [] ) + const [ styles, setStyles ] = useState( '' ) + + useEffect( () => { + if ( customPresets && typeof customPresets === 'object' ) { + renderGlobalStyles( customPresets, setStyles ) + } + }, [ JSON.stringify( customPresets ) ] ) + + return styles +} From 09cfc86c174c197a2e256913604c5ed0afc56d12 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 25 Mar 2025 12:55:44 +0800 Subject: [PATCH 019/124] fix: get current marked value, go to markmode --- src/block-components/typography/edit.js | 1 + src/components/advanced-range-control/index.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 946aa68f4e..fc5eb7df22 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -272,6 +272,7 @@ export const Controls = props => { description: __( 'Sets the size of text characters', i18n ), } } marks={ presetMarks } + hasCSSVariableValue={ true } /> { hasColor && ( diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index ff2277433d..839b6f6d2f 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -107,7 +107,7 @@ const AdvancedRangeControl = props => { // at the start, or show custom let isMarkValue = !! props.marks if ( props.marks && value ) { - const valueToCheck = value + unit + const valueToCheck = value + ( props.hasCSSVariableValue ? '' : unit ) // Check if the current value exsits in the marks isMarkValue = isMarkValue && props.marks.some( mark => mark.value === valueToCheck ) } From d85dffa28889d6aca7976e3358f1e9f9cb29499a Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 26 Mar 2025 17:06:09 +0800 Subject: [PATCH 020/124] fix: css generation should consider custom, by each preset, not by property --- .../global-settings/preset-controls/index.php | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 82ea41cb09..19b6bf7855 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -14,6 +14,16 @@ * Size and Spacing Preset Controls */ class Stackable_Size_And_Spacing_Preset_Controls { + public const PRESET_MAPPING = array( + 'fontSizes' => array( + 'settings' => array('typography', 'fontSizes' ), + 'prefix' => 'font-size', + ), + 'spacingSizes' => array( + 'settings' => array( 'spacing', 'spacingSizes' ), + 'prefix' => 'spacing-size', + ), + ); public $custom_presets; public $theme_presets; @@ -47,18 +57,33 @@ private function load_presets( $json_path ) { } /** - * Generate CSS variables based on preset type (e.g., fontSizes, spacing) - * - * @param array $presests + * Generate CSS variables based on the property (e.g., fontSizes, spacing). + * The given presets will be overriden it match with a preset from custom. + * + * @param array $property + * @param array $presets * @param array $prefix * @return mixed */ - public function generate_css_variables( $presets, $prefix ) { + public function generate_css_variables( $property, $presets, $prefix ) { + $custom_presets = $this->custom_presets[ $property ] ?? []; + $css = ""; + + // Convert presets into an associative array with key 'slug' + $presets_by_slug = []; foreach ( $presets as $preset ) { + $presets_by_slug[ $preset[ 'slug' ] ] = $preset; + } + // Override values in base presets if it exist in custom presets + foreach ( $custom_presets as $custom ) { + $presets_by_slug[ $custom[ 'slug' ] ] = $custom; + } + + foreach ( $presets_by_slug as $preset ) { $slug = $preset[ 'slug' ]; $size = $preset[ 'size' ]; - $css .= "--stk--preset--$prefix--$slug: $size;\n"; + $css .= "--stk--preset--$prefix--{$preset['slug']}: {$preset['size']};\n"; } return $css; @@ -82,33 +107,26 @@ public function deepGet( $array, $keys ) { * @return String */ public function add_preset_controls_styles( $current_css ) { - $preset_keys = array( - 'spacing-size' => array( 'spacing', 'spacingSizes' ), - 'font-size' => array( 'typography', 'fontSizes' ), - ); - $generated_css = ":root {\n"; - foreach ( $preset_keys as $key => $value ) { - if ( ! empty( $this->deepGet( $this->custom_presets, [ $value[ 1 ] ] ) ) ) { - $generated_css .= $this->generate_css_variables( - $this->deepGet( $this->custom_presets, [ $value[ 1 ] ] ), - $key, - ); - }elseif ( ! empty( $this->deepGet( $this->theme_presets, $value )[ 'theme' ] ) ) { + foreach ( self::PRESET_MAPPING as $key => $value ) { + if ( ! empty( $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ] ) ) { $generated_css .= $this->generate_css_variables( - $this->deepGet( $this->theme_presets, $value )[ 'theme' ], $key, + $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ], + $value[ 'prefix' ], ); - } elseif ( ! empty( $this->deepGet( $this->default_presets, $value )[ 'default' ] ) ) { + } elseif ( ! empty( $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ] ) ) { $generated_css .= $this->generate_css_variables( - $this->deepGet( $this->default_presets, $value )[ 'default' ], $key, + $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ], + $value[ 'prefix' ], ); } else { $generated_css .= $this->generate_css_variables( - $this->deepGet( $this->stackable_presets, $value ), $key, + $this->deepGet( $this->stackable_presets, $value[ 'settings' ] ), + $value[ 'prefix' ], ); } } From 4d37e1a2590a1e79af1e8f3ca8b462228febb192 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 27 Mar 2025 12:38:28 +0800 Subject: [PATCH 021/124] fix: add additional functionalities to usePresetControls --- src/block-components/helpers/size/edit.js | 2 +- src/block-components/typography/edit.js | 2 +- src/hooks/use-preset-controls.js | 75 ++++++++++++++--------- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index eeb1ca9a31..23f81ea0cf 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -173,7 +173,7 @@ const Spacing = props => { highlight: 'margin', } - const presetMarks = usePresetControls( 'spacingSizes' ) + const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null return ( <> diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index fc5eb7df22..83cd0a7ce5 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -100,7 +100,7 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) - const presetMarks = usePresetControls( 'fontSizes' ) + const presetMarks = usePresetControls( 'fontSizes' )?.getPresetMarks() || null return ( <> diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index dfcee10c66..4e6a91d6ff 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -14,37 +14,56 @@ const PRESET_MAPPING = { } export const usePresetControls = property => { - // Get the base presets + // Get the theme presets for the property const [ themePresets ] = useSettings( PRESET_MAPPING[ property ].settings.join( '.' ) ) - const basePresets = Array.isArray( themePresets ) && themePresets.length > 0 + // Get all custom presets + const { allCustomPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() + return { allCustomPresets: { ..._customPresetControls } } + }, [] ) + + const hasThemePresets = Array.isArray( themePresets ) && themePresets.length > 0 + + // Get the theme/default presets if the user have one, else return the stackable presets + const basePresets = hasThemePresets ? themePresets : PRESET_MAPPING[ property ].settings.reduce( ( acc, key ) => acc?.[ key ], DEFAULT_PRESETS.settings ) - // Get the custom presets - const { customPresets } = useSelect( select => { - const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() - return { customPresets: { ..._customPresetControls }[ property ] ?? [] } - }, [] ) + // Returns the base presets overriden by the custom presets + const getMergedPresets = () => { + const customPresets = allCustomPresets[ property ] ?? [] + // Convert custom presets into a lookup object for fast access + const customMap = customPresets.reduce( ( acc, item ) => { + acc[ item.slug ] = item + return acc + }, {} ) + + // Merge base presets with custom presets (priority) + return basePresets.map( baseItem => + customMap[ baseItem.slug ] + ? { ...baseItem, ...customMap[ baseItem.slug ] } + : baseItem + ) + } + + // Get the merge preset marks with the CSS Variable value + const getPresetMarks = () => { + const prefix = PRESET_MAPPING[ property ].prefix + const mergedPresets = getMergedPresets() + + return mergedPresets + .filter( preset => ! ( preset?.isDiscarded ) ) + .map( preset => ( { + ...preset, + value: `var(--stk--preset--${ prefix }--${ preset.slug }, ${ preset.size })`, + } ) ) + } - // Convert custom presets into a lookup object for fast access - const customMap = customPresets.reduce( ( acc, item ) => { - acc[ item.slug ] = item - return acc - }, {} ) - - // Merge base presets with custom presets (priority) - const mergedPresets = basePresets.map( baseItem => - customMap[ baseItem.slug ] - ? { ...baseItem, ...customMap[ baseItem.slug ] } - : baseItem - ) - - const prefix = PRESET_MAPPING[ property ].prefix - - return mergedPresets.map( preset => ( { - label: preset.name, - slug: preset.slug, - size: preset.size, - value: `var(--stk--preset--${ prefix }--${ preset.slug }, ${ preset.size })`, - } ) ) + return { + hasThemePresets, + basePresets, + allCustomPresets, + getMergedPresets, + getPresetMarks, + } } From 1a97892069a2088740381b14cb4f85fe22f82775 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 27 Mar 2025 12:39:28 +0800 Subject: [PATCH 022/124] fix: allow AdvancedRangeControl to set custom onReset --- .../advanced-range-control/editor.scss | 4 +++ .../advanced-range-control/index.js | 25 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/components/advanced-range-control/editor.scss b/src/components/advanced-range-control/editor.scss index 6dc6f06eae..5e437de64e 100644 --- a/src/components/advanced-range-control/editor.scss +++ b/src/components/advanced-range-control/editor.scss @@ -69,3 +69,7 @@ .stk-range-control--with-marks.stk-range-control--mark-mode .components-range-control__wrapper { margin-inline-end: 8px; } + +div.components-base-control.stk-preset-controls { + flex-grow: 1; +} diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 839b6f6d2f..84c9d52e3c 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -121,6 +121,10 @@ const AdvancedRangeControl = props => { // On reset, allow overriding the value. if ( newValue === '' ) { + // Reset should also change from mark mode + if ( isMarkMode ) { + setIsMarkMode( false ) + } const overrideValue = props.onOverrideReset?.() if ( typeof overrideValue !== 'undefined' ) { newValue = overrideValue @@ -144,18 +148,18 @@ const AdvancedRangeControl = props => { propsToPass.sliderMax = props.marks.length - 1 propsToPass.step = 1 - // Show the marks and labels + // Show the marks and names propsToPass.marks = props.marks.reduce( ( acc, mark, index ) => { return [ { value: index, - label: undefined, + name: undefined, }, ...acc, ] }, [] ) propsToPass.renderTooltipContent = value => { - return props.marks[ value ]?.label || '' + return props.marks[ value ]?.name || '' } // Other necessary props for steps. @@ -171,6 +175,11 @@ const AdvancedRangeControl = props => { controlProps.className += isMarkMode ? ' stk-range-control--mark-mode' : '' } + if ( props.isCustomPreset ) { + controlProps.className = controlProps.className || '' + controlProps.className += 'stk-preset-controls' + } + // We need to change the way we handle the value and onChange if we're doing marks let rangeValue = propsToPass.isDynamic || props.hasCSSVariableValue ? parseFloat( derivedValue ) : derivedValue let rangeOnChange = _onChange @@ -227,10 +236,13 @@ const AdvancedRangeControl = props => {
    ) @@ -238,6 +250,8 @@ const AdvancedRangeControl = props => { AdvancedRangeControl.defaultProps = { allowReset: true, + onReset: undefined, + showReset: undefined, isDynamic: false, default: '', @@ -250,9 +264,10 @@ AdvancedRangeControl.defaultProps = { onOverrideReset: undefined, forcePlaceholder: false, - marks: undefined, // [{ value: '14px', label: 'S' }, { value: '16px', label: 'M' }] + marks: undefined, // [{ value: '14px', name: 'S' }, { value: '16px', name: 'M' }] allowCustom: true, hasCSSVariableValue: false, + isCustomPreset: false, } export default memo( AdvancedRangeControl, isEqual ) From 8c523468f506773e33c6ad39ba20e8adc485af8e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 27 Mar 2025 12:41:53 +0800 Subject: [PATCH 023/124] fix: do not add style for discarded presets --- src/plugins/global-settings/preset-controls/editor-loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js index b2cc5dce9f..1164cb3f46 100644 --- a/src/plugins/global-settings/preset-controls/editor-loader.js +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -23,7 +23,7 @@ const renderGlobalStyles = ( customPresets, setStyles ) => { Object.entries( customPresets ).forEach( ( [ key, presets ] ) => { const styleRules = presets?.map( preset => { - return preset + return preset && ( ! preset?.isDiscarded ) ? `--stk--preset--${ PRESET_MAPPING[ key ]?.prefix }--${ preset?.slug || '' }: ${ preset?.size || '' };` : '' } ) From 6a26a9779e2294a3dd9fc24f25c3274b6ee42d9b Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 30 Mar 2025 11:33:18 +0800 Subject: [PATCH 024/124] fix: add comments, title in generated css --- src/components/advanced-range-control/index.js | 3 ++- src/plugins/global-settings/preset-controls/index.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 84c9d52e3c..274ab85d1d 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -181,6 +181,7 @@ const AdvancedRangeControl = props => { } // We need to change the way we handle the value and onChange if we're doing marks + // Convert to float if the attribute is string to work with the slider let rangeValue = propsToPass.isDynamic || props.hasCSSVariableValue ? parseFloat( derivedValue ) : derivedValue let rangeOnChange = _onChange if ( isMarkMode ) { @@ -266,7 +267,7 @@ AdvancedRangeControl.defaultProps = { marks: undefined, // [{ value: '14px', name: 'S' }, { value: '16px', name: 'M' }] allowCustom: true, - hasCSSVariableValue: false, + hasCSSVariableValue: false, // If the attribute can have CSS variable value (string attribute\) isCustomPreset: false, } diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 19b6bf7855..e1ea8b2643 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -107,7 +107,8 @@ public function deepGet( $array, $keys ) { * @return String */ public function add_preset_controls_styles( $current_css ) { - $generated_css = ":root {\n"; + $generated_css = "\n/* Global Preset Controls */\n"; + $generated_css .= ":root {\n"; foreach ( self::PRESET_MAPPING as $key => $value ) { if ( ! empty( $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ] ) ) { From 754bfcb1d07163510ffbc1b05cc37f4714f86c93 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 30 Mar 2025 21:57:44 +0800 Subject: [PATCH 025/124] fix: FourRangeControl markmode and initial value --- src/block-components/helpers/size/edit.js | 2 ++ .../advanced-range-control/index.js | 2 +- src/components/four-range-control/index.js | 20 ++++++++++--------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 23f81ea0cf..5aed5a9072 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -193,6 +193,7 @@ const Spacing = props => { visualGuide={ paddingVisualGuide } placeholder={ props.paddingPlaceholder } marks={ presetMarks } + hasCSSVariableValue={ true } /> { props.enableMargin && @@ -211,6 +212,7 @@ const Spacing = props => { } } visualGuide={ marginVisualGuide } marks={ presetMarks } + hasCSSVariableValue={ true } /> } diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 274ab85d1d..6ef56962b7 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -267,7 +267,7 @@ AdvancedRangeControl.defaultProps = { marks: undefined, // [{ value: '14px', name: 'S' }, { value: '16px', name: 'M' }] allowCustom: true, - hasCSSVariableValue: false, // If the attribute can have CSS variable value (string attribute\) + hasCSSVariableValue: false, // If the attribute can have CSS variable value (string attribute) isCustomPreset: false, } diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index ffd1ed4e01..0986423033 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -206,11 +206,12 @@ const FourRangeControl = memo( props => { } if ( props.marks && value ) { // Check if the current value exsits in the marks - isMarkValue.first = isMarkValue.first && props.marks.some( mark => mark.value === firstValue + unit ) - isMarkValue.top = isMarkValue.top && props.marks.some( mark => mark.value === value.top + unit ) - isMarkValue.right = isMarkValue.right && props.marks.some( mark => mark.value === value.right + unit ) - isMarkValue.bottom = isMarkValue.bottom && props.marks.some( mark => mark.value === value.bottom + unit ) - isMarkValue.left = isMarkValue.left && props.marks.some( mark => mark.value === value.left + unit ) + const marksUnit = ( props.hasCSSVariableValue ? '' : unit ) + isMarkValue.first = isMarkValue.first && props.marks.some( mark => mark.value === firstValue + marksUnit ) + isMarkValue.top = isMarkValue.top && props.marks.some( mark => mark.value === value.top + marksUnit ) + isMarkValue.right = isMarkValue.right && props.marks.some( mark => mark.value === value.right + marksUnit ) + isMarkValue.bottom = isMarkValue.bottom && props.marks.some( mark => mark.value === value.bottom + marksUnit ) + isMarkValue.left = isMarkValue.left && props.marks.some( mark => mark.value === value.left + marksUnit ) } const [ isFourMarkMode, setIsFourMarkMode ] = useState( isMarkValue ) @@ -288,18 +289,18 @@ const FourRangeControl = memo( props => { newProps.sliderMax = props.marks.length - 1 newProps.step = 1 - // Show the marks and labels + // Show the marks and names newProps.marks = props.marks.reduce( ( acc, mark, index ) => { return [ { value: index, - label: undefined, + name: undefined, }, ...acc, ] }, [] ) newProps.renderTooltipContent = value => { - return props.marks[ value ]?.label || '' + return props.marks[ value ]?.name || '' } // Other necessary props for steps. @@ -321,7 +322,7 @@ const FourRangeControl = memo( props => { if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { const [ _value, _unit ] = extractNumberAndUnit( mark.value ) - return _value === value + return _value === initialValue } ) rangeOnChange = value => { if ( value === '' ) { @@ -804,6 +805,7 @@ FourRangeControl.defaultProps = { marks: undefined, allowCustom: true, + hasCSSVariableValue: false, } export default memo( FourRangeControl ) From 01d221a830f82e8263846b3e0ad5a26dfd496065 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 30 Mar 2025 22:37:57 +0800 Subject: [PATCH 026/124] feat: add row and column gap --- src/block-components/columns/attributes.js | 4 +-- src/block-components/columns/edit.js | 7 ++++++ src/block-components/columns/style.js | 29 +++++++++++++++++++--- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/block-components/columns/attributes.js b/src/block-components/columns/attributes.js index fe482cd786..2158b042fc 100644 --- a/src/block-components/columns/attributes.js +++ b/src/block-components/columns/attributes.js @@ -9,12 +9,12 @@ export const addAttributes = attrObject => { }, columnGap: { stkResponsive: true, - type: 'number', + type: 'string', default: '', }, rowGap: { stkResponsive: true, - type: 'number', + type: 'string', default: '', }, columnWrapDesktop: { // Only applies to desktops diff --git a/src/block-components/columns/edit.js b/src/block-components/columns/edit.js index 83dbee0376..363a0a95ae 100644 --- a/src/block-components/columns/edit.js +++ b/src/block-components/columns/edit.js @@ -23,6 +23,7 @@ import { useBlockSetAttributesContext, useDeviceType, useBlockLayoutDefaults, + usePresetControls, } from '~stackable/hooks' import { range } from 'lodash' @@ -124,6 +125,8 @@ export const Controls = props => { : deviceType === 'Tablet' ? ( attributes.columnArrangementTablet || defaultArrangement ) : ( attributes.columnArrangementMobile || defaultArrangement ) + const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + return ( <> { props.hasColumnsControl && } @@ -284,6 +287,8 @@ export const Controls = props => { video: 'column-gap', description: __( 'Sets the distance between two or more columns', i18n ), } } + marks={ presetMarks } + hasCSSVariableValue={ true } /> { // TODO: Add a working video description: __( 'Sets the distance between two or more columns', i18n ), } } + marks={ presetMarks } + hasCSSVariableValue={ true } /> ) } diff --git a/src/block-components/columns/style.js b/src/block-components/columns/style.js index 8e2c614d5a..890e5cb1cc 100644 --- a/src/block-components/columns/style.js +++ b/src/block-components/columns/style.js @@ -30,16 +30,27 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { selector: '.%s-column', styleRule: '--stk-column-gap', attrName: 'columnGap', - format: '%spx', responsive: 'all', + valueCallback: value => { + // Substitute with using format to work with preset controls + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, }, { ...propsToPass, renderIn: 'edit', selector: '.%s-column > .block-editor-inner-blocks > .block-editor-block-list__layout', styleRule: '--stk-column-gap', attrName: 'columnGap', - format: '%spx', responsive: 'all', + valueCallback: value => { + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, } ] ) blockStyleGenerator.addBlockStyles( 'columnWrapDesktop', [ { @@ -78,16 +89,26 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { selector: '.%s-column', styleRule: 'rowGap', attrName: 'rowGap', - format: '%spx', responsive: 'all', + valueCallback: value => { + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, }, { ...propsToPass, renderIn: 'edit', selector: '.%s-column > .block-editor-inner-blocks > .block-editor-block-list__layout', styleRule: 'rowGap', attrName: 'rowGap', - format: '%spx', responsive: 'all', + valueCallback: value => { + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, } ] ) } From c95763074e9f0bf38b1c9dd625d68121eb6dcbb4 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 31 Mar 2025 09:58:04 +0800 Subject: [PATCH 027/124] feat: add blockHeight and borderRadius --- src/block-components/helpers/borders/edit.js | 6 +- src/block-components/helpers/borders/style.js | 33 ++++++- .../helpers/size/attributes.js | 2 +- src/block-components/helpers/size/edit.js | 40 +-------- src/hooks/use-preset-controls.js | 8 ++ .../global-settings/preset-controls/index.php | 8 ++ .../preset-controls/presets.json | 88 ++++++++++++++++++- 7 files changed, 140 insertions(+), 45 deletions(-) diff --git a/src/block-components/helpers/borders/edit.js b/src/block-components/helpers/borders/edit.js index 44ab25c4cb..ffd96697d2 100644 --- a/src/block-components/helpers/borders/edit.js +++ b/src/block-components/helpers/borders/edit.js @@ -15,7 +15,7 @@ import { import { Fragment } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { - useAttributeEditHandlers, useBlockLayoutDefaults, useBlockSetAttributesContext, + useAttributeEditHandlers, useBlockLayoutDefaults, useBlockSetAttributesContext, usePresetControls, } from '~stackable/hooks' import { applyFilters } from '@wordpress/hooks' @@ -71,6 +71,8 @@ export const BorderControls = props => { applyFilters( 'stackable.block-component.helpers.borders', null, getAttribute, updateAttributes ) + const presetMarks = usePresetControls( 'borderRadius' )?.getPresetMarks() || null + return ( { props.hasBorderType && @@ -132,6 +134,8 @@ export const BorderControls = props => { isCorner={ true } sliderMax={ props.borderSliderMax } placeholder={ props.borderRadiusPlaceholder } + marks={ presetMarks } + hasCSSVariableValue={ true } /> } { attrName: 'borderRadius2', key: 'borderTopLeftRadius2', attrNameTemplate, - format: '%spx', + valueCallback: value => { + // Substitute with using format to work with preset controls + value = typeof value === 'number' ? value.toString() : value + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, responsive: 'all', hover: 'all', valuePreCallback: value => value?.top, @@ -48,7 +55,13 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'borderRadius2', key: 'borderTopRightRadius2', attrNameTemplate, - format: '%spx', + valueCallback: value => { + value = typeof value === 'number' ? value.toString() : value + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, responsive: 'all', hover: 'all', valuePreCallback: value => value?.right, @@ -60,7 +73,13 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'borderRadius2', key: 'borderBottomRightRadius2', attrNameTemplate, - format: '%spx', + valueCallback: value => { + value = typeof value === 'number' ? value.toString() : value + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, responsive: 'all', hover: 'all', valuePreCallback: value => value?.left, @@ -72,7 +91,13 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'borderRadius2', key: 'borderBottomLeftRadius2', attrNameTemplate, - format: '%spx', + valueCallback: value => { + value = typeof value === 'number' ? value.toString() : value + if ( value.startsWith( 'var(--stk' ) ) { + return value + } + return value + 'px' + }, responsive: 'all', hover: 'all', valuePreCallback: value => value?.bottom, diff --git a/src/block-components/helpers/size/attributes.js b/src/block-components/helpers/size/attributes.js index 83f73deb1d..4c51277183 100644 --- a/src/block-components/helpers/size/attributes.js +++ b/src/block-components/helpers/size/attributes.js @@ -2,7 +2,7 @@ export const sizeAttributes = { height: { stkResponsive: true, stkUnits: 'px', - type: 'number', + type: 'string', default: '', }, width: { diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 5aed5a9072..597fc37126 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -17,41 +17,6 @@ import { import { __ } from '@wordpress/i18n' import { getAttributeNameFunc } from '~stackable/util' -const HEIGHT_PRESET = [ - { - value: '64px', - label: 'XS', - }, - { - value: '128px', - label: 'S', - }, - { - value: '192px', - label: 'M', - }, - { - value: '256px', - label: 'L', - }, - { - value: '384px', - label: 'XL', - }, - { - value: '512px', - label: '2XL', - }, - { - value: '640px', - label: '3XL', - }, - { - value: '100vh', - label: 'Full', - }, -] - const Layout = props => { const deviceType = useDeviceType() @@ -67,7 +32,7 @@ const Layout = props => { labelVerticalAlign = __( 'Content Vertical Align', i18n ), } = props.labels - const heightMarks = HEIGHT_PRESET + const presetMarks = usePresetControls( 'blockHeights' )?.getPresetMarks() || null return ( <> @@ -86,7 +51,8 @@ const Layout = props => { description: __( 'Adjusts the minimum allowable height of the block', i18n ), } } visualGuide={ props.visualGuide } - marks={ heightMarks } + marks={ presetMarks } + hasCSSVariableValue={ true } /> } { props.hasContentVerticalAlign && diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index 4e6a91d6ff..e7735af306 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -11,6 +11,14 @@ const PRESET_MAPPING = { settings: [ 'spacing', 'spacingSizes' ], prefix: 'spacing-size', }, + blockHeights: { + settings: [ 'blockHeights' ], + prefix: 'block-height', + }, + borderRadius: { + settings: [ 'borderRadius' ], + prefix: 'border-radius', + }, } export const usePresetControls = property => { diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index e1ea8b2643..111d58bcb9 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -23,6 +23,14 @@ class Stackable_Size_And_Spacing_Preset_Controls { 'settings' => array( 'spacing', 'spacingSizes' ), 'prefix' => 'spacing-size', ), + 'blockHeights' => array( + 'settings' => array( 'blockHeights' ), + 'prefix' => 'block-height', + ), + 'borderRadius' => array( + 'settings' => array( 'borderRadius' ), + 'prefix' => 'border-radius', + ), ); public $custom_presets; diff --git a/src/plugins/global-settings/preset-controls/presets.json b/src/plugins/global-settings/preset-controls/presets.json index 553bcc1328..60ce0169ef 100644 --- a/src/plugins/global-settings/preset-controls/presets.json +++ b/src/plugins/global-settings/preset-controls/presets.json @@ -92,6 +92,90 @@ "slug": "xxxxx-large" } ] - } - } + }, + "blockHeights": [ + { + "name": "XS", + "size": "4rem", + "slug": "x-small" + }, + { + "name": "S", + "size": "8rem", + "slug": "small" + }, + { + "name": "M", + "size": "12rem", + "slug": "medium" + }, + { + "name": "L", + "size": "16rem", + "slug": "large" + }, + { + "name": "XL", + "size": "24rem", + "slug": "x-large" + }, + { + "name": "2XL", + "size": "32rem", + "slug": "xx-large" + }, + { + "name": "3XL", + "size": "40rem", + "slug": "xxx-large" + }, + { + "name": "Full", + "size": "100vh", + "slug": "full" + } + ], + "borderRadius": [ + { + "name": "None", + "size": "0px", + "slug": "none" + }, + { + "name": "XS", + "size": "2px", + "slug": "x-small" + }, + { + "name": "S", + "size": "4px", + "slug": "small" + }, + { + "name": "M", + "size": "8px", + "slug": "medium" + }, + { + "name": "L", + "size": "16px", + "slug": "large" + }, + { + "name": "XL", + "size": "24px", + "slug": "x-large" + }, + { + "name": "2XL", + "size": "32px", + "slug": "xx-large" + }, + { + "name": "Full", + "size": "9999px", + "slug": "full" + } + ] + } } From 62275283463ac470f9fb1bdf32e9c2d205392f89 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 31 Mar 2025 09:58:26 +0800 Subject: [PATCH 028/124] feat: add blockHeight and borderRadius loader --- .../global-settings/preset-controls/editor-loader.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js index 1164cb3f46..d66aae36fd 100644 --- a/src/plugins/global-settings/preset-controls/editor-loader.js +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -16,6 +16,12 @@ const PRESET_MAPPING = { spacingSizes: { prefix: 'spacing-size', }, + blockHeights: { + prefix: 'block-height', + }, + borderRadius: { + prefix: 'border-radius', + }, } const renderGlobalStyles = ( customPresets, setStyles ) => { From 5d22e5f7a57fc000b5e7f06c1b45414ac915543e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 31 Mar 2025 10:11:53 +0800 Subject: [PATCH 029/124] fix: editorLoader merge --- .../preset-controls/editor-loader.js | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/plugins/global-settings/preset-controls/editor-loader.js diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js new file mode 100644 index 0000000000..d66aae36fd --- /dev/null +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -0,0 +1,57 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data' +import { useEffect, useState } from '@wordpress/element' + +/** + * External dependencies + */ +import { compact } from 'lodash' + +const PRESET_MAPPING = { + fontSizes: { + prefix: 'font-size', + }, + spacingSizes: { + prefix: 'spacing-size', + }, + blockHeights: { + prefix: 'block-height', + }, + borderRadius: { + prefix: 'border-radius', + }, +} + +const renderGlobalStyles = ( customPresets, setStyles ) => { + let css = '' + + Object.entries( customPresets ).forEach( ( [ key, presets ] ) => { + const styleRules = presets?.map( preset => { + return preset && ( ! preset?.isDiscarded ) + ? `--stk--preset--${ PRESET_MAPPING[ key ]?.prefix }--${ preset?.slug || '' }: ${ preset?.size || '' };` + : '' + } ) + css += compact( styleRules ).join( '' ) + } ) + + css = `:root { ${ css } }` + setStyles( css ) +} + +export const GlobalPresetControlsStyles = () => { + const { customPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() + return { customPresets: { ..._customPresetControls } ?? [] } + }, [] ) + const [ styles, setStyles ] = useState( '' ) + + useEffect( () => { + if ( customPresets && typeof customPresets === 'object' ) { + renderGlobalStyles( customPresets, setStyles ) + } + }, [ JSON.stringify( customPresets ) ] ) + + return styles +} From 962b8410f1c822069874147d6728864ca08cf8d0 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 2 Apr 2025 09:27:14 +0800 Subject: [PATCH 030/124] feat: Use Global Typography sizes as preset --- src/block-components/typography/edit.js | 7 +- src/global-settings.php | 12 ++++ src/hooks/use-preset-controls.js | 7 +- .../global-settings/typography/index.js | 69 ++++++++++++++++++- 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 83cd0a7ce5..5451880b55 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -30,6 +30,7 @@ import { } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { applyFilters } from '@wordpress/hooks' +import { useSelect } from '@wordpress/data' const TYPOGRAPHY_SHADOWS = [ 'none', @@ -80,6 +81,10 @@ export const Controls = props => { const attributeName = getAttrNameFunction( attrNameTemplate ) const text = getAttribute( 'text' ) const [ debouncedText, setDebouncedText ] = useState( text ) + const { useTypographyAsPresets } = useSelect( select => { + const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false + return { useTypographyAsPresets: { ..._useTypographyAsPresets } } + }, [] ) useEffect( () => { if ( text !== debouncedText ) { @@ -100,7 +105,7 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) - const presetMarks = usePresetControls( 'fontSizes' )?.getPresetMarks() || null + const presetMarks = usePresetControls( 'fontSizes' )?.getPresetMarks( useTypographyAsPresets ) || null return ( <> diff --git a/src/global-settings.php b/src/global-settings.php index ec8796676c..812e9fccc0 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -412,6 +412,18 @@ public function register_global_settings() { ) ); + register_setting( + 'stackable_global_settings', + 'stackable_use_typography_as_presets', + array( + 'type' => 'boolean', + 'description' => __( 'Use Global Typography font sizes as preset', STACKABLE_I18N ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => '', + ) + ); + register_setting( 'stackable_global_settings', 'stackable_icon_library', diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index e7735af306..ab89fa8e21 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -55,11 +55,12 @@ export const usePresetControls = property => { } // Get the merge preset marks with the CSS Variable value - const getPresetMarks = () => { + // Setting customOnly to true returns the preset marks for custom presets only + const getPresetMarks = ( customOnly = false ) => { const prefix = PRESET_MAPPING[ property ].prefix - const mergedPresets = getMergedPresets() + const presets = customOnly ? allCustomPresets[ property ] ?? [] : getMergedPresets() - return mergedPresets + return presets .filter( preset => ! ( preset?.isDiscarded ) ) .map( preset => ( { ...preset, diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 5750f59620..d9fe2179ad 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -12,7 +12,7 @@ import FREE_FONT_PAIRS from './font-pairs.json' import { PanelAdvancedSettings, AdvancedSelectControl, ControlSeparator, FontPairPicker, ProControlButton, } from '~stackable/components' -import { fetchSettings } from '~stackable/util' +import { fetchSettings, getDefaultFontSize } from '~stackable/util' import { i18n, isPro, showProNotice, } from 'stackable' @@ -29,6 +29,7 @@ import { addFilter, applyFilters, doAction, } from '@wordpress/hooks' import { __, sprintf } from '@wordpress/i18n' +import { dispatch, useSelect } from '@wordpress/data' export { GlobalTypographyStyles } @@ -36,34 +37,50 @@ const TYPOGRAPHY_TAGS = [ { label: sprintf( __( 'Heading %d', i18n ), 1 ), selector: 'h1', + presetName: '5XL', + presetSlug: 'xxxxx-large', }, { label: sprintf( __( 'Heading %d', i18n ), 2 ), selector: 'h2', + presetName: '4XL', + presetSlug: 'xxxx-large', }, { label: sprintf( __( 'Heading %d', i18n ), 3 ), selector: 'h3', + presetName: '3XL', + presetSlug: 'xxx-large', }, { label: sprintf( __( 'Heading %d', i18n ), 4 ), selector: 'h4', + presetName: '2XL', + presetSlug: 'xx-large', }, { label: sprintf( __( 'Heading %d', i18n ), 5 ), selector: 'h5', + presetName: 'XL', + presetSlug: 'x-large', }, { label: sprintf( __( 'Heading %d', i18n ), 6 ), selector: 'h6', + presetName: 'L', + presetSlug: 'large', }, { label: __( 'Body Text', i18n ), selector: 'p', + presetName: 'M', + presetSlug: 'medium', }, { label: __( 'Subtitle', i18n ), selector: '.stk-subtitle', + presetName: 'S', + presetSlug: 'small', help: ( <> { sprintf( __( "To apply this typography style, just add `%s` in your block\'s Additional CSS classes. Also make sure that `%s` tag is set to avoid conflict with other typography styles", i18n ), 'stk-subtitle', 'p' ) } @@ -78,8 +95,15 @@ const TYPOGRAPHY_TAGS = [ let saveTypographyThrottle = null let saveSelectedFontPairThrottle = null let saveCustomFontPairsThrottle = null +let saveTypographyAsPresetsThrottle = null addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', output => { + const { allCustomPresets, useTypographyAsPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() ?? {} + const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false + return { allCustomPresets: { ..._customPresetControls }, useTypographyAsPresets: { ..._useTypographyAsPresets } } + }, [] ) + const FONT_PAIRS = applyFilters( 'stackable.global-settings.typography.font-pairs.premium-font-pairs', FREE_FONT_PAIRS ) const [ isPanelOpen, setIsPanelOpen ] = useState( false ) @@ -104,6 +128,49 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', // When typography styles are changed, trigger our editor style generator to update. useEffect( () => { doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo ) + + if ( useTypographyAsPresets ) { + const fontSizePresets = TYPOGRAPHY_TAGS + .filter( ( { presetSlug } ) => !! presetSlug ) + .map( ( { + selector, presetName, presetSlug, + } ) => { + const size = typographySettings[ selector ]?.fontSize ?? getDefaultFontSize( selector ) ?? 16 + const unit = typographySettings[ selector ]?.fontSizeUnit ?? 'px' + return { + name: presetName, + slug: presetSlug, + size: `${ size }${ unit }`, + } + } ) + // Add the preset for extra small + let xSmallSize = typographySettings[ '.stk-subtitle' ]?.fontSize ?? getDefaultFontSize( '.stk-subtitle' ) ?? 16 + let xSmallUnit = typographySettings[ '.stk-subtitle' ]?.fontSizeUnit ?? 'px' + if ( xSmallUnit === 'px' ) { + xSmallSize = Math.pow( xSmallSize / 16, 2 ) + xSmallUnit = 'rem' + } else { + xSmallSize = Math.pow( xSmallSize, 2 ) + } + + fontSizePresets.push( { + name: 'XS', + slug: 'x-small', + size: `${ xSmallSize }${ xSmallUnit ?? 'px' }`, + } ) + // Reverse the presets so it's from smallest to biggest + fontSizePresets.reverse() + + const newSettings = { ...allCustomPresets, fontSizes: fontSizePresets } + + clearTimeout( saveTypographyAsPresetsThrottle ) + saveTypographyAsPresetsThrottle = setTimeout( () => { + const settings = new models.Settings( { stackable_global_custom_preset_controls: newSettings } ) // eslint-disable-line camelcase + settings.save() + }, 300 ) + + dispatch( 'stackable/global-preset-controls.custom' ).updateCustomPresetControls( newSettings ) + } }, [ JSON.stringify( typographySettings ), applySettingsTo ] ) // Scroll to the selected font pair when Global Typography tab is toggled From d0b7cba04404f1c38069a6951567b5c7f8848c01 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 2 Apr 2025 10:31:47 +0800 Subject: [PATCH 031/124] feat: add vertical/horizontal to mark mode for four range --- src/block-components/button/edit.js | 9 +++- src/components/four-range-control/index.js | 57 +++++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/block-components/button/edit.js b/src/block-components/button/edit.js index ed363ca0c1..9a58b50e3f 100644 --- a/src/block-components/button/edit.js +++ b/src/block-components/button/edit.js @@ -12,7 +12,9 @@ import { AdvancedSelectControl, } from '~stackable/components' import { i18n } from 'stackable' -import { useBlockAttributesContext, useBlockLayoutDefaults } from '~stackable/hooks' +import { + useBlockAttributesContext, useBlockLayoutDefaults, usePresetControls, +} from '~stackable/hooks' /** * WordPress dependencies @@ -185,6 +187,9 @@ const SizeControls = props => { const { getPlaceholder } = useBlockLayoutDefaults() const buttonPaddingPlaceholder = getPlaceholder( paddingPlaceholderName, { single: false } ) + + const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + return ( <> { props.hasFullWidth && ( { title: __( 'Button padding', i18n ), description: __( 'Adjusts the space between the button text and button borders', i18n ), } } + marks={ presetMarks } + hasCSSVariableValue={ true } /> ) } diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 0986423033..73a0f361e6 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -347,6 +347,7 @@ const FourRangeControl = memo( props => { ] } + // Create step supports for each side const [ propsToPassFirst, rangeValueFirst, rangeOnChangeFirst ] = stepSupport( isFourMarkMode.first, firstValue, @@ -377,6 +378,18 @@ const FourRangeControl = memo( props => { onChangeLeft, ) + const [ propsToPassVertical, rangeValueVertical, rangeOnChangeVertical ] = stepSupport( + isFourMarkMode.top, + value.top, + onChangeVertical, + ) + + const [ propsToPassHorizontal, rangeValueHorizontal, rangeOnChangeHorizontal ] = stepSupport( + isFourMarkMode.left, + value.left, + onChangeHorizontal, + ) + return ( { isLocked && ! props.vhMode && ( @@ -442,9 +455,9 @@ const FourRangeControl = memo( props => { { if ( currentHoverState !== 'normal' ) { @@ -472,7 +485,21 @@ const FourRangeControl = memo( props => { return typeof props.placeholderTop === 'undefined' ? propsToPass.placeholder : props.placeholderTop } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + { { if ( currentHoverState !== 'normal' ) { @@ -514,7 +541,21 @@ const FourRangeControl = memo( props => { } return typeof props.placeholderLeft === 'undefined' ? propsToPass.placeholder : props.placeholderLeft } )() } - /> + __nextHasNoMarginBottom + > + { props.allowCustom && props.marks && ( + + ) } + Date: Wed, 2 Apr 2025 11:05:45 +0800 Subject: [PATCH 032/124] fix: removing of unit picker according to four range mode --- src/components/four-range-control/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 73a0f361e6..ff011f6490 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -305,7 +305,6 @@ const FourRangeControl = memo( props => { // Other necessary props for steps. newProps.withInputField = false - controlProps.units = false } else { newProps.marks = undefined } @@ -347,6 +346,18 @@ const FourRangeControl = memo( props => { ] } + // Remove the unit picker if not in mark mode for every four range mode + if ( isLocked && ! props.vhMode ) { + controlProps.units = isFourMarkMode.first + ? false : controlProps.units + } else if ( isLocked && props.vhMode ) { + controlProps.units = isFourMarkMode.top && isFourMarkMode.left + ? false : controlProps.units + } else { + controlProps.units = isFourMarkMode.top && isFourMarkMode.right && isFourMarkMode.bottom && isFourMarkMode.left + ? false : controlProps.units + } + // Create step supports for each side const [ propsToPassFirst, rangeValueFirst, rangeOnChangeFirst ] = stepSupport( isFourMarkMode.first, From 9047dca37dc27e8ac5344d3e359aaf5a6047920f Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 3 Apr 2025 10:11:49 +0800 Subject: [PATCH 033/124] fix: useTypographyAsPresets reactivity --- src/block-components/typography/edit.js | 7 +++---- src/plugins/global-settings/typography/index.js | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 5451880b55..3e86c9576c 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -81,10 +81,9 @@ export const Controls = props => { const attributeName = getAttrNameFunction( attrNameTemplate ) const text = getAttribute( 'text' ) const [ debouncedText, setDebouncedText ] = useState( text ) - const { useTypographyAsPresets } = useSelect( select => { - const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false - return { useTypographyAsPresets: { ..._useTypographyAsPresets } } - }, [] ) + const useTypographyAsPresets = useSelect( select => + select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false + ) useEffect( () => { if ( text !== debouncedText ) { diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index d9fe2179ad..3252236757 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -101,7 +101,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const { allCustomPresets, useTypographyAsPresets } = useSelect( select => { const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() ?? {} const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false - return { allCustomPresets: { ..._customPresetControls }, useTypographyAsPresets: { ..._useTypographyAsPresets } } + return { allCustomPresets: { ..._customPresetControls }, useTypographyAsPresets: _useTypographyAsPresets } }, [] ) const FONT_PAIRS = applyFilters( 'stackable.global-settings.typography.font-pairs.premium-font-pairs', FREE_FONT_PAIRS ) @@ -125,10 +125,11 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', } ) }, [] ) - // When typography styles are changed, trigger our editor style generator to update. useEffect( () => { + // When typography styles are changed, trigger our editor style generator to update. doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo ) + // Update the custom presets when using typography as presets if ( useTypographyAsPresets ) { const fontSizePresets = TYPOGRAPHY_TAGS .filter( ( { presetSlug } ) => !! presetSlug ) @@ -171,7 +172,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', dispatch( 'stackable/global-preset-controls.custom' ).updateCustomPresetControls( newSettings ) } - }, [ JSON.stringify( typographySettings ), applySettingsTo ] ) + }, [ JSON.stringify( typographySettings ), applySettingsTo, useTypographyAsPresets ] ) // Scroll to the selected font pair when Global Typography tab is toggled useEffect( () => { From cd1cbc143e704c482188b5b98b009b52ee789be4 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 4 Apr 2025 15:35:09 +0800 Subject: [PATCH 034/124] feat: initial init --- .../global-settings/typography/index.js | 80 ++++++++++++++++++- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 3252236757..e079fdb0c5 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -92,6 +92,18 @@ const TYPOGRAPHY_TAGS = [ }, ] +const TYPE_SCALE = [ + { label: __( 'None / Custom', i18n ), value: 1 }, + { label: __( '1.067 - Minor Second', i18n ), value: 1.067 }, + { label: __( '1.125 - Major Second', i18n ), value: 1.125 }, + { label: __( '1.200 - Minor Third', i18n ), value: 1.2 }, + { label: __( '1.250 - Major Third', i18n ), value: 1.25 }, + { label: __( '1.333 - Perfect Fourth', i18n ), value: 1.333 }, + { label: __( '1.414 - Augmented Fourth', i18n ), value: 1.414 }, + { label: __( '1.500 - Perfect Fifth', i18n ), value: 1.5 }, + { label: __( '1.618 - Golden Ratio', i18n ), value: 1.618 }, +] + let saveTypographyThrottle = null let saveSelectedFontPairThrottle = null let saveCustomFontPairsThrottle = null @@ -112,16 +124,35 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const [ customFontPairs, setCustomFontPairs ] = useState( [] ) const [ selectedFontPairName, setSelectedFontPairName ] = useState( '' ) const [ isEditingFontPair, setIsEditingFontPair ] = useState( false ) + const [ selectedTypeScale, setSelectedTypeScale ] = useState( 1 ) const fontPairContainerRef = useRef( null ) useEffect( () => { fetchSettings().then( response => { // Get settings. - setTypographySettings( ( head( response.stackable_global_typography ) ) || {} ) + const _typographySettings = ( head( response.stackable_global_typography ) ) || {} + setTypographySettings( _typographySettings ) setApplySettingsTo( response.stackable_global_typography_apply_to || 'blocks-stackable-native' ) setCustomFontPairs( response.stackable_custom_font_pairs || [] ) setSelectedFontPairName( response.stackable_selected_font_pair || '' ) + + // Reversely compute the type scale from the font sizes + // Check first if the units are rem + if ( Object.values( _typographySettings ).every( setting => setting.fontSizeUnit === 'rem' ) ) { + let typeScale = _typographySettings?.h6?.fontSize + const computedApplied = getAppliedTypeScale( typeScale ) + + const tags = Object.keys( _typographySettings ) + for ( const tag of tags ) { + // If font size mismatch, set typography scale to None / Custom + if ( _typographySettings[ tag ].fontSize !== computedApplied[ tag ].fontSize ) { + typeScale = 1 + } + } + + setSelectedTypeScale( typeScale ) + } } ) }, [] ) @@ -194,6 +225,19 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', return [ ...FONT_PAIRS, ...customFontPairs ].find( fontPair => fontPair.name === selectedFontPairName ) } + const getAppliedTypeScale = typeScale => ( { + h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, + p: { fontSize: 1, fontSizeUnit: 'rem' }, + '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, + + } ) + const updateTypography = newSettings => { setTypographySettings( newSettings ) @@ -238,6 +282,21 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', model.save() } + const updateTypeScale = value => { + const typeScale = Number( value ) + if ( isNaN( typeScale ) ) { + return + } + + setSelectedTypeScale( typeScale ) + + // Only update the typography settings if not None/Custom + if ( typeScale !== 1 ) { + const newSettings = getAppliedTypeScale( typeScale ) + changeStyles( newSettings ) + } + } + const changeStyles = typography => { const newSettings = { ...typographySettings } @@ -433,6 +492,13 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography',

    { __( 'Typography Settings' ) }

    + { TYPOGRAPHY_TAGS.map( ( { label, selector, help, }, index ) => { @@ -445,8 +511,16 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', value={ ( typographySettings[ selector ] ) || {} } defaultFontFamily={ getDefaultFontFamily( selector ) } isAllowReset={ getIsAllowReset( selector ) } - onChange={ styles => changeStyles( { [ selector ]: styles } ) } - onReset={ () => resetStyles( selector ) } + onChange={ styles => { + changeStyles( { [ selector ]: styles } ) + // Also set the typescale to None/Custom + setSelectedTypeScale( 1 ) + } } + onReset={ () => { + resetStyles( selector ) + // Also set the typescale to None/Custom + setSelectedTypeScale( 1 ) + } } /> ) } ) } From 045f6a03d5ffa3ed42e03adf1a193fc62712171b Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sat, 5 Apr 2025 22:30:39 +0800 Subject: [PATCH 035/124] fix: separate none and custom, fix issue with setting to none --- .../global-settings/typography/index.js | 143 ++++++++++++------ 1 file changed, 97 insertions(+), 46 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index e079fdb0c5..1e4dcfa0e8 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -16,7 +16,9 @@ import { fetchSettings, getDefaultFontSize } from '~stackable/util' import { i18n, isPro, showProNotice, } from 'stackable' -import { head, isEqual } from 'lodash' +import { + head, isEqual, cloneDeep, +} from 'lodash' /** * WordPress dependencies @@ -93,15 +95,47 @@ const TYPOGRAPHY_TAGS = [ ] const TYPE_SCALE = [ - { label: __( 'None / Custom', i18n ), value: 1 }, - { label: __( '1.067 - Minor Second', i18n ), value: 1.067 }, - { label: __( '1.125 - Major Second', i18n ), value: 1.125 }, - { label: __( '1.200 - Minor Third', i18n ), value: 1.2 }, - { label: __( '1.250 - Major Third', i18n ), value: 1.25 }, - { label: __( '1.333 - Perfect Fourth', i18n ), value: 1.333 }, - { label: __( '1.414 - Augmented Fourth', i18n ), value: 1.414 }, - { label: __( '1.500 - Perfect Fifth', i18n ), value: 1.5 }, - { label: __( '1.618 - Golden Ratio', i18n ), value: 1.618 }, + { + label: __( 'None', i18n ), + value: 'none', + }, + { + label: __( 'Custom', i18n ), + value: 'custom', + disabled: true, + }, + { + label: __( '1.067 - Minor Second', i18n ), + value: '1.067', + }, + { + label: __( '1.125 - Major Second', i18n ), + value: '1.125', + }, + { + label: __( '1.200 - Minor Third', i18n ), + value: '1.2', + }, + { + label: __( '1.250 - Major Third', i18n ), + value: '1.25', + }, + { + label: __( '1.333 - Perfect Fourth', i18n ), + value: '1.333', + }, + { + label: __( '1.414 - Augmented Fourth', i18n ), + value: '1.414', + }, + { + label: __( '1.500 - Perfect Fifth', i18n ), + value: '1.5', + }, + { + label: __( '1.618 - Golden Ratio', i18n ), + value: '1.618', + }, ] let saveTypographyThrottle = null @@ -124,7 +158,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const [ customFontPairs, setCustomFontPairs ] = useState( [] ) const [ selectedFontPairName, setSelectedFontPairName ] = useState( '' ) const [ isEditingFontPair, setIsEditingFontPair ] = useState( false ) - const [ selectedTypeScale, setSelectedTypeScale ] = useState( 1 ) + const [ selectedTypeScale, setSelectedTypeScale ] = useState( 'none' ) const fontPairContainerRef = useRef( null ) @@ -135,22 +169,19 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', setTypographySettings( _typographySettings ) setApplySettingsTo( response.stackable_global_typography_apply_to || 'blocks-stackable-native' ) setCustomFontPairs( response.stackable_custom_font_pairs || [] ) - setSelectedFontPairName( response.stackable_selected_font_pair || '' ) + setSelectedFontPairName( response.stackable_selected_font_pair || 'theme-heading-default/theme-body-default' ) // Reversely compute the type scale from the font sizes - // Check first if the units are rem - if ( Object.values( _typographySettings ).every( setting => setting.fontSizeUnit === 'rem' ) ) { - let typeScale = _typographySettings?.h6?.fontSize - const computedApplied = getAppliedTypeScale( typeScale ) - + let typeScale = _typographySettings?.h6?.fontSize + if ( typeScale ) { + const computedApplied = getAppliedTypeScale( typeScale ) ?? {} const tags = Object.keys( _typographySettings ) for ( const tag of tags ) { - // If font size mismatch, set typography scale to None / Custom - if ( _typographySettings[ tag ].fontSize !== computedApplied[ tag ].fontSize ) { - typeScale = 1 + // If font size mismatch, set typography scale to Custom + if ( _typographySettings[ tag ]?.fontSize !== computedApplied[ tag ]?.fontSize ) { + typeScale = 'custom' } } - setSelectedTypeScale( typeScale ) } } ) @@ -225,18 +256,23 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', return [ ...FONT_PAIRS, ...customFontPairs ].find( fontPair => fontPair.name === selectedFontPairName ) } - const getAppliedTypeScale = typeScale => ( { - h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, - p: { fontSize: 1, fontSizeUnit: 'rem' }, - '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, - - } ) + const getAppliedTypeScale = value => { + const typeScale = Number( value ) + if ( Number.isNaN( typeScale ) ) { + return + } + return { + h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, + p: { fontSize: 1, fontSizeUnit: 'rem' }, + '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, + } + } const updateTypography = newSettings => { setTypographySettings( newSettings ) @@ -283,21 +319,31 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', } const updateTypeScale = value => { - const typeScale = Number( value ) - if ( isNaN( typeScale ) ) { + setSelectedTypeScale( value ) + + // If value is custom, do not do anything + if ( value === 'custom ' ) { return } - setSelectedTypeScale( typeScale ) - - // Only update the typography settings if not None/Custom - if ( typeScale !== 1 ) { - const newSettings = getAppliedTypeScale( typeScale ) + // If value is none, reset the font sizes and units + if ( value === 'none' ) { + const selectors = TYPOGRAPHY_TAGS.map( tag => tag.selector ) + const newSettings = selectors.reduce( ( acc, selector, ) => { + acc[ selector ] = { fontSize: '', fontSizeUnit: '' } + return acc + }, {} ) changeStyles( newSettings ) + return } + + // If value is valid type scale, apply to the styles + const newSettings = getAppliedTypeScale( value ) + changeStyles( newSettings ) } - const changeStyles = typography => { + const changeStyles = _typography => { + const typography = cloneDeep( _typography ) const newSettings = { ...typographySettings } Object.entries( typography ).forEach( ( [ selector, styles ] ) => { @@ -497,7 +543,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', options={ TYPE_SCALE } value={ selectedTypeScale } onChange={ updateTypeScale } - default={ 1 } + default="none" /> { TYPOGRAPHY_TAGS.map( ( { label, selector, help, @@ -513,13 +559,18 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', isAllowReset={ getIsAllowReset( selector ) } onChange={ styles => { changeStyles( { [ selector ]: styles } ) - // Also set the typescale to None/Custom - setSelectedTypeScale( 1 ) + // Set typeScale to custom when editing font size or units + if ( 'fontSize' in styles || 'fontSizeUnit' in styles ) { + setSelectedTypeScale( 'custom' ) + } } } onReset={ () => { resetStyles( selector ) - // Also set the typescale to None/Custom - setSelectedTypeScale( 1 ) + // Set typeScale to custom when editing font size or units + const styles = typographySettings[ selector ] + if ( 'fontSize' in styles || 'fontSizeUnit' in styles ) { + setSelectedTypeScale( 'custom' ) + } } } /> ) From bbe3dcaab9c12457d8636aba657d6b7b19334d44 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 11:08:29 +0800 Subject: [PATCH 036/124] fix: selecting font pair/type scale overwritting previous changes --- src/plugins/global-settings/typography/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 1e4dcfa0e8..f341ebb733 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -350,6 +350,13 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', if ( ! selector || typeof styles !== 'object' ) { return } + + // Merge the new styles with the previous, while overwritting similar styles. + // This allow adding styles without removing the previous ones. + // Check if the object is empty, used for resetting the whole setting. + if ( Object.keys( styles ).length !== 0 ) { + styles = { ...newSettings[ selector ], ...styles } + } /** * Delete the object keys with empty strings. * Otherwise, the API will throw an error code 400 From c817adaa6d5823d51ccec7b6f583f0fbd26c30e0 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 11:16:12 +0800 Subject: [PATCH 037/124] chore: move other functions to utils --- .../global-settings/typography/index.js | 19 +----------- .../global-settings/typography/utils.js | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 src/plugins/global-settings/typography/utils.js diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index f341ebb733..45c1f47d5a 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -5,6 +5,7 @@ import { GlobalTypographyStyles } from './editor-loader' import TypographyPicker from './typography-picker' import { getThemeStyles } from './get-theme-styles' import FREE_FONT_PAIRS from './font-pairs.json' +import { getAppliedTypeScale } from './utils' /** * External dependencies @@ -256,24 +257,6 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', return [ ...FONT_PAIRS, ...customFontPairs ].find( fontPair => fontPair.name === selectedFontPairName ) } - const getAppliedTypeScale = value => { - const typeScale = Number( value ) - if ( Number.isNaN( typeScale ) ) { - return - } - return { - h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, - p: { fontSize: 1, fontSizeUnit: 'rem' }, - '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, - } - } - const updateTypography = newSettings => { setTypographySettings( newSettings ) diff --git a/src/plugins/global-settings/typography/utils.js b/src/plugins/global-settings/typography/utils.js new file mode 100644 index 0000000000..5e98ee3775 --- /dev/null +++ b/src/plugins/global-settings/typography/utils.js @@ -0,0 +1,29 @@ +/** + * Generates a typographic scale based on the given value. + * + * This function returns an object where each key represents a text element + * (e.g., `h1`, `h2`, `p`, etc.) and its value contains a `fontSize` and `fontSizeUnit`, + * calculated using an exponential scale. + * + * @param {string|number} value - The base number to use for the typographic scale. + * @return {Object|undefined} An object mapping CSS selectors to their corresponding + * font size settings. Returns `undefined` if input is invalid. + */ + +export const getAppliedTypeScale = value => { + const typeScale = Number( value ) + if ( Number.isNaN( typeScale ) ) { + return + } + return { + h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, + p: { fontSize: 1, fontSizeUnit: 'rem' }, + '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, + } +} From 477cfbe809d37eaa910f22373c0c9f55cdd1c218 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 12:21:39 +0800 Subject: [PATCH 038/124] fix: only get the new style for onChange, since changeStyle now handles merging --- src/plugins/global-settings/typography/typography-picker.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/global-settings/typography/typography-picker.js b/src/plugins/global-settings/typography/typography-picker.js index 9b5ae1e7d3..bf949662ef 100644 --- a/src/plugins/global-settings/typography/typography-picker.js +++ b/src/plugins/global-settings/typography/typography-picker.js @@ -20,10 +20,9 @@ import { Dashicon } from '@wordpress/components' const TypographyPicker = props => { const { value, help } = props - // On style change, gather all the styles then trigger the onChange. + // On style change, only get the new style then trigger the onChange. const onChange = ( style, value ) => { const newStyles = { - ...props.value, [ style ]: value, } props.onChange( newStyles ) From 3f4f8ec0c2a12fbdc9e163abd97b06f02f53b31c Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 14:32:52 +0800 Subject: [PATCH 039/124] fix: set the value when changing from mark to custom --- src/components/advanced-range-control/index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 6ef56962b7..62ad2c89c9 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -189,13 +189,12 @@ const AdvancedRangeControl = props => { const [ _value, _unit ] = extractNumberAndUnit( mark.value ) return _value === derivedValue } ) - rangeOnChange = value => { + rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { return _onChange( value ) } - // Extract the unit and value. - const markValue = props.marks[ value ]?.value || '0' + const markValue = props.marks[ value ]?.[ property ] || '0' const [ _newValue, unit ] = extractNumberAndUnit( markValue ) const newValue = _newValue @@ -229,7 +228,13 @@ const AdvancedRangeControl = props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsMarkMode( ! isMarkMode ) } + onClick={ () => { + // Set the value when changing from mark mode to custom + if ( isMarkMode && rangeValue ) { + rangeOnChange( rangeValue, 'size' ) + } + setIsMarkMode( ! isMarkMode ) + } } icon={ settings } > From 0b33f3a43c33971a4c799f6f2160275e3b19a881 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 15:15:01 +0800 Subject: [PATCH 040/124] fix: typography min and max slider for rem --- src/block-components/typography/edit.js | 6 +++--- src/components/advanced-range-control/index.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 3e86c9576c..ea1a00f749 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -265,9 +265,9 @@ export const Controls = props => { allowReset={ true } attribute={ attributeName( 'fontSize' ) } units={ [ 'px', 'em', 'rem' ] } - min={ [ 0, 0 ] } - sliderMax={ [ 150, 7 ] } - step={ [ 1, 0.05 ] } + min={ [ 0, 0, 0 ] } + sliderMax={ [ 150, 7, 7 ] } + step={ [ 1, 0.05, 0.05 ] } placeholder={ props.sizePlaceholder } responsive="all" helpTooltip={ { diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 62ad2c89c9..4de53e3073 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -230,7 +230,7 @@ const AdvancedRangeControl = props => { variant="tertiary" onClick={ () => { // Set the value when changing from mark mode to custom - if ( isMarkMode && rangeValue ) { + if ( isMarkMode && rangeValue !== -1 ) { rangeOnChange( rangeValue, 'size' ) } setIsMarkMode( ! isMarkMode ) From 558d0fefb907505044608b92c04ebf6e9ac337fe Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 6 Apr 2025 20:38:22 +0800 Subject: [PATCH 041/124] fix: use only "var" in detecting CSS variable --- src/block-components/columns/style.js | 8 ++++---- src/block-components/helpers/borders/style.js | 8 ++++---- src/components/advanced-range-control/index.js | 2 +- src/components/block-css/index.js | 2 +- src/components/four-range-control/index.js | 2 +- src/util/styles/style-object.js | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/block-components/columns/style.js b/src/block-components/columns/style.js index 890e5cb1cc..2355a4d16a 100644 --- a/src/block-components/columns/style.js +++ b/src/block-components/columns/style.js @@ -33,7 +33,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { responsive: 'all', valueCallback: value => { // Substitute with using format to work with preset controls - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -46,7 +46,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'columnGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -91,7 +91,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'rowGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -104,7 +104,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'rowGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' diff --git a/src/block-components/helpers/borders/style.js b/src/block-components/helpers/borders/style.js index ab53790d33..d1b9e4a2d6 100644 --- a/src/block-components/helpers/borders/style.js +++ b/src/block-components/helpers/borders/style.js @@ -39,7 +39,7 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { valueCallback: value => { // Substitute with using format to work with preset controls value = typeof value === 'number' ? value.toString() : value - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -57,7 +57,7 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrNameTemplate, valueCallback: value => { value = typeof value === 'number' ? value.toString() : value - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -75,7 +75,7 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrNameTemplate, valueCallback: value => { value = typeof value === 'number' ? value.toString() : value - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -93,7 +93,7 @@ export const addBorderStyles = ( blockStyleGenerator, props = {} ) => { attrNameTemplate, valueCallback: value => { value = typeof value === 'number' ? value.toString() : value - if ( value.startsWith( 'var(--stk' ) ) { + if ( value.startsWith( 'var' ) ) { return value } return value + 'px' diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 4de53e3073..11d3f5b3cf 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -283,7 +283,7 @@ export default memo( AdvancedRangeControl, isEqual ) const extractNumberAndUnit = value => { // Match the last characters that are not numbers. const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches || value.startsWith( 'var(--stk' ) ) { + if ( ! matches || value.startsWith( 'var' ) ) { return [ value, '' ] } return [ matches[ 1 ], matches[ 2 ] ] diff --git a/src/components/block-css/index.js b/src/components/block-css/index.js index 232b687cef..16dbe2e482 100644 --- a/src/components/block-css/index.js +++ b/src/components/block-css/index.js @@ -183,7 +183,7 @@ const BlockCss = props => { if ( unit ) { // Note: this will only work for non-objects. // If the value is `auto` or a CSS variable, don't add units. - if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var(--stk' ) ) ) ) { + if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var' ) ) ) ) { value = `${ value }${ unit }` } } diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index ff011f6490..9171cf18ab 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -59,7 +59,7 @@ const isEqualInitial = ( props, value, firstValue ) => { const extractNumberAndUnit = value => { // Match the last characters that are not numbers. const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches || value.startsWith( 'var(--stk' ) ) { + if ( ! matches || value.startsWith( 'var' ) ) { return [ value, '' ] } return [ matches[ 1 ], matches[ 2 ] ] diff --git a/src/util/styles/style-object.js b/src/util/styles/style-object.js index 28c18f445d..d486dfdab4 100644 --- a/src/util/styles/style-object.js +++ b/src/util/styles/style-object.js @@ -336,7 +336,7 @@ class StyleObject { if ( unit ) { // Note: this will only work for non-objects. // If the value is `auto` or a CSS variable, don't add units. - if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var(--stk' ) ) ) ) { + if ( ! ( value === 'auto' || ( typeof value === 'string' && value.startsWith( 'var' ) ) ) ) { value = `${ value }${ unit }` } } From f74c4888f863f72f802236a88576a8965f899f30 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 7 Apr 2025 11:00:20 +0800 Subject: [PATCH 042/124] fix: FourRangeControl synchronization of markmode; default markmode --- src/components/four-range-control/index.js | 57 ++++++++++++---------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 9171cf18ab..98e40d08b0 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -204,7 +204,7 @@ const FourRangeControl = memo( props => { bottom: !! props.marks, left: !! props.marks, } - if ( props.marks && value ) { + if ( props.marks && firstValue ) { // Check if the current value exsits in the marks const marksUnit = ( props.hasCSSVariableValue ? '' : unit ) isMarkValue.first = isMarkValue.first && props.marks.some( mark => mark.value === firstValue + marksUnit ) @@ -222,6 +222,13 @@ const FourRangeControl = memo( props => { bottom: props.enableBottom ? newValue : value.bottom, left: props.enableLeft ? newValue : value.left, } ) + setIsFourMarkMode( prev => ( { + ...prev, + top: prev.first, + right: prev.first, + bottom: prev.first, + left: prev.first, + } ) ) } const onChangeTop = newValue => { @@ -231,6 +238,7 @@ const FourRangeControl = memo( props => { bottom: value.bottom, left: value.left, } ) + setIsFourMarkMode( prev => ( { ...prev, first: prev.top } ) ) } const onChangeRight = newValue => { @@ -240,6 +248,7 @@ const FourRangeControl = memo( props => { bottom: value.bottom, left: value.left, } ) + setIsFourMarkMode( prev => ( { ...prev, first: prev.right } ) ) } const onChangeBottom = newValue => { @@ -249,6 +258,7 @@ const FourRangeControl = memo( props => { bottom: newValue, left: value.left, } ) + setIsFourMarkMode( prev => ( { ...prev, first: prev.bottom } ) ) } const onChangeLeft = newValue => { @@ -258,6 +268,7 @@ const FourRangeControl = memo( props => { bottom: value.bottom, left: newValue, } ) + setIsFourMarkMode( prev => ( { ...prev, first: prev.left } ) ) } const onChangeVertical = newValue => { @@ -267,6 +278,11 @@ const FourRangeControl = memo( props => { bottom: newValue, left: value.left, } ) + setIsFourMarkMode( prev => ( { + ...prev, + top: prev.top, + bottom: prev.top, + } ) ) } const onChangeHorizontal = newValue => { @@ -276,6 +292,11 @@ const FourRangeControl = memo( props => { bottom: value.bottom, left: newValue, } ) + setIsFourMarkMode( prev => ( { + ...prev, + right: prev.right, + left: prev.right, + } ) ) } // Support for steps. Modify the props to make the range control show steps. const stepSupport = ( isMarkMode, initialValue, initialOnChange ) => { @@ -351,7 +372,7 @@ const FourRangeControl = memo( props => { controlProps.units = isFourMarkMode.first ? false : controlProps.units } else if ( isLocked && props.vhMode ) { - controlProps.units = isFourMarkMode.top && isFourMarkMode.left + controlProps.units = isFourMarkMode.top && isFourMarkMode.right ? false : controlProps.units } else { controlProps.units = isFourMarkMode.top && isFourMarkMode.right && isFourMarkMode.bottom && isFourMarkMode.left @@ -396,8 +417,8 @@ const FourRangeControl = memo( props => { ) const [ propsToPassHorizontal, rangeValueHorizontal, rangeOnChangeHorizontal ] = stepSupport( - isFourMarkMode.left, - value.left, + isFourMarkMode.right, + value.right, onChangeHorizontal, ) @@ -443,9 +464,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, first: ! prev.first } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) } icon={ settings } > @@ -503,9 +522,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, top: ! prev.top } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } icon={ settings } > @@ -559,9 +576,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, left: ! prev.left } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } icon={ settings } > @@ -621,9 +636,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, top: ! prev.top } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } icon={ settings } > @@ -680,9 +693,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, right: ! prev.right } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) } icon={ settings } > @@ -739,9 +750,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, bottom: ! prev.bottom } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) } icon={ settings } > @@ -798,9 +807,7 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => { - return { ...prev, left: ! prev.left } - } ) } + onClick={ () => setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } icon={ settings } > From f86d2102f74b641bfca304ced95811a75d4da085 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 7 Apr 2025 11:17:17 +0800 Subject: [PATCH 043/124] feat: add none presets to padding and margin similar to native --- src/block-components/helpers/size/edit.js | 9 ++++++++- src/block-components/typography/edit.js | 3 ++- src/hooks/use-preset-controls.js | 5 +++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 597fc37126..155e436266 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -139,7 +139,14 @@ const Spacing = props => { highlight: 'margin', } - const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + // Add additional presets for setting margins and paddings to None + const nonePreset = { + name: 'None', + size: '0rem', + slug: 'none', + } + const presetMarks = usePresetControls( 'spacingSizes' ) + ?.getPresetMarks( { additionalPresets: [ nonePreset ] } ) || null return ( <> diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index ea1a00f749..53d7590e92 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -104,7 +104,8 @@ export const Controls = props => { const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] ) - const presetMarks = usePresetControls( 'fontSizes' )?.getPresetMarks( useTypographyAsPresets ) || null + const presetMarks = usePresetControls( 'fontSizes' ) + ?.getPresetMarks( { customOnly: useTypographyAsPresets } ) || null return ( <> diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index ab89fa8e21..9f59668252 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -56,9 +56,10 @@ export const usePresetControls = property => { // Get the merge preset marks with the CSS Variable value // Setting customOnly to true returns the preset marks for custom presets only - const getPresetMarks = ( customOnly = false ) => { + const getPresetMarks = ( { customOnly = false, additionalPresets = [] } = {} ) => { const prefix = PRESET_MAPPING[ property ].prefix - const presets = customOnly ? allCustomPresets[ property ] ?? [] : getMergedPresets() + let presets = customOnly ? allCustomPresets[ property ] ?? [] : getMergedPresets() + presets = [ ...additionalPresets, ...presets ] return presets .filter( preset => ! ( preset?.isDiscarded ) ) From 888ad9b3cfa66f95af5af0c8d5498b9f04b53fe9 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 7 Apr 2025 21:06:36 +0800 Subject: [PATCH 044/124] fix: use extractNumbersAndUnits that can process clamp, min, etc. --- .../advanced-range-control/index.js | 16 +--- src/components/four-range-control/index.js | 78 ++++++++++++------- src/util/index.js | 29 +++++++ 3 files changed, 84 insertions(+), 39 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 11d3f5b3cf..ae6e369174 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -13,6 +13,7 @@ import { useBlockSetAttributesContext, useDeviceType, } from '~stackable/hooks' +import { extractNumbersAndUnits } from '~stackable/util' /** * External dependencies @@ -186,7 +187,7 @@ const AdvancedRangeControl = props => { let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumberAndUnit( mark.value ) + const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] return _value === derivedValue } ) rangeOnChange = ( value, property = 'value' ) => { @@ -195,7 +196,7 @@ const AdvancedRangeControl = props => { } // Extract the unit and value. const markValue = props.marks[ value ]?.[ property ] || '0' - const [ _newValue, unit ] = extractNumberAndUnit( markValue ) + const [ _newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] const newValue = _newValue // Update the unit. @@ -277,14 +278,3 @@ AdvancedRangeControl.defaultProps = { } export default memo( AdvancedRangeControl, isEqual ) - -// The value can be in the format '10px' or '10.0em' or '10rem'. -// Return an array with the number and the unit. -const extractNumberAndUnit = value => { - // Match the last characters that are not numbers. - const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches || value.startsWith( 'var' ) ) { - return [ value, '' ] - } - return [ matches[ 1 ], matches[ 2 ] ] -} diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 98e40d08b0..14d96a11a0 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -18,6 +18,7 @@ import RangeControl from '../advanced-range-control/range-control' import { ResetButton } from '../base-control2/reset-button' import AdvancedControl, { extractControlProps } from '../base-control2' import { useControlHandlers } from '../base-control2/hooks' +import { extractNumbersAndUnits } from '~stackable/util' /** * WordPress dependencies @@ -54,17 +55,6 @@ const isEqualInitial = ( props, value, firstValue ) => { return isEqual } -// The value can be in the format '10px' or '10.0em' or '10rem'. -// Return an array with the number and the unit. -const extractNumberAndUnit = value => { - // Match the last characters that are not numbers. - const matches = value.match( /([\d.]+)(\D*)$/ ) - if ( ! matches || value.startsWith( 'var' ) ) { - return [ value, '' ] - } - return [ matches[ 1 ], matches[ 2 ] ] -} - const FourRangeControl = memo( props => { const [ _value, _onChange ] = useControlHandlers( props.attribute, props.responsive, props.hover, props.valueCallback, props.changeCallback ) const [ propsToPass, controlProps ] = extractControlProps( props ) @@ -294,8 +284,8 @@ const FourRangeControl = memo( props => { } ) setIsFourMarkMode( prev => ( { ...prev, - right: prev.right, - left: prev.right, + right: prev.left, + left: prev.left, } ) ) } // Support for steps. Modify the props to make the range control show steps. @@ -341,17 +331,17 @@ const FourRangeControl = memo( props => { let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumberAndUnit( mark.value ) + const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] return _value === initialValue } ) - rangeOnChange = value => { + rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { return initialOnChange( value ) } // Extract the unit and value. - const markValue = props.marks[ value ]?.value || '0' - const [ _newValue, unit ] = extractNumberAndUnit( markValue ) + const markValue = props.marks[ value ]?.[ property ] || '0' + const [ _newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] const newValue = _newValue // Update the unit. @@ -417,8 +407,8 @@ const FourRangeControl = memo( props => { ) const [ propsToPassHorizontal, rangeValueHorizontal, rangeOnChangeHorizontal ] = stepSupport( - isFourMarkMode.right, - value.right, + isFourMarkMode.left, + value.left, onChangeHorizontal, ) @@ -464,7 +454,13 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) } + onClick={ () => { + // Set the value when changing from mark mode to custom + if ( isFourMarkMode.first && rangeValueFirst !== -1 ) { + rangeOnChangeFirst( rangeValueFirst, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) + } } icon={ settings } > @@ -522,7 +518,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } + onClick={ () => { + if ( isFourMarkMode.top && rangeValueTop !== -1 ) { + rangeOnChangeTop( rangeValueTop, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) + } } icon={ settings } > @@ -576,7 +577,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } + onClick={ () => { + if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { + rangeOnChangeLeft( rangeValueLeft, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) + } } icon={ settings } > @@ -636,7 +642,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } + onClick={ () => { + if ( isFourMarkMode.top && rangeValueTop !== -1 ) { + rangeOnChangeTop( rangeValueTop, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) + } } icon={ settings } > @@ -693,7 +704,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) } + onClick={ () => { + if ( isFourMarkMode.right && rangeValueRight !== -1 ) { + rangeOnChangeRight( rangeValueRight, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) + } } icon={ settings } > @@ -750,7 +766,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) } + onClick={ () => { + if ( isFourMarkMode.bottom && rangeValueBottom !== -1 ) { + rangeOnChangeBottom( rangeValueBottom, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) + } } icon={ settings } > @@ -807,7 +828,12 @@ const FourRangeControl = memo( props => { className="stk-range-control__custom-button" size="small" variant="tertiary" - onClick={ () => setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } + onClick={ () => { + if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { + rangeOnChangeLeft( rangeValueLeft, 'size' ) + } + setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) + } } icon={ settings } > diff --git a/src/util/index.js b/src/util/index.js index 3d011c095c..08f02913e8 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -388,3 +388,32 @@ export const createUniqueClass = uid => `${ uid.substring( 0, 7 ) }` export const semverCompare = ( version1, operator, version2 ) => { return compare( version1, version2, operator ) } + +/** + * Extracts all number-unit pairs from a CSS value. + * + * @param { string } value - The CSS value to extract from. + * @return { Array } An array of tuples, each containing a number and its corresponding unit. + * + * @example + * extractNumbersAndUnits( "min(1.5rem, 2vw)" ) + * // Returns: [["1.5", "rem"], ["2", "vw"]] + */ +export const extractNumbersAndUnits = value => { + if ( value.startsWith( 'var' ) ) { + return [ [ value, '' ] ] + } + // Match numbers followed by a unit, including decimals and negative values. + const regex = /(-?\d*\.?\d+)([a-zA-Z%]*)/g + const matches = [ ...value.matchAll( regex ) ] + + if ( matches.length ) { + return matches.map( match => [ match[ 1 ], match[ 2 ] || 'px' ] ) + } + + // If the input is purely numeric (e.g., "10"), assume "px" + if ( /^-?\d*\.?\d+$/.test( value ) ) { + return [ [ value, 'px' ] ] + } + return [ [ '0', 'px' ] ] +} From ea5101e79438a9cbe5ffe2028c29d7b21476d7e9 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 7 Apr 2025 21:38:58 +0800 Subject: [PATCH 045/124] fix: add rem support to allow custom mode to have values when switching, convert from rem if only has px support --- src/block-components/helpers/size/edit.js | 20 +++++++++---------- .../advanced-range-control/index.js | 10 ++++++++-- src/components/four-range-control/index.js | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/block-components/helpers/size/edit.js b/src/block-components/helpers/size/edit.js index 155e436266..86581cea3d 100644 --- a/src/block-components/helpers/size/edit.js +++ b/src/block-components/helpers/size/edit.js @@ -40,10 +40,10 @@ const Layout = props => { label={ labelHeight } attribute={ getAttrName( 'height' ) } responsive="all" - units={ [ 'px', 'vh' ] } - min={ [ 0, 0 ] } - sliderMax={ [ 1000, 100 ] } - step={ [ 1, 1 ] } + units={ [ 'px', 'rem', 'vh' ] } + min={ [ 0, 0, 0 ] } + sliderMax={ [ 1000, 60, 100 ] } + step={ [ 1, 1, 1 ] } allowReset={ true } placeholder="0" helpTooltip={ { @@ -155,10 +155,10 @@ const Spacing = props => { attribute={ getAttrName( 'padding' ) } responsive="all" hover="all" - units={ [ 'px', 'em', '%' ] } + units={ [ 'px', 'em', 'rem', '%' ] } defaultLocked={ true } - min={ [ 0, 0, 0 ] } - sliderMax={ [ 200, 30, 100 ] } + min={ [ 0, 0, 0, 0 ] } + sliderMax={ [ 200, 30, 30, 100 ] } helpTooltip={ { video: 'inner-block-padding', description: __( 'Sets the block paddings, i.e the space between the inner columns and the block border', i18n ), @@ -174,10 +174,10 @@ const Spacing = props => { label={ labelMargins } attribute={ getAttrName( 'margin' ) } responsive="all" - units={ [ 'px', '%' ] } + units={ [ 'px', 'rem', '%' ] } defaultLocked={ false } - sliderMin={ [ -200, -100 ] } - sliderMax={ [ 200, 100 ] } + sliderMin={ [ -200, -15, -100 ] } + sliderMax={ [ 200, 15, 100 ] } placeholder="0" helpTooltip={ { video: 'advanced-block-margin', diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index ae6e369174..ba5b60cff3 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -196,8 +196,14 @@ const AdvancedRangeControl = props => { } // Extract the unit and value. const markValue = props.marks[ value ]?.[ property ] || '0' - const [ _newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] - const newValue = _newValue + let [ newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] + + // If the attribute has no units (only support px), and the + // preset units are rem or em, convert to px + if ( ! hasUnits && ( unit === 'rem' || unit === 'em' ) ) { + newValue = `${ parseFloat( newValue ) * 16 }` + unit = 'px' + } // Update the unit. if ( unit ) { diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 14d96a11a0..6e9aee60ea 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -327,7 +327,7 @@ const FourRangeControl = memo( props => { } // We need to change the way we handle the value and onChange if we're doing marks - let rangeValue = initialValue + let rangeValue = props.hasCSSVariableValue ? parseFloat( initialValue ) : initialValue let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { From ae3813ae48defaeea34c8c165d633fe43eb1216e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 8 Apr 2025 11:39:38 +0800 Subject: [PATCH 046/124] feat: support mimic/clamping by using WP generated presets --- src/hooks/use-preset-controls.js | 2 +- .../preset-controls/editor-loader.js | 2 +- .../global-settings/preset-controls/index.php | 38 +++++++++++++------ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/hooks/use-preset-controls.js b/src/hooks/use-preset-controls.js index 9f59668252..d2e3cc69df 100644 --- a/src/hooks/use-preset-controls.js +++ b/src/hooks/use-preset-controls.js @@ -9,7 +9,7 @@ const PRESET_MAPPING = { }, spacingSizes: { settings: [ 'spacing', 'spacingSizes' ], - prefix: 'spacing-size', + prefix: 'spacing', }, blockHeights: { settings: [ 'blockHeights' ], diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js index d66aae36fd..20170436bc 100644 --- a/src/plugins/global-settings/preset-controls/editor-loader.js +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -14,7 +14,7 @@ const PRESET_MAPPING = { prefix: 'font-size', }, spacingSizes: { - prefix: 'spacing-size', + prefix: 'spacing', }, blockHeights: { prefix: 'block-height', diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 111d58bcb9..6c33b5583d 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -21,7 +21,7 @@ class Stackable_Size_And_Spacing_Preset_Controls { ), 'spacingSizes' => array( 'settings' => array( 'spacing', 'spacingSizes' ), - 'prefix' => 'spacing-size', + 'prefix' => 'spacing', ), 'blockHeights' => array( 'settings' => array( 'blockHeights' ), @@ -71,27 +71,41 @@ private function load_presets( $json_path ) { * @param array $property * @param array $presets * @param array $prefix + * @param bool $isTheme * @return mixed */ - public function generate_css_variables( $property, $presets, $prefix ) { + public function generate_css_variables( $property, $presets, $prefix, $isTheme = false ) { + $filter_name = current_filter(); $custom_presets = $this->custom_presets[ $property ] ?? []; $css = ""; - // Convert presets into an associative array with key 'slug' + $presets_by_slug = []; + // Convert presets into an associative array with key 'slug' foreach ( $presets as $preset ) { $presets_by_slug[ $preset[ 'slug' ] ] = $preset; } - // Override values in base presets if it exist in custom presets - foreach ( $custom_presets as $custom ) { - $presets_by_slug[ $custom[ 'slug' ] ] = $custom; + + if ( $filter_name !== 'stackable_inline_editor_styles' ) { + // Override values in base presets if it exist in custom presets + foreach ( $custom_presets as $custom ) { + $custom[ '__is_custom' ] = true; + $presets_by_slug[ $custom[ 'slug' ] ] = $custom; + } } - - foreach ( $presets_by_slug as $preset ) { - $slug = $preset[ 'slug' ]; - $size = $preset[ 'size' ]; - $css .= "--stk--preset--$prefix--{$preset['slug']}: {$preset['size']};\n"; + + // If custom presets or using stackable presets, use the given size. + // If using theme presets, use WP generated --wp-preset to support theme.json specific + // configuration (fluid, clamping, etc.) + foreach ( $presets_by_slug as $slug => $preset ) { + $is_custom = $preset[ '__is_custom' ] ?? false; + + $value = $is_custom || ! $isTheme + ? $preset['size'] + : "var(--wp--preset--$prefix--$slug)"; + + $css .= "--stk--preset--$prefix--$slug: $value;\n"; } return $css; @@ -124,12 +138,14 @@ public function add_preset_controls_styles( $current_css ) { $key, $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ], $value[ 'prefix' ], + true ); } elseif ( ! empty( $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ] ) ) { $generated_css .= $this->generate_css_variables( $key, $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ], $value[ 'prefix' ], + true ); } else { $generated_css .= $this->generate_css_variables( From 9a558c8f1c37fcd8a32849acfc45e46399e58796 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 9 Apr 2025 18:10:41 +0800 Subject: [PATCH 047/124] fix: global preset control title --- src/components/pro-control/index.js | 5 +++-- src/plugins/global-settings/preset-controls/index.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/pro-control/index.js b/src/components/pro-control/index.js index 23a8ad0634..a15a7ed4ec 100644 --- a/src/components/pro-control/index.js +++ b/src/components/pro-control/index.js @@ -134,9 +134,10 @@ const LABELS = { , }, 'preset-controls': { - title: __( 'Customize the Preset Controls', i18n ), + title: __( 'Premium Preset Controls', i18n ), description:
      -
    • { __( 'Customize your own Preset Controls', i18n ) }
    • +
    • { __( 'Customize your own presets', i18n ) }
    • +
    • { __( 'Use Global Typography sizes as presets', i18n ) }
    , }, } diff --git a/src/plugins/global-settings/preset-controls/index.js b/src/plugins/global-settings/preset-controls/index.js index 1a042a4e91..b6681180fa 100644 --- a/src/plugins/global-settings/preset-controls/index.js +++ b/src/plugins/global-settings/preset-controls/index.js @@ -31,7 +31,7 @@ if ( showProNotice || isPro ) { { output } From 054216e8e70e49476da82f4d21672b9b92e04681 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 10 Apr 2025 11:40:07 +0800 Subject: [PATCH 048/124] feat: apply body to html --- src/global-settings.php | 28 ++++++++++++++++--- .../typography/editor-loader.js | 27 ++++++++++++------ .../global-settings/typography/index.js | 28 +++++++++++++++++-- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/global-settings.php b/src/global-settings.php index 812e9fccc0..73fc07e39b 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -424,6 +424,18 @@ public function register_global_settings() { ) ); + register_setting( + 'stackable_global_settings', + 'stackable_is_apply_body_to_html', + array( + 'type' => 'boolean', + 'description' => __( 'Stackable global typography apply to setting', STACKABLE_I18N ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => '', + ) + ); + register_setting( 'stackable_global_settings', 'stackable_icon_library', @@ -650,11 +662,19 @@ public function form_tag_selector( $tag ) { } public function form_paragraph_selector() { - return array_merge( - $this->form_tag_selector( 'p' ), // Core text. - $this->form_tag_selector( 'li' ), // Core lists. - $this->form_tag_selector( 'td' ) // Core table cells. + $is_apply_body_to_html = get_option( 'stackable_is_apply_body_to_html' ) ?? false; + $selectors = array_merge( + $this->form_tag_selector( 'p' ), // Core text + $this->form_tag_selector( 'li' ), // Core lists + $this->form_tag_selector( 'td' ) // Core table cells ); + + // Add 'html' only if is_apply_body_to_html is true + if ( $is_apply_body_to_html ) { + $selectors[] = 'html'; + } + + return $selectors; } /** diff --git a/src/plugins/global-settings/typography/editor-loader.js b/src/plugins/global-settings/typography/editor-loader.js index 6c7f39da55..93413f77f0 100644 --- a/src/plugins/global-settings/typography/editor-loader.js +++ b/src/plugins/global-settings/typography/editor-loader.js @@ -29,6 +29,7 @@ import { useSelect } from '@wordpress/data' export const GlobalTypographyStyles = () => { const [ typographySettings, setTypographySettings ] = useState( [] ) const [ applySettingsTo, setApplySettingsTo ] = useState( '' ) + const [ isApplyBodyToHTML, setIsApplyBodyToHTML ] = useState( false ) // These are for debouncing the style generation to make things faster. const [ styles, setStyles ] = useState( '' ) @@ -54,12 +55,16 @@ export const GlobalTypographyStyles = () => { fetchSettings().then( response => { setTypographySettings( ( head( response.stackable_global_typography ) ) || {} ) setApplySettingsTo( response.stackable_global_typography_apply_to || 'blocks-stackable-native' ) + setIsApplyBodyToHTML( response.stackable_is_apply_body_to_html || false ) } ) // Allow actions to trigger styles to update. - addAction( 'stackable.global-settings.typography.update-trigger', 'stackable/typography-styles', ( newTypographySettings, newAapplySettingsTo ) => { + addAction( 'stackable.global-settings.typography.update-trigger', 'stackable/typography-styles', ( + newTypographySettings, newAapplySettingsTo, newIsApplyBodyToHTML + ) => { setTypographySettings( newTypographySettings ) setApplySettingsTo( newAapplySettingsTo ) + setIsApplyBodyToHTML( newIsApplyBodyToHTML ) } ) return () => { removeAction( 'stackable.global-settings.typography.update-trigger', 'stackable/typography-styles' ) @@ -71,7 +76,7 @@ export const GlobalTypographyStyles = () => { addAction( 'stackable.global-settings.typography-update-global-styles', 'stackable/typography-styles', typographySettings => { // Generate all the typography styles. const styleObject = Object.keys( typographySettings ).map( tag => { - const selectors = formSelectors( tag, applySettingsTo ) + const selectors = formSelectors( tag, applySettingsTo, isApplyBodyToHTML ) // Build our selector, target h2.block or .block h2. // Some blocks may output the heading tag right away. @@ -118,16 +123,16 @@ export const GlobalTypographyStyles = () => { setStyleTimeout( setTimeout( () => doAction( 'stackable.global-settings.typography-update-global-styles', typographySettings ), 200 ) ) return () => removeAction( 'stackable.global-settings.typography-update-global-styles', 'stackable/typography-styles' ) - }, [ JSON.stringify( typographySettings ), applySettingsTo, device, editorMode ] ) + }, [ JSON.stringify( typographySettings ), applySettingsTo, isApplyBodyToHTML, device, editorMode ] ) return styles } -export const formSelectors = ( selector, applyTo ) => { +export const formSelectors = ( selector, applyTo, isApplyBodyToHTML ) => { if ( [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ].includes( selector ) || selector.startsWith( '.' ) ) { return formClassOrTagSelectors( selector, applyTo ) } - return formParagraphSelectors( applyTo ) + return formParagraphSelectors( applyTo, isApplyBodyToHTML ) } export const formClassOrTagSelectors = ( selector, applyTo ) => { @@ -152,8 +157,8 @@ export const formClassOrTagSelectors = ( selector, applyTo ) => { return applyFilters( 'stackable.global-settings.typography-selectors', selectors, selector ) } -export const formParagraphSelectors = applyTo => { - return applyFilters( 'stackable.global-settings.typography-selectors', [ +export const formParagraphSelectors = ( applyTo, isApplyBodyToHTML ) => { + const selectors = [ ...formClassOrTagSelectors( 'p', applyTo ), ...formClassOrTagSelectors( 'li', applyTo ), `.editor-styles-wrapper p.block-editor-block-list__block[data-type^="core/"]`, @@ -162,7 +167,13 @@ export const formParagraphSelectors = applyTo => { `.editor-styles-wrapper .block-editor-block-list__block[data-type^="core/"] td`, // Apply the font styles to the content placeholder text when the post is blank. '.block-editor-default-block-appender.has-visible-prompt', - ], '' ) + ] + + // Add 'html' only if is_apply_body_to_html is true + if ( isApplyBodyToHTML ) { + selectors.push( 'html' ) + } + return applyFilters( 'stackable.global-settings.typography-selectors', selectors, '' ) } // Make sure that this isn't applied to the native query loop block, otherwise diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 45c1f47d5a..a559433e7d 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -11,7 +11,7 @@ import { getAppliedTypeScale } from './utils' * External dependencies */ import { - PanelAdvancedSettings, AdvancedSelectControl, ControlSeparator, FontPairPicker, ProControlButton, + PanelAdvancedSettings, AdvancedSelectControl, ControlSeparator, FontPairPicker, ProControlButton, AdvancedToggleControl, } from '~stackable/components' import { fetchSettings, getDefaultFontSize } from '~stackable/util' import { @@ -160,6 +160,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const [ selectedFontPairName, setSelectedFontPairName ] = useState( '' ) const [ isEditingFontPair, setIsEditingFontPair ] = useState( false ) const [ selectedTypeScale, setSelectedTypeScale ] = useState( 'none' ) + const [ isApplyBodyToHTML, setIsApplyBodyToHTML ] = useState( false ) const fontPairContainerRef = useRef( null ) @@ -171,6 +172,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', setApplySettingsTo( response.stackable_global_typography_apply_to || 'blocks-stackable-native' ) setCustomFontPairs( response.stackable_custom_font_pairs || [] ) setSelectedFontPairName( response.stackable_selected_font_pair || 'theme-heading-default/theme-body-default' ) + setIsApplyBodyToHTML( response.stackable_is_apply_body_to_html || false ) // Reversely compute the type scale from the font sizes let typeScale = _typographySettings?.h6?.fontSize @@ -190,7 +192,12 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', useEffect( () => { // When typography styles are changed, trigger our editor style generator to update. - doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo ) + doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo, isApplyBodyToHTML ) + }, [ JSON.stringify( typographySettings ), applySettingsTo, isApplyBodyToHTML ] ) + + useEffect( () => { + // When typography styles are changed, trigger our editor style generator to update. + doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo, isApplyBodyToHTML ) // Update the custom presets when using typography as presets if ( useTypographyAsPresets ) { @@ -235,7 +242,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', dispatch( 'stackable/global-preset-controls.custom' ).updateCustomPresetControls( newSettings ) } - }, [ JSON.stringify( typographySettings ), applySettingsTo, useTypographyAsPresets ] ) + }, [ JSON.stringify( typographySettings ), useTypographyAsPresets ] ) // Scroll to the selected font pair when Global Typography tab is toggled useEffect( () => { @@ -301,6 +308,14 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', model.save() } + const changeIsApplyBodyToHTML = value => { + setIsApplyBodyToHTML( value ) + const model = new models.Settings( { + stackable_is_apply_body_to_html: value, // eslint-disable-line + } ) + model.save() + } + const updateTypeScale = value => { setSelectedTypeScale( value ) @@ -587,6 +602,13 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', getIsAllowReset={ getIsAllowReset } /> } +
    ) From f912ffe02dded0bd57ed432e2c16f6fbfbca28fa Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 10 Apr 2025 13:44:42 +0800 Subject: [PATCH 049/124] fix: add margin to apply html toggle --- src/plugins/global-settings/typography/editor.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/global-settings/typography/editor.scss b/src/plugins/global-settings/typography/editor.scss index 6715415711..418d565c26 100644 --- a/src/plugins/global-settings/typography/editor.scss +++ b/src/plugins/global-settings/typography/editor.scss @@ -137,4 +137,7 @@ .stk-inspector-sub-header { margin-bottom: 16px; } + .stk-toggle-control { + margin-top: 28px; + } } From 1ec5add451675be44fd308d91c663b2c34608508 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 11 Apr 2025 12:41:35 +0800 Subject: [PATCH 050/124] fix: remove hasCSSVariableValue, always assume that marks has string value --- src/components/advanced-range-control/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index ba5b60cff3..17c7039821 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -114,11 +114,12 @@ const AdvancedRangeControl = props => { } const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) - // If this supports dynamic content and can have CSS variables, the value should be saved as a String. + // If this supports dynamic content, the value should be saved as a String. + // Similar if using marks to accomodate CSS variable // Important, the attribute type for this option should be a string. const _onChange = value => { const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange - let newValue = props.isDynamic || props.hasCSSVariableValue ? value.toString() : value + let newValue = props.isDynamic || props.marks ? value.toString() : value // On reset, allow overriding the value. if ( newValue === '' ) { @@ -183,7 +184,7 @@ const AdvancedRangeControl = props => { // We need to change the way we handle the value and onChange if we're doing marks // Convert to float if the attribute is string to work with the slider - let rangeValue = propsToPass.isDynamic || props.hasCSSVariableValue ? parseFloat( derivedValue ) : derivedValue + let rangeValue = propsToPass.isDynamic || props.marks ? parseFloat( derivedValue ) : derivedValue let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { @@ -277,9 +278,8 @@ AdvancedRangeControl.defaultProps = { onOverrideReset: undefined, forcePlaceholder: false, - marks: undefined, // [{ value: '14px', name: 'S' }, { value: '16px', name: 'M' }] + marks: undefined, // [{ value: 'var(--stk-preset-font-size-small', name: 'S' }] allowCustom: true, - hasCSSVariableValue: false, // If the attribute can have CSS variable value (string attribute) isCustomPreset: false, } From 5ff46022bd58e68d2128f84ac60b2cfce5b060b2 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 11 Apr 2025 12:47:48 +0800 Subject: [PATCH 051/124] fix: get_option is never null --- src/global-settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global-settings.php b/src/global-settings.php index 73fc07e39b..6dfa1813f2 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -662,7 +662,6 @@ public function form_tag_selector( $tag ) { } public function form_paragraph_selector() { - $is_apply_body_to_html = get_option( 'stackable_is_apply_body_to_html' ) ?? false; $selectors = array_merge( $this->form_tag_selector( 'p' ), // Core text $this->form_tag_selector( 'li' ), // Core lists @@ -670,6 +669,7 @@ public function form_paragraph_selector() { ); // Add 'html' only if is_apply_body_to_html is true + $is_apply_body_to_html = get_option( 'stackable_is_apply_body_to_html' ); if ( $is_apply_body_to_html ) { $selectors[] = 'html'; } From c6d157a962a2ee746c44dd4327916b9fb7c01e1c Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 11 Apr 2025 12:56:16 +0800 Subject: [PATCH 052/124] fix: use wp_json_file_decode --- src/plugins/global-settings/preset-controls/index.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 6c33b5583d..3bfa83c2ea 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -45,7 +45,7 @@ function __construct() { $this->custom_presets = get_option( 'stackable_global_custom_preset_controls' ); $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); $this->default_presets = WP_Theme_JSON_Resolver::get_core_data()->get_settings(); - $this->stackable_presets = $this->load_presets( __DIR__ . '/presets.json'); + $this->stackable_presets = $this->load_json_file( __DIR__ . '/presets.json'); add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); @@ -55,10 +55,11 @@ public static function sanitize_array_setting( $input ) { return ! is_array( $input ) ? array( array() ) : $input; } - private function load_presets( $json_path ) { + private function load_json_file( $json_path ) { if ( file_exists( $json_path ) ) { - $json_data = file_get_contents( $json_path ); - $decoded_data = json_decode( $json_data, true ); + $decoded_data = wp_json_file_decode( $json_path, [ + 'associative' => true, + ] ); return $decoded_data[ 'settings' ] ?? []; } return []; From 07460776fafb3f908020893d77d80fec4c1e046c Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 11 Apr 2025 13:02:08 +0800 Subject: [PATCH 053/124] fix: remove JSON stringify since customPresets is just an array --- src/plugins/global-settings/preset-controls/editor-loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/global-settings/preset-controls/editor-loader.js b/src/plugins/global-settings/preset-controls/editor-loader.js index 20170436bc..25042dff93 100644 --- a/src/plugins/global-settings/preset-controls/editor-loader.js +++ b/src/plugins/global-settings/preset-controls/editor-loader.js @@ -51,7 +51,7 @@ export const GlobalPresetControlsStyles = () => { if ( customPresets && typeof customPresets === 'object' ) { renderGlobalStyles( customPresets, setStyles ) } - }, [ JSON.stringify( customPresets ) ] ) + }, [ customPresets ] ) return styles } From 3c0c069c416dd5e5bac0233c925e0abc9f78cc8d Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 11 Apr 2025 23:26:19 +0800 Subject: [PATCH 054/124] fix: separate loading of presets in another function --- src/plugins/global-settings/preset-controls/index.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 3bfa83c2ea..ff3f0e0310 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -42,13 +42,15 @@ class Stackable_Size_And_Spacing_Preset_Controls { * Initialize */ function __construct() { + add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); + add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); + } + + public function load_presets() { $this->custom_presets = get_option( 'stackable_global_custom_preset_controls' ); $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); $this->default_presets = WP_Theme_JSON_Resolver::get_core_data()->get_settings(); $this->stackable_presets = $this->load_json_file( __DIR__ . '/presets.json'); - - add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); - add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); } public static function sanitize_array_setting( $input ) { @@ -130,6 +132,8 @@ public function deepGet( $array, $keys ) { * @return String */ public function add_preset_controls_styles( $current_css ) { + $this->load_presets(); + $generated_css = "\n/* Global Preset Controls */\n"; $generated_css .= ":root {\n"; From 2d1512fb0198c1bc0a4915f46f612fdf3748ec15 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sat, 12 Apr 2025 00:12:34 +0800 Subject: [PATCH 055/124] fix: use wp_style_engine_get_stylesheet_from_css_rules to generate styles --- .../global-settings/preset-controls/index.php | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index ff3f0e0310..d1da2976ca 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -68,7 +68,7 @@ private function load_json_file( $json_path ) { } /** - * Generate CSS variables based on the property (e.g., fontSizes, spacing). + * Generate CSS variable style defintions based on the property (e.g., fontSizes, spacing). * The given presets will be overriden it match with a preset from custom. * * @param array $property @@ -77,19 +77,18 @@ private function load_json_file( $json_path ) { * @param bool $isTheme * @return mixed */ - public function generate_css_variables( $property, $presets, $prefix, $isTheme = false ) { + public function generate_css_variables_styles( $property, $presets, $prefix, $isTheme = false ) { $filter_name = current_filter(); $custom_presets = $this->custom_presets[ $property ] ?? []; - - $css = ""; - $presets_by_slug = []; // Convert presets into an associative array with key 'slug' foreach ( $presets as $preset ) { $presets_by_slug[ $preset[ 'slug' ] ] = $preset; } + // There is no need to generate custom presets in the editor. + // The custom presets are generated dynamically. if ( $filter_name !== 'stackable_inline_editor_styles' ) { // Override values in base presets if it exist in custom presets foreach ( $custom_presets as $custom ) { @@ -98,20 +97,25 @@ public function generate_css_variables( $property, $presets, $prefix, $isTheme = } } + // Build the CSS variables array. // If custom presets or using stackable presets, use the given size. // If using theme presets, use WP generated --wp-preset to support theme.json specific // configuration (fluid, clamping, etc.) + $css_vars = []; foreach ( $presets_by_slug as $slug => $preset ) { - $is_custom = $preset[ '__is_custom' ] ?? false; + $is_custom = $preset['__is_custom'] ?? false; $value = $is_custom || ! $isTheme ? $preset['size'] : "var(--wp--preset--$prefix--$slug)"; - $css .= "--stk--preset--$prefix--$slug: $value;\n"; + $css_vars[ "--stk--preset--$prefix--$slug" ] = $value; } - return $css; + return array( + 'selector' => ':root', + 'declarations' => $css_vars, + ); } /** @@ -133,40 +137,42 @@ public function deepGet( $array, $keys ) { */ public function add_preset_controls_styles( $current_css ) { $this->load_presets(); - - $generated_css = "\n/* Global Preset Controls */\n"; - $generated_css .= ":root {\n"; + $generated_styles = array(); foreach ( self::PRESET_MAPPING as $key => $value ) { if ( ! empty( $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ] ) ) { - $generated_css .= $this->generate_css_variables( + $styles = $this->generate_css_variables_styles( $key, $this->deepGet( $this->theme_presets, $value[ 'settings' ] )[ 'theme' ], $value[ 'prefix' ], true ); + $generated_styles[] = $styles; + } elseif ( ! empty( $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ] ) ) { - $generated_css .= $this->generate_css_variables( + $styles = $this->generate_css_variables_styles( $key, $this->deepGet( $this->default_presets, $value[ 'settings' ] )[ 'default' ], $value[ 'prefix' ], true ); + $generated_styles[] = $styles; } else { - $generated_css .= $this->generate_css_variables( + $styles = $this->generate_css_variables_styles( $key, $this->deepGet( $this->stackable_presets, $value[ 'settings' ] ), $value[ 'prefix' ], ); + $generated_styles[] = $styles; } } - $generated_css .= "}\n"; - + $generated_css = wp_style_engine_get_stylesheet_from_css_rules( $generated_styles ); if ( ! $generated_css ) { return $current_css; } - + + $current_css .= "\n/* Global Preset Controls */\n"; $current_css .= $generated_css; return apply_filters( 'stackable_frontend_css' , $current_css ); From c97950959250341b5c98d576d3bbc25dff13f8ba Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sat, 12 Apr 2025 00:19:20 +0800 Subject: [PATCH 056/124] fix: translate only the words --- .../global-settings/preset-controls/index.php | 2 +- src/plugins/global-settings/typography/index.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index d1da2976ca..682ea3baa9 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -68,7 +68,7 @@ private function load_json_file( $json_path ) { } /** - * Generate CSS variable style defintions based on the property (e.g., fontSizes, spacing). + * Generate CSS variable style definitions based on the property (e.g., fontSizes, spacing). * The given presets will be overriden it match with a preset from custom. * * @param array $property diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index a559433e7d..5c608f685a 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -106,35 +106,35 @@ const TYPE_SCALE = [ disabled: true, }, { - label: __( '1.067 - Minor Second', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.067', __( 'Minor Second', i18n ) ), value: '1.067', }, { - label: __( '1.125 - Major Second', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.125', __( 'Major Second', i18n ) ), value: '1.125', }, { - label: __( '1.200 - Minor Third', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.2', __( 'Minor Third', i18n ) ), value: '1.2', }, { - label: __( '1.250 - Major Third', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.25', __( 'Major Third', i18n ) ), value: '1.25', }, { - label: __( '1.333 - Perfect Fourth', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.333', __( 'Perfect Fourth', i18n ) ), value: '1.333', }, { - label: __( '1.414 - Augmented Fourth', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.414', __( 'Augmented Fourth', i18n ) ), value: '1.414', }, { - label: __( '1.500 - Perfect Fifth', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.5', __( 'Perfect Fifth', i18n ) ), value: '1.5', }, { - label: __( '1.618 - Golden Ratio', i18n ), + label: sprintf( __( '%s - %s', i18n ), '1.618', __( 'Golden Ratio', i18n ) ), value: '1.618', }, ] From 17ae06e70a246cd6c59208aac5979c3626aa1651 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sat, 12 Apr 2025 14:35:06 +0800 Subject: [PATCH 057/124] fix: move updating presets with typography to editor loader --- .../typography/editor-loader.js | 65 +++++++++++++++++- .../global-settings/typography/index.js | 66 +++---------------- 2 files changed, 72 insertions(+), 59 deletions(-) diff --git a/src/plugins/global-settings/typography/editor-loader.js b/src/plugins/global-settings/typography/editor-loader.js index 93413f77f0..29e56a05e0 100644 --- a/src/plugins/global-settings/typography/editor-loader.js +++ b/src/plugins/global-settings/typography/editor-loader.js @@ -1,6 +1,9 @@ /** * Loads all the typography styles for the blocks in the editor. */ +/** + * Internal dependencies + */ /** * External dependencies @@ -9,6 +12,7 @@ import { createTypographyStyles, fetchSettings, loadGoogleFont, + getDefaultFontSize, } from '~stackable/util' import { generateStyles } from '~stackable/block-components' import deepmerge from 'deepmerge' @@ -24,9 +28,17 @@ import { import { addAction, removeAction, applyFilters, doAction, addFilter, } from '@wordpress/hooks' -import { useSelect } from '@wordpress/data' +import { useSelect, dispatch } from '@wordpress/data' +import { models } from '@wordpress/api' + +let saveTypographyAsPresetsThrottle = null export const GlobalTypographyStyles = () => { + const { allCustomPresets } = useSelect( select => { + const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() ?? {} + return { allCustomPresets: { ..._customPresetControls } } + }, [] ) + const [ typographySettings, setTypographySettings ] = useState( [] ) const [ applySettingsTo, setApplySettingsTo ] = useState( '' ) const [ isApplyBodyToHTML, setIsApplyBodyToHTML ] = useState( false ) @@ -50,6 +62,53 @@ export const GlobalTypographyStyles = () => { return select( 'core/edit-site' )?.getEditorMode() || select( 'core/edit-post' )?.getEditorMode() } ) + // Update the custom presets when using typography as presets + const updatePresetsWithTypography = ( useTypographyAsPresets, typographySettings, tags ) => { + if ( useTypographyAsPresets ) { + const fontSizePresets = tags + .filter( ( { presetSlug } ) => !! presetSlug ) + .map( ( { + selector, presetName, presetSlug, + } ) => { + // If no fontSize available, the unit should default to px + const size = typographySettings[ selector ]?.fontSize + const unit = size ? typographySettings[ selector ]?.fontSizeUnit : 'px' + return { + name: presetName, + slug: presetSlug, + size: `${ size ?? getDefaultFontSize( selector ) ?? 16 }${ unit ?? 'px' }`, + } + } ) + // Add the preset for extra small + let xSmallSize = typographySettings[ '.stk-subtitle' ]?.fontSize ?? getDefaultFontSize( '.stk-subtitle' ) ?? 16 + let xSmallUnit = typographySettings[ '.stk-subtitle' ]?.fontSizeUnit ?? 'px' + if ( xSmallUnit === 'px' ) { + xSmallSize = Math.pow( xSmallSize / 16, 2 ) + xSmallUnit = 'rem' + } else { + xSmallSize = Math.pow( xSmallSize, 2 ) + } + + fontSizePresets.push( { + name: 'XS', + slug: 'x-small', + size: `${ xSmallSize }${ xSmallUnit ?? 'px' }`, + } ) + // Reverse the presets so it's from smallest to biggest + fontSizePresets.reverse() + + const newSettings = { ...allCustomPresets, fontSizes: fontSizePresets } + + clearTimeout( saveTypographyAsPresetsThrottle ) + saveTypographyAsPresetsThrottle = setTimeout( () => { + const settings = new models.Settings( { stackable_global_custom_preset_controls: newSettings } ) // eslint-disable-line camelcase + settings.save() + }, 300 ) + + dispatch( 'stackable/global-preset-controls.custom' ).updateCustomPresetControls( newSettings ) + } + } + useEffect( () => { // Get settings. fetchSettings().then( response => { @@ -60,8 +119,10 @@ export const GlobalTypographyStyles = () => { // Allow actions to trigger styles to update. addAction( 'stackable.global-settings.typography.update-trigger', 'stackable/typography-styles', ( - newTypographySettings, newAapplySettingsTo, newIsApplyBodyToHTML + newTypographySettings, newAapplySettingsTo, newUseTypographyAsPresets, newIsApplyBodyToHTML, tags ) => { + updatePresetsWithTypography( newUseTypographyAsPresets, newTypographySettings, tags ) + setTypographySettings( newTypographySettings ) setApplySettingsTo( newAapplySettingsTo ) setIsApplyBodyToHTML( newIsApplyBodyToHTML ) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 5c608f685a..bbaa0c6110 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -13,7 +13,7 @@ import { getAppliedTypeScale } from './utils' import { PanelAdvancedSettings, AdvancedSelectControl, ControlSeparator, FontPairPicker, ProControlButton, AdvancedToggleControl, } from '~stackable/components' -import { fetchSettings, getDefaultFontSize } from '~stackable/util' +import { fetchSettings } from '~stackable/util' import { i18n, isPro, showProNotice, } from 'stackable' @@ -32,7 +32,7 @@ import { addFilter, applyFilters, doAction, } from '@wordpress/hooks' import { __, sprintf } from '@wordpress/i18n' -import { dispatch, useSelect } from '@wordpress/data' +import { useSelect } from '@wordpress/data' export { GlobalTypographyStyles } @@ -142,13 +142,11 @@ const TYPE_SCALE = [ let saveTypographyThrottle = null let saveSelectedFontPairThrottle = null let saveCustomFontPairsThrottle = null -let saveTypographyAsPresetsThrottle = null addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', output => { - const { allCustomPresets, useTypographyAsPresets } = useSelect( select => { - const _customPresetControls = select( 'stackable/global-preset-controls.custom' )?.getCustomPresetControls() ?? {} + const { useTypographyAsPresets } = useSelect( select => { const _useTypographyAsPresets = select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false - return { allCustomPresets: { ..._customPresetControls }, useTypographyAsPresets: _useTypographyAsPresets } + return { useTypographyAsPresets: _useTypographyAsPresets } }, [] ) const FONT_PAIRS = applyFilters( 'stackable.global-settings.typography.font-pairs.premium-font-pairs', FREE_FONT_PAIRS ) @@ -192,57 +190,11 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', useEffect( () => { // When typography styles are changed, trigger our editor style generator to update. - doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo, isApplyBodyToHTML ) - }, [ JSON.stringify( typographySettings ), applySettingsTo, isApplyBodyToHTML ] ) - - useEffect( () => { - // When typography styles are changed, trigger our editor style generator to update. - doAction( 'stackable.global-settings.typography.update-trigger', typographySettings, applySettingsTo, isApplyBodyToHTML ) - - // Update the custom presets when using typography as presets - if ( useTypographyAsPresets ) { - const fontSizePresets = TYPOGRAPHY_TAGS - .filter( ( { presetSlug } ) => !! presetSlug ) - .map( ( { - selector, presetName, presetSlug, - } ) => { - const size = typographySettings[ selector ]?.fontSize ?? getDefaultFontSize( selector ) ?? 16 - const unit = typographySettings[ selector ]?.fontSizeUnit ?? 'px' - return { - name: presetName, - slug: presetSlug, - size: `${ size }${ unit }`, - } - } ) - // Add the preset for extra small - let xSmallSize = typographySettings[ '.stk-subtitle' ]?.fontSize ?? getDefaultFontSize( '.stk-subtitle' ) ?? 16 - let xSmallUnit = typographySettings[ '.stk-subtitle' ]?.fontSizeUnit ?? 'px' - if ( xSmallUnit === 'px' ) { - xSmallSize = Math.pow( xSmallSize / 16, 2 ) - xSmallUnit = 'rem' - } else { - xSmallSize = Math.pow( xSmallSize, 2 ) - } - - fontSizePresets.push( { - name: 'XS', - slug: 'x-small', - size: `${ xSmallSize }${ xSmallUnit ?? 'px' }`, - } ) - // Reverse the presets so it's from smallest to biggest - fontSizePresets.reverse() - - const newSettings = { ...allCustomPresets, fontSizes: fontSizePresets } - - clearTimeout( saveTypographyAsPresetsThrottle ) - saveTypographyAsPresetsThrottle = setTimeout( () => { - const settings = new models.Settings( { stackable_global_custom_preset_controls: newSettings } ) // eslint-disable-line camelcase - settings.save() - }, 300 ) - - dispatch( 'stackable/global-preset-controls.custom' ).updateCustomPresetControls( newSettings ) - } - }, [ JSON.stringify( typographySettings ), useTypographyAsPresets ] ) + // This also triggers updating presets with typography, and applying body font size to html. + doAction( 'stackable.global-settings.typography.update-trigger', + typographySettings, applySettingsTo, useTypographyAsPresets, isApplyBodyToHTML, TYPOGRAPHY_TAGS, + ) + }, [ JSON.stringify( typographySettings ), applySettingsTo, useTypographyAsPresets, isApplyBodyToHTML ] ) // Scroll to the selected font pair when Global Typography tab is toggled useEffect( () => { From 4098a505b5e0c5438c0043df2ca889c87259a845 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 14 Apr 2025 09:29:02 +0800 Subject: [PATCH 058/124] fix: add typography deprecation for font size --- src/block-components/typography/attributes.js | 20 ++++-- src/block-components/typography/deprecated.js | 38 +++++++++++ src/block-components/typography/index.js | 4 +- src/block/button/deprecated.js | 22 +++++++ src/block/count-up/deprecated.js | 23 ++++++- src/block/countdown/deprecated.js | 31 ++++++++- src/block/heading/deprecated.js | 23 ++++++- src/block/icon-list-item/deprecated.js | 21 ++++++- src/block/icon-list/deprecated/index.js | 62 +++++++++++++++++- src/block/image/deprecated.js | 23 ++++++- src/block/number-box/deprecated.js | 21 ++++++- src/block/posts/deprecated.js | 63 +++++++++++++++++++ src/block/progress-bar/deprecated.js | 23 ++++++- src/block/progress-circle/deprecated.js | 23 ++++++- src/block/subtitle/deprecated.js | 23 ++++++- src/block/tab-labels/deprecated.js | 27 +++++++- src/block/table-of-contents/deprecated.js | 24 ++++++- src/block/text/deprecated.js | 23 ++++++- src/block/timeline/deprecated.js | 23 ++++++- 19 files changed, 496 insertions(+), 21 deletions(-) diff --git a/src/block-components/typography/attributes.js b/src/block-components/typography/attributes.js index 9e8c87512f..3b26b76a3a 100644 --- a/src/block-components/typography/attributes.js +++ b/src/block-components/typography/attributes.js @@ -1,12 +1,6 @@ import { deprecatedAddAttributes } from './deprecated' const typographyAttributes = { - fontSize: { - stkResponsive: true, - type: 'string', - default: '', - stkUnits: 'px', - }, lineHeight: { stkResponsive: true, type: 'number', @@ -104,4 +98,18 @@ export const addAttributes = ( attrObject, selector = '.stk-content', options = versionDeprecated: '', attrNameTemplate, } ) + + attrObject.add( { + attributes: { + fontSize: { + stkResponsive: true, + type: 'string', + default: '', + stkUnits: 'px', + }, + }, + versionAdded: '3.15.3', + versionDeprecated: '', + attrNameTemplate, + } ) } diff --git a/src/block-components/typography/deprecated.js b/src/block-components/typography/deprecated.js index 968b066982..db634c525c 100644 --- a/src/block-components/typography/deprecated.js +++ b/src/block-components/typography/deprecated.js @@ -22,6 +22,20 @@ export const deprecatedAddAttributes = ( attrObject, options ) => { versionDeprecated: '3.12.0', attrNameTemplate, } ) + + attrObject.add( { + attributes: { + fontSize: { + stkResponsive: true, + type: 'number', + default: '', + stkUnits: 'px', + }, + }, + versionAdded: '3.0.0', + versionDeprecated: '3.15.3', + attrNameTemplate, + } ) } export const deprecateTypographyGradientColor = { @@ -96,3 +110,27 @@ export const deprecateTypographyShadowColor = { return newAttributes }, } + +export const deprecateTypographyFontSize = { + isEligible: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const fontSize = getAttribute( 'fontSize' ) + + return typeof fontSize === 'number' + }, + migrate: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const fontSize = getAttribute( 'fontSize' ) + + const newAttributes = { + ...attributes, + [ getAttrName( 'fontSize' ) ]: String( fontSize ), + } + + return newAttributes + }, +} diff --git a/src/block-components/typography/index.js b/src/block-components/typography/index.js index 33f5d196a5..664ca2e384 100644 --- a/src/block-components/typography/index.js +++ b/src/block-components/typography/index.js @@ -28,7 +28,9 @@ import { memo, } from '@wordpress/element' -export { deprecateTypographyGradientColor, deprecateTypographyShadowColor } from './deprecated' +export { + deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, +} from './deprecated' export const Typography = memo( forwardRef( ( props, ref ) => { const { diff --git a/src/block/button/deprecated.js b/src/block/button/deprecated.js index e7b1f9febc..39beef85dd 100644 --- a/src/block/button/deprecated.js +++ b/src/block/button/deprecated.js @@ -6,9 +6,31 @@ import { deprecateBlockBackgroundColorOpacity, deprecateButtonGradientColor, deprecateContainerBackgroundColorOpacity, deprecateShadowColor, deprecateContainerShadowColor, deprecateBlockShadowColor, + deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( 'button%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'button%s' )( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateButtonGradientColor.migrate( 'button%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'button%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/count-up/deprecated.js b/src/block/count-up/deprecated.js index 0db6560075..a9151421e6 100644 --- a/src/block/count-up/deprecated.js +++ b/src/block/count-up/deprecated.js @@ -4,10 +4,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/countdown/deprecated.js b/src/block/countdown/deprecated.js index 31e834a1aa..c3e3a07bc1 100644 --- a/src/block/countdown/deprecated.js +++ b/src/block/countdown/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,35 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const hasDigitFontSize = deprecateTypographyFontSize.isEligible( 'digit%s' )( attributes ) + const hasLabelFontSize = deprecateTypographyFontSize.isEligible( 'label%s' )( attributes ) + const hasMessageFontSize = deprecateTypographyFontSize.isEligible( 'message%s' )( attributes ) + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + return hasDigitFontSize || hasLabelFontSize || hasMessageFontSize || isNotV4 + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'digit%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'label%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'message%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'digit%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'label%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'message%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/heading/deprecated.js b/src/block/heading/deprecated.js index e8cc3be197..59416795a0 100644 --- a/src/block/heading/deprecated.js +++ b/src/block/heading/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, getResponsiveClasses, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' /** @@ -34,6 +34,27 @@ addFilter( 'stackable.heading.save.blockClassNames', 'stackable/3.6.1', ( output } ) const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/icon-list-item/deprecated.js b/src/block/icon-list-item/deprecated.js index 2c6633b1ac..f3d73a8440 100644 --- a/src/block/icon-list-item/deprecated.js +++ b/src/block/icon-list-item/deprecated.js @@ -2,9 +2,28 @@ import { Save } from './save' import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' -import { deprecateBlockShadowColor, deprecateContainerShadowColor } from '~stackable/block-components' +import { + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, +} from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/icon-list/deprecated/index.js b/src/block/icon-list/deprecated/index.js index a137d3593c..9e47890457 100644 --- a/src/block/icon-list/deprecated/index.js +++ b/src/block/icon-list/deprecated/index.js @@ -4,7 +4,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' import { createUniqueClass } from '~stackable/util' @@ -68,6 +68,66 @@ const getEquivalentIconSize = iconSize => { } const deprecated = [ + { + // Support the new shadow color. + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + supports: { + anchor: true, + spacing: true, + __unstablePasteTextInline: true, + __experimentalSelector: 'ol,ul', + __experimentalOnMerge: true, + }, + migrate: ( attributes, innerBlocks ) => { + let newAttributes = { ...attributes } + const { + text, icons, iconSize, ordered, iconGap, + } = attributes + + const _iconSize = iconSize ? iconSize : 1 + const _iconGap = iconGap ? iconGap : 0 + + newAttributes = { + ...newAttributes, + listFullWidth: false, + iconVerticalAlignment: 'baseline', + iconGap: _iconGap + 4, // Our gap is smaller now. + iconSize: ordered + ? getEquivalentFontSize( _iconSize ) + : getEquivalentIconSize( _iconSize ), + } + + if ( ! text ) { + const block = createBlock( 'stackable/icon-list-item' ) + innerBlocks = [ block ] + } else { + const contents = textToArray( text ) + const blocks = contents.map( ( content, index ) => { + const newBlock = createBlock( 'stackable/icon-list-item', { + text: content, + icon: getUniqueIcon( icons, index ), + } ) + newBlock.attributes.uniqueId = createUniqueClass( newBlock.clientId ) + + return newBlock + } ) + innerBlocks = blocks + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return [ newAttributes, innerBlocks ] + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/image/deprecated.js b/src/block/image/deprecated.js index 04e709ee1a..52ad380b79 100644 --- a/src/block/image/deprecated.js +++ b/src/block/image/deprecated.js @@ -4,7 +4,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecationImageOverlayOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' import { RichText } from '@wordpress/block-editor' @@ -59,6 +59,27 @@ addFilter( 'stackable.image.save.wrapper', 'stackable/image-caption-wrapper', ( } ) const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( 'figcaption%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecationImageOverlayOpacity.migrate( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'image%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'figcaption%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/number-box/deprecated.js b/src/block/number-box/deprecated.js index 45adeda965..2775402b5c 100644 --- a/src/block/number-box/deprecated.js +++ b/src/block/number-box/deprecated.js @@ -4,7 +4,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateTypographyGradientColor, deprecationBackgrounColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyFontSize, BlockDiv, CustomCSS, Typography, getResponsiveClasses, getTypographyClasses, getAlignmentClasses, } from '~stackable/block-components' @@ -53,6 +53,25 @@ const depecatedSave_3_13_11 = props => { } const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = deprecateBlockBackgroundColorOpacity.migrate( attributes ) + newAttributes = deprecationBackgrounColorOpacity.migrate( 'shape%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'shape%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { attributes: attributes( '3.13.11' ), save: depecatedSave_3_13_11, diff --git a/src/block/posts/deprecated.js b/src/block/posts/deprecated.js index 88bb28a624..9df29f28ba 100644 --- a/src/block/posts/deprecated.js +++ b/src/block/posts/deprecated.js @@ -13,6 +13,7 @@ import { Image, deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecationImageOverlayOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateTypographyFontSize, } from '~stackable/block-components' /** @@ -41,6 +42,68 @@ addFilter( 'stackable.posts.title.readmore-content', 'stackable/3_0_2', addUndef addFilter( 'stackable.posts.feature-image', 'stackable/3_6_3', determineFeatureImage ) const deprecated = [ + { + // Support the new shadow color. + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const hasFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + return hasFontSize || isNotV4 + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + version: 2, + } + + // We used to have an "Inner content width" which is now just the block width + const hasOldInnerContentWidth = attributes.innerBlockContentWidth || attributes.innerBlockContentWidthTablet || attributes.innerBlockContentWidthMobile + + if ( hasOldInnerContentWidth ) { + newAttributes = { + ...newAttributes, + innerBlockContentWidth: '', + innerBlockContentWidthTablet: '', + innerBlockContentWidthMobile: '', + innerBlockContentWidthUnit: 'px', + innerBlockContentWidthUnitTablet: '', + innerBlockContentWidthUnitMobile: '', + blockWidth: attributes.innerBlockContentWidth, + blockWidthTablet: attributes.innerBlockContentWidthTablet, + blockWidthMobile: attributes.innerBlockContentWidthMobile, + blockWidthUnit: attributes.innerBlockContentWidthUnit, + blockWidthUnitTablet: attributes.innerBlockContentWidthUnitTablet, + blockWidthUnitMobile: attributes.innerBlockContentWidthUnitMobile, + innerBlockAlign: '', + innerBlockAlignTablet: '', + innerBlockAlignMobile: '', + blockHorizontalAlign: attributes.innerBlockAlign, + blockHorizontalAlignTablet: attributes.innerBlockAlignTablet, + blockHorizontalAlignMobile: attributes.innerBlockAlignMobile, + } + } + + newAttributes = deprecationImageOverlayOpacity.migrate( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + + newAttributes = deprecateTypographyGradientColor.migrate( 'title%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'category%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'excerpt%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'meta%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'readmore%s' )( newAttributes ) + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'image%s' )( newAttributes ) + + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/progress-bar/deprecated.js b/src/block/progress-bar/deprecated.js index 0db6560075..a9151421e6 100644 --- a/src/block/progress-bar/deprecated.js +++ b/src/block/progress-bar/deprecated.js @@ -4,10 +4,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/progress-circle/deprecated.js b/src/block/progress-circle/deprecated.js index 0db6560075..a9151421e6 100644 --- a/src/block/progress-circle/deprecated.js +++ b/src/block/progress-circle/deprecated.js @@ -4,10 +4,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/subtitle/deprecated.js b/src/block/subtitle/deprecated.js index a0c6c42406..586654d9ca 100644 --- a/src/block/subtitle/deprecated.js +++ b/src/block/subtitle/deprecated.js @@ -4,10 +4,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/tab-labels/deprecated.js b/src/block/tab-labels/deprecated.js index 6f6cca19d5..96cc92cb7d 100644 --- a/src/block/tab-labels/deprecated.js +++ b/src/block/tab-labels/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateButtonGradientColor, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( 'tab%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'tab%s' )( newAttributes ) + newAttributes = deprecateButtonGradientColor.migrate( 'tab%s' )( newAttributes ) + newAttributes = deprecateButtonGradientColor.migrate( 'activeTab%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'tab%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'activeTab%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'tab%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/table-of-contents/deprecated.js b/src/block/table-of-contents/deprecated.js index 1aa3cccf62..6abb5a19ac 100644 --- a/src/block/table-of-contents/deprecated.js +++ b/src/block/table-of-contents/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' /** @@ -36,6 +36,28 @@ addFilter( 'stackable.table-of-contents.save.tableOfContentsClasses', 'stackable } ) const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( 'title%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( 'title%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'title%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/text/deprecated.js b/src/block/text/deprecated.js index 57d902aa2b..393a7baa74 100644 --- a/src/block/text/deprecated.js +++ b/src/block/text/deprecated.js @@ -4,10 +4,31 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/timeline/deprecated.js b/src/block/timeline/deprecated.js index 8ef5054ff1..9ded4d89b2 100644 --- a/src/block/timeline/deprecated.js +++ b/src/block/timeline/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,27 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for fontSize + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), From 4a6815300324f9e401af1d048827ef4c893feda4 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 14 Apr 2025 11:56:51 +0800 Subject: [PATCH 059/124] fix: add block height deprecations --- src/block-components/block-div/deprecated.js | 13 +++- src/block-components/block-div/index.js | 4 +- .../helpers/size/attributes.js | 24 ++++-- .../helpers/size/deprecated.js | 41 ++++++++++ src/block-components/helpers/size/index.js | 1 + src/block/accordion/deprecated.js | 21 +++++- src/block/blockquote/deprecated.js | 26 ++++++- src/block/button-group/deprecated/index.js | 21 +++++- src/block/button/deprecated.js | 7 +- src/block/call-to-action/deprecated.js | 73 +++++++++++++++++- src/block/card/deprecated.js | 72 +++++++++++++++++- src/block/carousel/deprecated.js | 23 +++++- src/block/column/deprecated.js | 75 ++++++++++++++++++- src/block/columns/deprecated.js | 29 +++++++ src/block/count-up/deprecated.js | 8 +- src/block/countdown/deprecated.js | 8 +- src/block/divider/deprecated.js | 21 +++++- src/block/expand/deprecated.js | 21 +++++- src/block/feature-grid/deprecated.js | 29 +++++++ src/block/feature/deprecated.js | 38 ++++++++++ src/block/heading/deprecated.js | 9 ++- src/block/hero/deprecated.js | 72 ++++++++++++++++++ src/block/horizontal-scroller/deprecated.js | 21 +++++- src/block/icon-box/deprecated.js | 37 ++++++++- src/block/icon-button/deprecated.js | 23 +++++- src/block/icon-label/deprecated.js | 17 ++++- src/block/icon-list-item/deprecated.js | 9 ++- src/block/icon-list/deprecated/index.js | 9 ++- src/block/icon/deprecated.js | 21 +++++- src/block/image-box/deprecated.js | 21 +++++- src/block/image/deprecated.js | 6 +- src/block/map/deprecated.js | 21 +++++- src/block/notification/deprecated.js | 71 +++++++++++++++++- src/block/number-box/deprecated.js | 6 +- src/block/posts/deprecated.js | 22 ++++-- src/block/price/deprecated.js | 21 +++++- src/block/pricing-box/deprecated.js | 71 +++++++++++++++++- src/block/progress-bar/deprecated.js | 8 +- src/block/progress-circle/deprecated.js | 8 +- src/block/separator/deprecated.js | 21 ++++++ src/block/spacer/deprecated.js | 21 +++++- src/block/subtitle/deprecated.js | 8 +- src/block/tab-content/deprecated.js | 21 +++++- src/block/tab-labels/deprecated.js | 8 +- src/block/table-of-contents/deprecated.js | 9 ++- src/block/tabs/deprecated.js | 32 +++++++- src/block/team-member/deprecated.js | 71 +++++++++++++++++- src/block/testimonial/deprecated.js | 71 +++++++++++++++++- src/block/text/deprecated.js | 6 +- src/block/timeline/deprecated.js | 6 +- src/block/video-popup/deprecated.js | 17 ++++- 51 files changed, 1250 insertions(+), 69 deletions(-) create mode 100644 src/block-components/helpers/size/deprecated.js diff --git a/src/block-components/block-div/deprecated.js b/src/block-components/block-div/deprecated.js index f8f2655b80..f6571a9513 100644 --- a/src/block-components/block-div/deprecated.js +++ b/src/block-components/block-div/deprecated.js @@ -1,4 +1,6 @@ -import { deprecationBackgrounColorOpacity, deprecateShadowColor } from '../helpers' +import { + deprecationBackgrounColorOpacity, deprecateShadowColor, deprecateSizeControlHeight, +} from '../helpers' import { addFilter } from '@wordpress/hooks' import { semverCompare } from '~stackable/util' @@ -38,3 +40,12 @@ export const deprecateBlockShadowColor = { return deprecateShadowColor.migrate( 'block%s' )( attributes ) }, } + +export const deprecateBlockHeight = { + isEligible: attributes => { + return deprecateSizeControlHeight.isEligible( 'block%s' )( attributes ) + }, + migrate: attributes => { + return deprecateSizeControlHeight.migrate( 'block%s' )( attributes ) + }, +} diff --git a/src/block-components/block-div/index.js b/src/block-components/block-div/index.js index 123eaacbe2..de84319ed1 100644 --- a/src/block-components/block-div/index.js +++ b/src/block-components/block-div/index.js @@ -18,7 +18,9 @@ import { CustomAttributes } from '../custom-attributes' import { version as VERSION } from 'stackable' export { useUniqueId } -export { deprecateBlockBackgroundColorOpacity, deprecateBlockShadowColor } from './deprecated' +export { + deprecateBlockBackgroundColorOpacity, deprecateBlockShadowColor, deprecateBlockHeight, +} from './deprecated' export const BlockDiv = memo( props => { const { diff --git a/src/block-components/helpers/size/attributes.js b/src/block-components/helpers/size/attributes.js index 4c51277183..33f2204c4c 100644 --- a/src/block-components/helpers/size/attributes.js +++ b/src/block-components/helpers/size/attributes.js @@ -1,10 +1,6 @@ +import { deprecatedAddAttributes } from './deprecated' + export const sizeAttributes = { - height: { - stkResponsive: true, - stkUnits: 'px', - type: 'string', - default: '', - }, width: { stkResponsive: true, stkUnits: 'px', @@ -37,10 +33,26 @@ export const sizeAttributes = { } export const addSizeAttributes = ( attrObject, attrNameTemplate = '%s' ) => { + deprecatedAddAttributes( attrObject, attrNameTemplate ) + attrObject.add( { attributes: sizeAttributes, attrNameTemplate, versionAdded: '3.0.0', versionDeprecated: '', } ) + + attrObject.add( { + attributes: { + height: { + stkResponsive: true, + stkUnits: 'px', + type: 'string', + default: '', + }, + }, + attrNameTemplate, + versionAdded: '3.15.3', + versionDeprecated: '', + } ) } diff --git a/src/block-components/helpers/size/deprecated.js b/src/block-components/helpers/size/deprecated.js new file mode 100644 index 0000000000..8c2267a187 --- /dev/null +++ b/src/block-components/helpers/size/deprecated.js @@ -0,0 +1,41 @@ +import { getAttrNameFunction } from '~stackable/util' + +export const deprecatedAddAttributes = ( attrObject, attrNameTemplate = '%s' ) => { + attrObject.add( { + attributes: { + height: { + stkResponsive: true, + stkUnits: 'px', + type: 'number', + default: '', + }, + }, + attrNameTemplate, + versionAdded: '3.0.0', + versionDeprecated: '3.15.3', + } ) +} + +export const deprecateSizeControlHeight = { + isEligible: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const height = getAttribute( 'height' ) + + return typeof height === 'number' + }, + migrate: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const height = getAttribute( 'height' ) + + const newAttributes = { + ...attributes, + [ getAttrName( 'height' ) ]: String( height ), + } + + return newAttributes + }, +} diff --git a/src/block-components/helpers/size/index.js b/src/block-components/helpers/size/index.js index 3303c5b46a..71d4d32c99 100644 --- a/src/block-components/helpers/size/index.js +++ b/src/block-components/helpers/size/index.js @@ -1,3 +1,4 @@ export * from './attributes' export * from './style' export * from './edit' +export { deprecateSizeControlHeight } from './deprecated' diff --git a/src/block/accordion/deprecated.js b/src/block/accordion/deprecated.js index 962ab17155..a9ebe51e9e 100644 --- a/src/block/accordion/deprecated.js +++ b/src/block/accordion/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/blockquote/deprecated.js b/src/block/blockquote/deprecated.js index da35e425a2..dbd05187c9 100644 --- a/src/block/blockquote/deprecated.js +++ b/src/block/blockquote/deprecated.js @@ -4,10 +4,34 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + version: 2, + } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/button-group/deprecated/index.js b/src/block/button-group/deprecated/index.js index 55251ee121..ba4e86cb9a 100644 --- a/src/block/button-group/deprecated/index.js +++ b/src/block/button-group/deprecated/index.js @@ -6,10 +6,29 @@ import { attributes } from '../schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/button/deprecated.js b/src/block/button/deprecated.js index 39beef85dd..2e11ae544f 100644 --- a/src/block/button/deprecated.js +++ b/src/block/button/deprecated.js @@ -6,7 +6,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateButtonGradientColor, deprecateContainerBackgroundColorOpacity, deprecateShadowColor, deprecateContainerShadowColor, deprecateBlockShadowColor, - deprecateTypographyFontSize, + deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ @@ -15,7 +15,9 @@ const deprecated = [ attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( 'button%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -27,6 +29,7 @@ const deprecated = [ newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) newAttributes = deprecateButtonGradientColor.migrate( 'button%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'button%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/call-to-action/deprecated.js b/src/block/call-to-action/deprecated.js index e185c0314b..3a78c5a206 100644 --- a/src/block/call-to-action/deprecated.js +++ b/src/block/call-to-action/deprecated.js @@ -10,7 +10,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import compareVersions from 'compare-versions' @@ -35,6 +35,77 @@ addFilter( 'stackable.call-to-action.save.innerClassNames', 'stackable/3.8.0', ( } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return [ newAttributes, innerBlocks ] + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/card/deprecated.js b/src/block/card/deprecated.js index da6cd555bd..d14fc94e23 100644 --- a/src/block/card/deprecated.js +++ b/src/block/card/deprecated.js @@ -12,7 +12,7 @@ import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecationImageOverlayOpacity, getAlignmentClasses, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -86,6 +86,76 @@ addFilter( 'stackable.card.save.innerClassNames', 'stackable/3.0.2', ( output, p } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecationImageOverlayOpacity.migrate( newAttributes ), + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return [ newAttributes, innerBlocks ] + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/carousel/deprecated.js b/src/block/carousel/deprecated.js index 047bfa76c6..140d8429e3 100644 --- a/src/block/carousel/deprecated.js +++ b/src/block/carousel/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,27 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/column/deprecated.js b/src/block/column/deprecated.js index 1c889b75e5..dcfaee13c8 100644 --- a/src/block/column/deprecated.js +++ b/src/block/column/deprecated.js @@ -17,7 +17,7 @@ import { addFilter } from '@wordpress/hooks' import { semverCompare } from '~stackable/util' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' // Version 3.8 added horizontal flex, we changed the stk--block-orientation-* to stk--block-horizontal-flex. @@ -75,6 +75,79 @@ addFilter( 'stackable.column.save.blockClassNames', 'stackable/3.8.0', ( output, } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 4 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 4, + className: classnames( attributes.className, { + 'stk-block-column--v2': false, + 'stk-block-column--v3': false, + } ), + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return [ newAttributes, innerBlocks ] + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/columns/deprecated.js b/src/block/columns/deprecated.js index 8b6d5db023..5bf006f75a 100644 --- a/src/block/columns/deprecated.js +++ b/src/block/columns/deprecated.js @@ -17,6 +17,7 @@ import { semverCompare } from '~stackable/util' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockHeight, } from '~stackable/block-components' // Version 3.6.2 Deprecations, we now don't need the stk--has-column-order class. @@ -36,6 +37,34 @@ addFilter( 'stackable.columns.save.contentClassNames', 'stackable/3.8.0', ( clas } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const hasColumnFit = !! attributes.columnFit + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasColumnFit || hasNumberBlockHeight + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + columnFit: '', + columnFitAlign: '', + columnJustify: !! attributes.columnFit ? ( attributes.columnFitAlign || 'flex-start' ) : '', + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/count-up/deprecated.js b/src/block/count-up/deprecated.js index a9151421e6..7c9b21f8c9 100644 --- a/src/block/count-up/deprecated.js +++ b/src/block/count-up/deprecated.js @@ -5,15 +5,18 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/countdown/deprecated.js b/src/block/countdown/deprecated.js index c3e3a07bc1..620ddb9ef6 100644 --- a/src/block/countdown/deprecated.js +++ b/src/block/countdown/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -9,16 +9,17 @@ import { withVersion } from '~stackable/higher-order' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { const hasDigitFontSize = deprecateTypographyFontSize.isEligible( 'digit%s' )( attributes ) const hasLabelFontSize = deprecateTypographyFontSize.isEligible( 'label%s' )( attributes ) const hasMessageFontSize = deprecateTypographyFontSize.isEligible( 'message%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' - return hasDigitFontSize || hasLabelFontSize || hasMessageFontSize || isNotV4 + return hasDigitFontSize || hasLabelFontSize || hasMessageFontSize || hasNumberBlockHeight || isNotV4 }, migrate: attributes => { let newAttributes = { ...attributes } @@ -33,6 +34,7 @@ const deprecated = [ newAttributes = deprecateTypographyFontSize.migrate( 'digit%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'label%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'message%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/divider/deprecated.js b/src/block/divider/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/divider/deprecated.js +++ b/src/block/divider/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/expand/deprecated.js b/src/block/expand/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/expand/deprecated.js +++ b/src/block/expand/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/feature-grid/deprecated.js b/src/block/feature-grid/deprecated.js index d8792d68d8..2803f5c4db 100644 --- a/src/block/feature-grid/deprecated.js +++ b/src/block/feature-grid/deprecated.js @@ -13,6 +13,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, getResponsiveClasses, getSeparatorClasses, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockHeight, } from '~stackable/block-components' /** @@ -60,6 +61,34 @@ addFilter( 'stackable.feature-grid.save.blockClassNames', 'stackable/3.1.0', ( o } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const hasColumnFit = !! attributes.columnFit + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasColumnFit || hasNumberBlockHeight + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + columnFit: '', + columnFitAlign: '', + columnJustify: !! attributes.columnFit ? ( attributes.columnFitAlign || 'flex-start' ) : '', + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/feature/deprecated.js b/src/block/feature/deprecated.js index 851ca5574a..791adb3605 100644 --- a/src/block/feature/deprecated.js +++ b/src/block/feature/deprecated.js @@ -14,6 +14,7 @@ import { deprecateContainerBackgroundColorOpacity, getAlignmentClasses, getContentAlignmentClasses, getRowClasses, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockHeight, } from '~stackable/block-components' /** @@ -62,6 +63,43 @@ addFilter( 'stackable.feature.save.innerClassNames', 'stackable/3.8.0', ( output } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + version: 2, + } + + // Update the old column fit into flexbox + const hasOldColumnFit = !! attributes.columnFit + if ( hasOldColumnFit ) { + newAttributes = { + ...newAttributes, + columnFit: '', + columnFitAlign: '', + columnJustify: attributes.columnFitAlign, + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/heading/deprecated.js b/src/block/heading/deprecated.js index 59416795a0..118cfee850 100644 --- a/src/block/heading/deprecated.js +++ b/src/block/heading/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, getResponsiveClasses, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -35,11 +35,13 @@ addFilter( 'stackable.heading.save.blockClassNames', 'stackable/3.6.1', ( output const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -51,6 +53,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/hero/deprecated.js b/src/block/hero/deprecated.js index cc43238e39..ad6562c0ab 100644 --- a/src/block/hero/deprecated.js +++ b/src/block/hero/deprecated.js @@ -12,6 +12,7 @@ import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockHeight, } from '~stackable/block-components' /** @@ -35,6 +36,77 @@ addFilter( 'stackable.hero.save.innerClassNames', 'stackable/3.8.0', ( output, p } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/horizontal-scroller/deprecated.js b/src/block/horizontal-scroller/deprecated.js index a3d054b758..d04b468302 100644 --- a/src/block/horizontal-scroller/deprecated.js +++ b/src/block/horizontal-scroller/deprecated.js @@ -4,7 +4,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import { addFilter } from '@wordpress/hooks' import compareVersions from 'compare-versions' @@ -22,6 +22,25 @@ addFilter( 'stackable.horizontal-scroller.save.contentClassNames', 'stackable/3_ } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/icon-box/deprecated.js b/src/block/icon-box/deprecated.js index 7a6bc048e0..c410bb4d6d 100644 --- a/src/block/icon-box/deprecated.js +++ b/src/block/icon-box/deprecated.js @@ -4,10 +4,45 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: attributes => { + let newAttributes = { + ...attributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/icon-button/deprecated.js b/src/block/icon-button/deprecated.js index 9bf89108c1..48f200c0fd 100644 --- a/src/block/icon-button/deprecated.js +++ b/src/block/icon-button/deprecated.js @@ -5,10 +5,31 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateButtonGradientColor, deprecateContainerBackgroundColorOpacity, deprecateShadowColor, - deprecateContainerShadowColor, deprecateBlockShadowColor, + deprecateContainerShadowColor, deprecateBlockShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateButtonGradientColor.migrate( 'button%s' )( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'button%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/icon-label/deprecated.js b/src/block/icon-label/deprecated.js index 99014e6cbb..1d35507e39 100644 --- a/src/block/icon-label/deprecated.js +++ b/src/block/icon-label/deprecated.js @@ -4,10 +4,25 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { attributes: attributes( '3.13.1' ), save: withVersion( '3.13.1' )( Save ), diff --git a/src/block/icon-list-item/deprecated.js b/src/block/icon-list-item/deprecated.js index f3d73a8440..c6d88e177d 100644 --- a/src/block/icon-list-item/deprecated.js +++ b/src/block/icon-list-item/deprecated.js @@ -3,16 +3,18 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -20,6 +22,7 @@ const deprecated = [ newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/icon-list/deprecated/index.js b/src/block/icon-list/deprecated/index.js index 9e47890457..9f1a77dcdc 100644 --- a/src/block/icon-list/deprecated/index.js +++ b/src/block/icon-list/deprecated/index.js @@ -4,7 +4,7 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' import { createUniqueClass } from '~stackable/util' @@ -69,11 +69,13 @@ const getEquivalentIconSize = iconSize => { const deprecated = [ { - // Support the new shadow color. + // Support the change of type for font size and block height attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, supports: { anchor: true, @@ -124,6 +126,7 @@ const deprecated = [ newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return [ newAttributes, innerBlocks ] }, diff --git a/src/block/icon/deprecated.js b/src/block/icon/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/icon/deprecated.js +++ b/src/block/icon/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/image-box/deprecated.js b/src/block/image-box/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/image-box/deprecated.js +++ b/src/block/image-box/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/image/deprecated.js b/src/block/image/deprecated.js index 52ad380b79..976b7cc2c9 100644 --- a/src/block/image/deprecated.js +++ b/src/block/image/deprecated.js @@ -5,6 +5,7 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecationImageOverlayOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' import { RichText } from '@wordpress/block-editor' @@ -64,7 +65,9 @@ const deprecated = [ attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( 'figcaption%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( 'figcaption%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -76,6 +79,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'image%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'figcaption%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/map/deprecated.js b/src/block/map/deprecated.js index 90e9210c9c..f22f4db86e 100644 --- a/src/block/map/deprecated.js +++ b/src/block/map/deprecated.js @@ -7,7 +7,7 @@ import { withVersion } from '~stackable/higher-order' import { semverCompare, createElementFromHTMLString } from '~stackable/util' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import { addFilter } from '@wordpress/hooks' @@ -35,6 +35,25 @@ addFilter( 'stackable.map.icon-options', 'stackable/3.13.0', ( output, attribute } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/notification/deprecated.js b/src/block/notification/deprecated.js index 843109e860..694c439735 100644 --- a/src/block/notification/deprecated.js +++ b/src/block/notification/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -35,6 +35,75 @@ addFilter( 'stackable.notification.save.innerClassNames', 'stackable/3.8.0', ( o } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/number-box/deprecated.js b/src/block/number-box/deprecated.js index 2775402b5c..5dc233b26b 100644 --- a/src/block/number-box/deprecated.js +++ b/src/block/number-box/deprecated.js @@ -5,6 +5,7 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateTypographyGradientColor, deprecationBackgrounColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, BlockDiv, CustomCSS, Typography, getResponsiveClasses, getTypographyClasses, getAlignmentClasses, } from '~stackable/block-components' @@ -58,7 +59,9 @@ const deprecated = [ attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = deprecateBlockBackgroundColorOpacity.migrate( attributes ) @@ -68,6 +71,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'shape%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/posts/deprecated.js b/src/block/posts/deprecated.js index 9df29f28ba..0a2b80f225 100644 --- a/src/block/posts/deprecated.js +++ b/src/block/posts/deprecated.js @@ -13,7 +13,7 @@ import { Image, deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecationImageOverlayOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, - deprecateTypographyFontSize, + deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -43,14 +43,20 @@ addFilter( 'stackable.posts.feature-image', 'stackable/3_6_3', determineFeatureI const deprecated = [ { - // Support the new shadow color. + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - const hasFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSizeTitle = deprecateTypographyFontSize.isEligible( 'title%s' )( attributes ) + const hasNumberFontSizeCategory = deprecateTypographyFontSize.isEligible( 'category%s' )( attributes ) + const hasNumberFontSizeExcerpt = deprecateTypographyFontSize.isEligible( 'excerpt%s' )( attributes ) + const hasNumberFontSizeMeta = deprecateTypographyFontSize.isEligible( 'meta%s' )( attributes ) + const hasNumberFontSizeReadmore = deprecateTypographyFontSize.isEligible( 'readmore%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' - return hasFontSize || isNotV4 + return hasNumberFontSizeTitle || hasNumberFontSizeCategory || hasNumberFontSizeExcerpt || + hasNumberFontSizeMeta || hasNumberFontSizeReadmore || hasNumberBlockHeight || isNotV4 }, migrate: attributes => { let newAttributes = { @@ -99,7 +105,13 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'image%s' )( newAttributes ) - newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'title%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'category%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'excerpt%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'meta%s' )( newAttributes ) + newAttributes = deprecateTypographyFontSize.migrate( 'readmore%s' )( newAttributes ) + + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/price/deprecated.js b/src/block/price/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/price/deprecated.js +++ b/src/block/price/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/pricing-box/deprecated.js b/src/block/pricing-box/deprecated.js index aa0e1113fa..48d9382153 100644 --- a/src/block/pricing-box/deprecated.js +++ b/src/block/pricing-box/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** * WordPress dependencies @@ -34,6 +34,75 @@ addFilter( 'stackable.pricing-box.save.innerClassNames', 'stackable/3.8.0', ( ou } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/progress-bar/deprecated.js b/src/block/progress-bar/deprecated.js index a9151421e6..7c9b21f8c9 100644 --- a/src/block/progress-bar/deprecated.js +++ b/src/block/progress-bar/deprecated.js @@ -5,15 +5,18 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/progress-circle/deprecated.js b/src/block/progress-circle/deprecated.js index a9151421e6..7c9b21f8c9 100644 --- a/src/block/progress-circle/deprecated.js +++ b/src/block/progress-circle/deprecated.js @@ -5,15 +5,18 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/separator/deprecated.js b/src/block/separator/deprecated.js index 329d461cd9..69cdb00e89 100644 --- a/src/block/separator/deprecated.js +++ b/src/block/separator/deprecated.js @@ -5,9 +5,30 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateShadowColor.migrate( 'separator%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/spacer/deprecated.js b/src/block/spacer/deprecated.js index 395ffb36b0..90136ec005 100644 --- a/src/block/spacer/deprecated.js +++ b/src/block/spacer/deprecated.js @@ -4,10 +4,29 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/subtitle/deprecated.js b/src/block/subtitle/deprecated.js index 586654d9ca..809819a1c3 100644 --- a/src/block/subtitle/deprecated.js +++ b/src/block/subtitle/deprecated.js @@ -5,15 +5,18 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/tab-content/deprecated.js b/src/block/tab-content/deprecated.js index 57fd88d266..c0ba97b625 100644 --- a/src/block/tab-content/deprecated.js +++ b/src/block/tab-content/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,25 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/tab-labels/deprecated.js b/src/block/tab-labels/deprecated.js index 96cc92cb7d..8758b25c77 100644 --- a/src/block/tab-labels/deprecated.js +++ b/src/block/tab-labels/deprecated.js @@ -1,6 +1,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateButtonGradientColor, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateTypographyShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -9,11 +10,13 @@ import { withVersion } from '~stackable/higher-order' const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( 'tab%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( 'tab%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -29,6 +32,7 @@ const deprecated = [ newAttributes = deprecateShadowColor.migrate( 'tab%s' )( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'activeTab%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'tab%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/table-of-contents/deprecated.js b/src/block/table-of-contents/deprecated.js index 6abb5a19ac..e5af95f982 100644 --- a/src/block/table-of-contents/deprecated.js +++ b/src/block/table-of-contents/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, - deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -37,11 +37,13 @@ addFilter( 'stackable.table-of-contents.save.tableOfContentsClasses', 'stackable const deprecated = [ { - // Support the change of type for fontSize + // Support the change of type for fontSize and blockHeight attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( 'title%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( 'title%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -54,6 +56,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( 'title%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/tabs/deprecated.js b/src/block/tabs/deprecated.js index 85ccf8730f..a9574ef754 100644 --- a/src/block/tabs/deprecated.js +++ b/src/block/tabs/deprecated.js @@ -1,6 +1,6 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -8,6 +8,36 @@ import { attributes } from './schema' import { withVersion } from '~stackable/higher-order' const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + const hasContainerOpacity = deprecateContainerBackgroundColorOpacity.isEligible( attributes ) + if ( hasContainerOpacity ) { + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + } + + const hasBlockOpacity = deprecateBlockBackgroundColorOpacity.isEligible( attributes ) + if ( hasBlockOpacity ) { + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + } + + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return { + ...newAttributes, + equalTabHeight: true, + } + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/team-member/deprecated.js b/src/block/team-member/deprecated.js index ef5c7d6b2a..47ec8ffe9f 100644 --- a/src/block/team-member/deprecated.js +++ b/src/block/team-member/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -35,6 +35,75 @@ addFilter( 'stackable.team-member.save.innerClassNames', 'stackable/3.8.0', ( ou } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/testimonial/deprecated.js b/src/block/testimonial/deprecated.js index cc64e5fd2e..df70cb918e 100644 --- a/src/block/testimonial/deprecated.js +++ b/src/block/testimonial/deprecated.js @@ -11,7 +11,7 @@ import { withVersion } from '~stackable/higher-order' import compareVersions from 'compare-versions' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -35,6 +35,75 @@ addFilter( 'stackable.testimonial.save.innerClassNames', 'stackable/3.8.0', ( ou } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return isNotV4 || hasNumberBlockHeight + }, + migrate: ( attributes, innerBlocks ) => { + const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' + + let newAttributes = { + ...attributes, + } + + if ( isNotV4 ) { + newAttributes = { + ...newAttributes, + version: 2, + } + + // Update the vertical align into flexbox + const hasOldVerticalAlign = !! attributes.containerVerticalAlign // Column only, this was changed to flexbox + + if ( hasOldVerticalAlign ) { + newAttributes = { + ...newAttributes, + containerVerticalAlign: '', + innerBlockAlign: attributes.containerVerticalAlign, + } + } + + // If the inner blocks are horizontal, adjust to accomodate the new + // column gap, it will modify blocks because people used block + // margins before instead of a proper column gap. + if ( attributes.innerBlockOrientation === 'horizontal' ) { + innerBlocks.forEach( ( block, index ) => { + if ( index ) { + if ( ! block.attributes.blockMargin ) { + block.attributes.blockMargin = { + top: '', + right: '', + bottom: '', + left: '', + } + } + if ( block.attributes.blockMargin.left === '' ) { + block.attributes.blockMargin.left = 24 + } + } + } ) + + newAttributes = { + ...newAttributes, + innerBlockColumnGap: 0, + } + } + } + + newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) + newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) + newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { // Support the new shadow color. attributes: attributes( '3.12.11' ), diff --git a/src/block/text/deprecated.js b/src/block/text/deprecated.js index 393a7baa74..229a66c427 100644 --- a/src/block/text/deprecated.js +++ b/src/block/text/deprecated.js @@ -5,6 +5,7 @@ import { withVersion } from '~stackable/higher-order' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' const deprecated = [ @@ -13,7 +14,9 @@ const deprecated = [ attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/timeline/deprecated.js b/src/block/timeline/deprecated.js index 9ded4d89b2..6b7f581bae 100644 --- a/src/block/timeline/deprecated.js +++ b/src/block/timeline/deprecated.js @@ -1,6 +1,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, deprecateTypographyShadowColor, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateTypographyFontSize, + deprecateBlockHeight, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -13,7 +14,9 @@ const deprecated = [ attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberFontSize = deprecateTypographyFontSize.isEligible( '%s' )( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + return hasNumberFontSize || hasNumberBlockHeight }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateTypographyShadowColor.migrate( '%s' )( newAttributes ) newAttributes = deprecateTypographyFontSize.migrate( '%s' )( newAttributes ) + newAttributes = deprecateBlockHeight.migrate( newAttributes ) return newAttributes }, diff --git a/src/block/video-popup/deprecated.js b/src/block/video-popup/deprecated.js index 7d84849ead..1a0131f3b1 100644 --- a/src/block/video-popup/deprecated.js +++ b/src/block/video-popup/deprecated.js @@ -6,7 +6,7 @@ import { semverCompare } from '~stackable/util' import { i18n } from 'stackable' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, - deprecateBlockShadowColor, deprecateContainerShadowColor, + deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, } from '~stackable/block-components' /** @@ -28,6 +28,21 @@ addFilter( 'stackable.video-popup.save.div.content', 'stackable/3.12.14', ( outp } ) const deprecated = [ + { + // Support the change of type for block height + attributes: attributes( '3.15.2' ), + save: withVersion( '3.15.2' )( Save ), + isEligible: attributes => { + return deprecateBlockHeight.isEligible( attributes ) + }, + migrate: attributes => { + let newAttributes = { ...attributes } + + newAttributes = deprecateBlockHeight.migrate( newAttributes ) + + return newAttributes + }, + }, { attributes: attributes( '3.12.14' ), save: withVersion( '3.12.14' )( Save ), From 444166084153d8b1e1e0051a5144634bfff2d5e6 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 14 Apr 2025 12:46:02 +0800 Subject: [PATCH 060/124] fix: add row and column gaps deprecations --- src/block-components/columns/attributes.js | 24 ++++++++++++++++------ src/block-components/columns/index.js | 2 ++ src/block-components/columns/style.js | 8 ++++---- src/block/carousel/deprecated.js | 8 ++++++-- src/block/columns/deprecated.js | 8 +++++--- src/block/feature-grid/deprecated.js | 8 +++++--- src/block/feature/deprecated.js | 8 +++++--- src/block/tab-content/deprecated.js | 8 ++++++-- 8 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/block-components/columns/attributes.js b/src/block-components/columns/attributes.js index 2158b042fc..253a6ebf28 100644 --- a/src/block-components/columns/attributes.js +++ b/src/block-components/columns/attributes.js @@ -1,4 +1,8 @@ -export const addAttributes = attrObject => { +import { deprecatedAddAttributes } from './deprecated/index' + +export const addAttributes = ( attrObject, attrNameTemplate = '%s' ) => { + deprecatedAddAttributes( attrObject, attrNameTemplate ) + attrObject.add( { attributes: { columnSpacing: { @@ -7,6 +11,17 @@ export const addAttributes = attrObject => { type: 'number', default: '', }, + columnWrapDesktop: { // Only applies to desktops + type: 'boolean', + default: false, + }, + }, + versionAdded: '3.0.0', + versionDeprecated: '', + } ) + + attrObject.add( { + attributes: { columnGap: { stkResponsive: true, type: 'string', @@ -17,12 +32,9 @@ export const addAttributes = attrObject => { type: 'string', default: '', }, - columnWrapDesktop: { // Only applies to desktops - type: 'boolean', - default: false, - }, }, - versionAdded: '3.0.0', + attrNameTemplate, + versionAdded: '3.15.3', versionDeprecated: '', } ) } diff --git a/src/block-components/columns/index.js b/src/block-components/columns/index.js index 53398ebe93..bb0ec6397e 100644 --- a/src/block-components/columns/index.js +++ b/src/block-components/columns/index.js @@ -6,6 +6,8 @@ export const Columns = () => { return null } +export { deprecateColumnAndRowGap } from './deprecated/index' + Columns.InspectorControls = Edit Columns.addStyles = addStyles diff --git a/src/block-components/columns/style.js b/src/block-components/columns/style.js index 2355a4d16a..6a09e38ac5 100644 --- a/src/block-components/columns/style.js +++ b/src/block-components/columns/style.js @@ -33,7 +33,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { responsive: 'all', valueCallback: value => { // Substitute with using format to work with preset controls - if ( value.startsWith( 'var' ) ) { + if ( typeof value === 'string' && value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -46,7 +46,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'columnGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var' ) ) { + if ( typeof value === 'string' && value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -91,7 +91,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'rowGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var' ) ) { + if ( typeof value === 'string' && value.startsWith( 'var' ) ) { return value } return value + 'px' @@ -104,7 +104,7 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => { attrName: 'rowGap', responsive: 'all', valueCallback: value => { - if ( value.startsWith( 'var' ) ) { + if ( typeof value === 'string' && value.startsWith( 'var' ) ) { return value } return value + 'px' diff --git a/src/block/carousel/deprecated.js b/src/block/carousel/deprecated.js index 140d8429e3..92bfd20efc 100644 --- a/src/block/carousel/deprecated.js +++ b/src/block/carousel/deprecated.js @@ -1,6 +1,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, deprecateBlockHeight, + deprecateColumnAndRowGap, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -9,11 +10,13 @@ import { withVersion } from '~stackable/higher-order' const deprecated = [ { - // Support the change of type for block height + // Support the change of type for block height and gaps attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateBlockHeight.isEligible( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + const hasNumberGaps = deprecateColumnAndRowGap.isEligible( '%s' )( attributes ) + return hasNumberBlockHeight || hasNumberGaps }, migrate: attributes => { let newAttributes = { ...attributes } @@ -25,6 +28,7 @@ const deprecated = [ newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) newAttributes = deprecateBlockHeight.migrate( newAttributes ) + newAttributes = deprecateColumnAndRowGap.migrate( '%s' )( newAttributes ) return newAttributes }, diff --git a/src/block/columns/deprecated.js b/src/block/columns/deprecated.js index 5bf006f75a..a00faef833 100644 --- a/src/block/columns/deprecated.js +++ b/src/block/columns/deprecated.js @@ -17,7 +17,7 @@ import { semverCompare } from '~stackable/util' import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, - deprecateBlockHeight, + deprecateBlockHeight, deprecateColumnAndRowGap, } from '~stackable/block-components' // Version 3.6.2 Deprecations, we now don't need the stk--has-column-order class. @@ -38,13 +38,14 @@ addFilter( 'stackable.columns.save.contentClassNames', 'stackable/3.8.0', ( clas const deprecated = [ { - // Support the change of type for block height + // Support the change of type for block height and gaps attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { const hasColumnFit = !! attributes.columnFit const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) - return hasColumnFit || hasNumberBlockHeight + const hasNumberGaps = deprecateColumnAndRowGap.isEligible( '%s' )( attributes ) + return hasColumnFit || hasNumberBlockHeight || hasNumberGaps }, migrate: attributes => { let newAttributes = { @@ -61,6 +62,7 @@ const deprecated = [ newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) newAttributes = deprecateBlockHeight.migrate( newAttributes ) + newAttributes = deprecateColumnAndRowGap.migrate( '%s' )( newAttributes ) return newAttributes }, diff --git a/src/block/feature-grid/deprecated.js b/src/block/feature-grid/deprecated.js index 2803f5c4db..46e7ebe17a 100644 --- a/src/block/feature-grid/deprecated.js +++ b/src/block/feature-grid/deprecated.js @@ -13,7 +13,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, getResponsiveClasses, getSeparatorClasses, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, - deprecateBlockHeight, + deprecateBlockHeight, deprecateColumnAndRowGap, } from '~stackable/block-components' /** @@ -62,13 +62,14 @@ addFilter( 'stackable.feature-grid.save.blockClassNames', 'stackable/3.1.0', ( o const deprecated = [ { - // Support the change of type for block height + // Support the change of type for block height and gaps attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { const hasColumnFit = !! attributes.columnFit const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) - return hasColumnFit || hasNumberBlockHeight + const hasNumberGaps = deprecateColumnAndRowGap.isEligible( '%s' )( attributes ) + return hasColumnFit || hasNumberBlockHeight || hasNumberGaps }, migrate: attributes => { let newAttributes = { @@ -85,6 +86,7 @@ const deprecated = [ newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) newAttributes = deprecateBlockHeight.migrate( newAttributes ) + newAttributes = deprecateColumnAndRowGap.migrate( '%s' )( newAttributes ) return newAttributes }, diff --git a/src/block/feature/deprecated.js b/src/block/feature/deprecated.js index 791adb3605..581002a463 100644 --- a/src/block/feature/deprecated.js +++ b/src/block/feature/deprecated.js @@ -14,7 +14,7 @@ import { deprecateContainerBackgroundColorOpacity, getAlignmentClasses, getContentAlignmentClasses, getRowClasses, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateShadowColor, - deprecateBlockHeight, + deprecateBlockHeight, deprecateColumnAndRowGap, } from '~stackable/block-components' /** @@ -64,13 +64,14 @@ addFilter( 'stackable.feature.save.innerClassNames', 'stackable/3.8.0', ( output const deprecated = [ { - // Support the change of type for block height + // Support the change of type for block height and gaps attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { const isNotV4 = attributes.version < 2 || typeof attributes.version === 'undefined' const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) - return isNotV4 || hasNumberBlockHeight + const hasNumberGaps = deprecateColumnAndRowGap.isEligible( '%s' )( attributes ) + return isNotV4 || hasNumberBlockHeight || hasNumberGaps }, migrate: attributes => { let newAttributes = { @@ -96,6 +97,7 @@ const deprecated = [ newAttributes = deprecateShadowColor.migrate( 'topSeparator%s' )( newAttributes ) newAttributes = deprecateShadowColor.migrate( 'bottomSeparator%s' )( newAttributes ) newAttributes = deprecateBlockHeight.migrate( newAttributes ) + newAttributes = deprecateColumnAndRowGap.migrate( '%s' )( newAttributes ) return newAttributes }, diff --git a/src/block/tab-content/deprecated.js b/src/block/tab-content/deprecated.js index c0ba97b625..f9d19b712d 100644 --- a/src/block/tab-content/deprecated.js +++ b/src/block/tab-content/deprecated.js @@ -1,6 +1,7 @@ import { deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateBlockShadowColor, deprecateContainerShadowColor, deprecateBlockHeight, + deprecateColumnAndRowGap, } from '~stackable/block-components' import { Save } from './save' import { attributes } from './schema' @@ -9,11 +10,13 @@ import { withVersion } from '~stackable/higher-order' const deprecated = [ { - // Support the change of type for block height + // Support the change of type for block height and gaps attributes: attributes( '3.15.2' ), save: withVersion( '3.15.2' )( Save ), isEligible: attributes => { - return deprecateBlockHeight.isEligible( attributes ) + const hasNumberBlockHeight = deprecateBlockHeight.isEligible( attributes ) + const hasNumberGaps = deprecateColumnAndRowGap.isEligible( '%s' )( attributes ) + return hasNumberBlockHeight || hasNumberGaps }, migrate: attributes => { let newAttributes = { ...attributes } @@ -23,6 +26,7 @@ const deprecated = [ newAttributes = deprecateBlockShadowColor.migrate( newAttributes ) newAttributes = deprecateContainerShadowColor.migrate( newAttributes ) newAttributes = deprecateBlockHeight.migrate( newAttributes ) + newAttributes = deprecateColumnAndRowGap.migrate( '%s' )( newAttributes ) return newAttributes }, From 38d75f0e81de74d8c97501b82793d9db5cea1adf Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 15 Apr 2025 12:14:38 +0800 Subject: [PATCH 061/124] fix: add row and column gaps deprecations --- .../columns/deprecated/index.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/block-components/columns/deprecated/index.js diff --git a/src/block-components/columns/deprecated/index.js b/src/block-components/columns/deprecated/index.js new file mode 100644 index 0000000000..efce36ad69 --- /dev/null +++ b/src/block-components/columns/deprecated/index.js @@ -0,0 +1,48 @@ +import { getAttrNameFunction } from '~stackable/util' + +export const deprecatedAddAttributes = ( attrObject, attrNameTemplate = '%s' ) => { + attrObject.add( { + attributes: { + columnGap: { + stkResponsive: true, + type: 'number', + default: '', + }, + rowGap: { + stkResponsive: true, + type: 'number', + default: '', + }, + }, + attrNameTemplate, + versionAdded: '3.0.0', + versionDeprecated: '3.15.3', + } ) +} + +export const deprecateColumnAndRowGap = { + isEligible: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const columnGap = getAttribute( 'columnGap' ) + const rowGap = getAttribute( 'rowGap' ) + + return typeof columnGap === 'number' || typeof rowGap === 'number' + }, + migrate: attrNameTemplate => attributes => { + const getAttrName = getAttrNameFunction( attrNameTemplate ) + const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + + const columnGap = getAttribute( 'columnGap' ) + const rowGap = getAttribute( 'rowGap' ) + + const newAttributes = { + ...attributes, + [ getAttrName( 'columnGap' ) ]: String( columnGap ), + [ getAttrName( 'rowGap' ) ]: String( rowGap ), + } + + return newAttributes + }, +} From 130d4986eca69db11e68367519a97202b187777f Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Wed, 7 May 2025 11:40:54 +0800 Subject: [PATCH 062/124] tweaked premium notice wording --- src/components/pro-control/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/pro-control/index.js b/src/components/pro-control/index.js index a15a7ed4ec..83be6b65b8 100644 --- a/src/components/pro-control/index.js +++ b/src/components/pro-control/index.js @@ -136,7 +136,7 @@ const LABELS = { 'preset-controls': { title: __( 'Premium Preset Controls', i18n ), description:
      -
    • { __( 'Customize your own presets', i18n ) }
    • +
    • { __( 'Customize your own presets like small, medium, large & x-large', i18n ) }
    • { __( 'Use Global Typography sizes as presets', i18n ) }
    , }, From 46a7b29a8cd67a06c03a84e247c83d7a0c381781 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Fri, 9 May 2025 10:55:36 +0800 Subject: [PATCH 063/124] fix: still match previous value even the custom values changes --- .../advanced-range-control/index.js | 20 +++++++++++++------ src/util/index.js | 6 ++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 17c7039821..b0d0522c2e 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -13,7 +13,7 @@ import { useBlockSetAttributesContext, useDeviceType, } from '~stackable/hooks' -import { extractNumbersAndUnits } from '~stackable/util' +import { extractNumbersAndUnits, getCSSVarName } from '~stackable/util' /** * External dependencies @@ -106,13 +106,18 @@ const AdvancedRangeControl = props => { // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom - let isMarkValue = !! props.marks + let currentMark = null if ( props.marks && value ) { const valueToCheck = value + ( props.hasCSSVariableValue ? '' : unit ) - // Check if the current value exsits in the marks - isMarkValue = isMarkValue && props.marks.some( mark => mark.value === valueToCheck ) + // Check if the current value exists in the marks only by their CSS variable name + // to match in case the fallback size changes. + props.marks.forEach( mark => { + if ( getCSSVarName( mark.value ) === getCSSVarName( valueToCheck ) ) { + currentMark = mark + } + } ) } - const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) + const [ isMarkMode, setIsMarkMode ] = useState( !! currentMark ) // If this supports dynamic content, the value should be saved as a String. // Similar if using marks to accomodate CSS variable @@ -135,7 +140,10 @@ const AdvancedRangeControl = props => { onChangeFunc( newValue ) } - const derivedValue = typeof props.value === 'undefined' ? value : props.value + const derivedValue = typeof props.value === 'undefined' + // Use the value from the provided marks + ? currentMark ? currentMark.value : value + : props.value const dynamicContentProps = useDynamicContentControlProps( { value: derivedValue, diff --git a/src/util/index.js b/src/util/index.js index 08f02913e8..f060aaa231 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -417,3 +417,9 @@ export const extractNumbersAndUnits = value => { } return [ [ '0', 'px' ] ] } + +// Return only the CSS variable name given a CSS variable +export const getCSSVarName = value => { + const match = value.match( /var\(\s*([^,)\s]+)/ ) + return match ? match[ 1 ] : null +} From 8efb71e91d2c1f88e98a2b76a48d14f62f614cfe Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 14 May 2025 10:00:00 +0800 Subject: [PATCH 064/124] fix: implement presets in the design system; fix issues with reactivity, variable type, units, etc. --- .../advanced-range-control/index.js | 34 ++++----- src/components/four-range-control/index.js | 69 +++++++++++++------ src/global-settings.php | 35 +++++++++- .../buttons-and-icons/index.js | 19 +++++ .../buttons-and-icons/index.php | 17 ++--- .../spacing-and-borders/index.js | 35 +++++++--- .../spacing-and-borders/index.php | 17 ++--- .../utils/use-block-layout-editor-loader.js | 8 ++- 8 files changed, 167 insertions(+), 67 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index b0d0522c2e..33f1316ed1 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -104,20 +104,22 @@ const AdvancedRangeControl = props => { placeholderRender = null } + let derivedValue = typeof props.value === 'undefined' + ? value : props.value + // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom - let currentMark = null - if ( props.marks && value ) { - const valueToCheck = value + ( props.hasCSSVariableValue ? '' : unit ) + let isMarkValue = !! props.marks + if ( props.marks && derivedValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. - props.marks.forEach( mark => { - if ( getCSSVarName( mark.value ) === getCSSVarName( valueToCheck ) ) { - currentMark = mark - } - } ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( derivedValue ) ) + isMarkValue = !! matchedMark + if ( matchedMark ) { + derivedValue = matchedMark.value + } } - const [ isMarkMode, setIsMarkMode ] = useState( !! currentMark ) + const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) // If this supports dynamic content, the value should be saved as a String. // Similar if using marks to accomodate CSS variable @@ -140,11 +142,6 @@ const AdvancedRangeControl = props => { onChangeFunc( newValue ) } - const derivedValue = typeof props.value === 'undefined' - // Use the value from the provided marks - ? currentMark ? currentMark.value : value - : props.value - const dynamicContentProps = useDynamicContentControlProps( { value: derivedValue, onChange: _onChange, @@ -207,9 +204,9 @@ const AdvancedRangeControl = props => { const markValue = props.marks[ value ]?.[ property ] || '0' let [ newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] - // If the attribute has no units (only support px), and the - // preset units are rem or em, convert to px - if ( ! hasUnits && ( unit === 'rem' || unit === 'em' ) ) { + // If the attribute has no support for rem, and the + // preset units is rem, convert to px + if ( ( ! hasUnits || ( hasUnits && ! props.units.includes( 'rem' ) ) ) && unit === 'rem' ) { newValue = `${ parseFloat( newValue ) * 16 }` unit = 'px' } @@ -218,6 +215,9 @@ const AdvancedRangeControl = props => { if ( unit ) { dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() setAttributes( { [ unitAttrName ]: unit } ) + if ( props.onChangeUnit ) { + props.onChangeUnit( unit ) + } } _onChange( newValue ) diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 6e9aee60ea..492a9bd115 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -18,7 +18,6 @@ import RangeControl from '../advanced-range-control/range-control' import { ResetButton } from '../base-control2/reset-button' import AdvancedControl, { extractControlProps } from '../base-control2' import { useControlHandlers } from '../base-control2/hooks' -import { extractNumbersAndUnits } from '~stackable/util' /** * WordPress dependencies @@ -45,6 +44,7 @@ import { useBlockHoverState, useBlockSetAttributesContext, } from '~stackable/hooks' +import { extractNumbersAndUnits, getCSSVarName } from '~stackable/util' const isEqualInitial = ( props, value, firstValue ) => { let isEqual = true @@ -81,7 +81,7 @@ const FourRangeControl = memo( props => { ( props.enableBottom && value.bottom === '' ) && ( props.enableLeft && value.left === '' ) - const firstValue = props.enableTop ? value.top + let firstValue = props.enableTop ? value.top : props.enableRight ? value.right : props.enableBottom ? value.bottom : value.left @@ -195,17 +195,26 @@ const FourRangeControl = memo( props => { left: !! props.marks, } if ( props.marks && firstValue ) { - // Check if the current value exsits in the marks - const marksUnit = ( props.hasCSSVariableValue ? '' : unit ) - isMarkValue.first = isMarkValue.first && props.marks.some( mark => mark.value === firstValue + marksUnit ) - isMarkValue.top = isMarkValue.top && props.marks.some( mark => mark.value === value.top + marksUnit ) - isMarkValue.right = isMarkValue.right && props.marks.some( mark => mark.value === value.right + marksUnit ) - isMarkValue.bottom = isMarkValue.bottom && props.marks.some( mark => mark.value === value.bottom + marksUnit ) - isMarkValue.left = isMarkValue.left && props.marks.some( mark => mark.value === value.left + marksUnit ) + // Check if the current value exists in the marks only by their CSS variable name + // to match in case the fallback size changes. + const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( firstValue ) ) + isMarkValue.first = !! firstMatchedMark + if ( firstMatchedMark ) { + firstValue = firstMatchedMark.value + } + + [ 'top', 'right', 'bottom', 'left' ].forEach( side => { + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( value[ side ] ) ) + isMarkValue[ side ] = !! matchedMark + if ( matchedMark ) { + value[ side ] = matchedMark.value + } + } ) } const [ isFourMarkMode, setIsFourMarkMode ] = useState( isMarkValue ) - const onChangeAll = newValue => { + const onChangeAll = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: props.enableTop ? newValue : value.top, right: props.enableRight ? newValue : value.right, @@ -221,7 +230,8 @@ const FourRangeControl = memo( props => { } ) ) } - const onChangeTop = newValue => { + const onChangeTop = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: newValue, right: value.right, @@ -231,7 +241,8 @@ const FourRangeControl = memo( props => { setIsFourMarkMode( prev => ( { ...prev, first: prev.top } ) ) } - const onChangeRight = newValue => { + const onChangeRight = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: value.top, right: newValue, @@ -241,7 +252,8 @@ const FourRangeControl = memo( props => { setIsFourMarkMode( prev => ( { ...prev, first: prev.right } ) ) } - const onChangeBottom = newValue => { + const onChangeBottom = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: value.top, right: value.right, @@ -251,7 +263,8 @@ const FourRangeControl = memo( props => { setIsFourMarkMode( prev => ( { ...prev, first: prev.bottom } ) ) } - const onChangeLeft = newValue => { + const onChangeLeft = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: value.top, right: value.right, @@ -261,7 +274,8 @@ const FourRangeControl = memo( props => { setIsFourMarkMode( prev => ( { ...prev, first: prev.left } ) ) } - const onChangeVertical = newValue => { + const onChangeVertical = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: newValue, right: value.right, @@ -275,7 +289,8 @@ const FourRangeControl = memo( props => { } ) ) } - const onChangeHorizontal = newValue => { + const onChangeHorizontal = _newValue => { + const newValue = props.marks ? String( _newValue ) : _newValue onChange( { top: value.top, right: newValue, @@ -327,7 +342,7 @@ const FourRangeControl = memo( props => { } // We need to change the way we handle the value and onChange if we're doing marks - let rangeValue = props.hasCSSVariableValue ? parseFloat( initialValue ) : initialValue + let rangeValue = props.marks ? parseFloat( initialValue ) : initialValue let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { @@ -341,12 +356,23 @@ const FourRangeControl = memo( props => { // Extract the unit and value. const markValue = props.marks[ value ]?.[ property ] || '0' - const [ _newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] - const newValue = _newValue + let [ newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] + + // If the attribute has no support for rem, and the + // preset units is rem, convert to px + if ( ( ! hasUnits || ( hasUnits && ! props.units.includes( 'rem' ) ) ) && unit === 'rem' ) { + newValue = `${ parseFloat( newValue ) * 16 }` + unit = 'px' + } // Update the unit. - dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() - setAttributes( { [ unitAttrName ]: unit } ) + if ( unit ) { + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: unit } ) + if ( props.onChangeUnit ) { + props.onChangeUnit( unit ) + } + } initialOnChange( newValue ) } @@ -890,7 +916,6 @@ FourRangeControl.defaultProps = { marks: undefined, allowCustom: true, - hasCSSVariableValue: false, } export default memo( FourRangeControl ) diff --git a/src/global-settings.php b/src/global-settings.php index 6dfa1813f2..e12013367f 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -144,6 +144,26 @@ public static function get_four_range_properties() { return Stackable_Global_Settings::create_global_schema( $four_range_type ); } + + /** + * This function defines a schema for a string four-range type and utilizes the + * `create_global_schema` function to generate the complete schema. + * + * @return array The generated schema for four-range type. + */ + public static function get_string_four_range_properties() { + $string_four_range_type = array( + 'type' => 'object', + 'properties' => array( + 'top' => array( 'type' => 'string', 'default' => '' ), + 'right' => array( 'type' => 'string', 'default' => '' ), + 'bottom' => array( 'type' => 'string', 'default' => '' ), + 'left' => array( 'type' => 'string', 'default' => '' ), + ) + ); + + return Stackable_Global_Settings::create_global_schema( $string_four_range_type ); + } /** * This function defines a schema for a string type and utilizes the @@ -1019,7 +1039,7 @@ public static function generate_global_block_layouts( $option_name, $settings_na $custom_property .= '-' . $hover_state; } - if ( is_string( $value ) ) { + if ( is_string( $value ) && ! is_numeric( $value ) ) { if ( strpos( $value, 'rgb' ) ) { // Convert rgba colors to hex alpha colors because // the function wp_style_engine_get_stylesheet_from_css_rules() doesn't allow css values to have '(' @@ -1050,7 +1070,10 @@ public static function generate_global_block_layouts( $option_name, $settings_na $bottom = isset( $value[ 'bottom' ] ) ? $value[ 'bottom' ] : $default_value[ 'bottom' ]; $left = isset( $value[ 'left' ] ) ? $value[ 'left' ] : $default_value[ 'left' ]; - $style = $top . $unit . ' ' . $right . $unit . ' ' . $bottom . $unit . ' ' . $left . $unit; + $style = Stackable_Global_Settings::append_unit_if_needed( $top, $unit ) . ' ' + . Stackable_Global_Settings::append_unit_if_needed( $right, $unit ) . ' ' + . Stackable_Global_Settings::append_unit_if_needed( $bottom, $unit ) . ' ' + . Stackable_Global_Settings::append_unit_if_needed( $left, $unit ); } else { $style = $value . $unit; } @@ -1100,6 +1123,14 @@ public static function generate_global_block_layouts( $option_name, $settings_na return $generated_css; } + public static function append_unit_if_needed( $value, $unit ) { + if ( is_string( $value ) && str_starts_with( trim( $value ), 'var(' ) ) { + return $value; + } + return $value . $unit; + } + + public static function extract_rgba($value) { $options = $value; $color = ''; diff --git a/src/plugins/global-settings/buttons-and-icons/index.js b/src/plugins/global-settings/buttons-and-icons/index.js index 00b117c9b2..776611352e 100644 --- a/src/plugins/global-settings/buttons-and-icons/index.js +++ b/src/plugins/global-settings/buttons-and-icons/index.js @@ -23,6 +23,7 @@ import { } from '../utils' import { BORDER_CONTROLS } from '~stackable/block-components' import { i18n } from 'stackable' +import { usePresetControls } from '~stackable/hooks' /** * WordPress dependencies @@ -54,6 +55,17 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-buttons-and- saveTimeout ) + // Add additional presets for setting margins and paddings to None + const nonePreset = { + name: 'None', + size: '0rem', + slug: 'none', + } + const sizePresetMarks = usePresetControls( 'spacingSizes' ) + ?.getPresetMarks( { additionalPresets: [ nonePreset ] } ) || null + const gapPresetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + const borderRadiusPresetMarks = usePresetControls( 'borderRadius' )?.getPresetMarks() || null + return ( <> { output } @@ -112,6 +124,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-buttons-and- onChange={ value => onChange( 'button-min-height', value, STATES.RESPONSIVE ) } hasTabletValue={ getHasDeviceValue( 'button-min-height', 'tablet' ) } hasMobileValue={ getHasDeviceValue( 'button-min-height', 'mobile' ) } + marks={ sizePresetMarks } /> onChange( 'button-column-gap', value, STATES.RESPONSIVE ) } hasTabletValue={ getHasDeviceValue( 'button-column-gap', 'tablet' ) } hasMobileValue={ getHasDeviceValue( 'button-column-gap', 'mobile' ) } + marks={ gapPresetMarks } /> onChange( 'button-row-gap', value, STATES.RESPONSIVE ) } hasTabletValue={ getHasDeviceValue( 'button-row-gap', 'tablet' ) } hasMobileValue={ getHasDeviceValue( 'button-row-gap', 'mobile' ) } + marks={ gapPresetMarks } /> @@ -294,6 +311,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-buttons-and- title: __( 'Button padding', i18n ), description: __( 'Adjusts the space between the button text and button borders', i18n ), } } + marks={ sizePresetMarks } /> onChange( 'icon-list-row-gap', value, STATES.RESPONSIVE ) } hasTabletValue={ getHasDeviceValue( 'icon-list-row-gap', 'tablet' ) } hasMobileValue={ getHasDeviceValue( 'icon-list-row-gap', 'mobile' ) } + marks={ gapPresetMarks } /> array( 'schema' => array( 'properties' => array( - 'button-min-height' => $number_properties, - 'button-padding' => $four_range_properties, - 'icon-button-padding' => $four_range_properties, + 'button-min-height' => $string_properties, + 'button-padding' => $string_four_range_properties, + 'icon-button-padding' => $string_four_range_properties, 'button-border-style' => $string_properties, 'button-border-width' => $four_range_properties, 'button-ghost-border-width' => $four_range_properties, - 'button-border-radius' => $four_range_properties, + 'button-border-radius' => $string_four_range_properties, 'button-box-shadow' => $string_properties, 'button-icon-size' => $number_properties, - 'button-icon-gap' => $number_properties, - 'button-column-gap' => $number_properties, - 'button-row-gap' => $number_properties, + 'button-icon-gap' => $string_properties, + 'button-column-gap' => $string_properties, + 'button-row-gap' => $string_properties, 'icon-list-icon-size' => $number_properties, - 'icon-list-row-gap' => $number_properties, + 'icon-list-row-gap' => $string_properties, 'icon-list-icon-gap' => $number_properties, 'icon-list-indentation' => $number_properties, diff --git a/src/plugins/global-settings/spacing-and-borders/index.js b/src/plugins/global-settings/spacing-and-borders/index.js index 966d3d1eb0..552cc9ea38 100644 --- a/src/plugins/global-settings/spacing-and-borders/index.js +++ b/src/plugins/global-settings/spacing-and-borders/index.js @@ -23,7 +23,9 @@ import { } from '../utils' import { BORDER_CONTROLS, IMAGE_SHADOWS } from '~stackable/block-components' import { i18n } from 'stackable' -import { useDeviceType, useBlockLayoutDefaults } from '~stackable/hooks' +import { + useDeviceType, useBlockLayoutDefaults, usePresetControls, +} from '~stackable/hooks' /** * WordPress dependencies @@ -54,6 +56,16 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- setDisplayHoverNotice, saveTimeout ) + // Add additional presets for setting margins and paddings to None + const nonePreset = { + name: 'None', + size: '0rem', + slug: 'none', + } + const sizePresetMarks = usePresetControls( 'spacingSizes' ) + ?.getPresetMarks( { additionalPresets: [ nonePreset ] } ) || null + const gapPresetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + const borderRadiusPresetMarks = usePresetControls( 'borderRadius' )?.getPresetMarks() || null return ( <> @@ -120,6 +132,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- video: 'advanced-block-margin', description: __( 'Sets the block margin bottom, i.e. the space outside the block between the block border and the next block.', i18n ), } } + marks={ sizePresetMarks } /> @@ -159,6 +172,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- video: 'column-gap', description: __( 'Sets the distance between two or more columns', i18n ), } } + marks={ sizePresetMarks } /> @@ -187,9 +202,9 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- responsive="all" // hover="all" forceUpdateHoverState={ true } - min={ [ 0, 0, 0 ] } - sliderMax={ [ 200, 30, 100 ] } - units={ [ 'px', 'em', '%' ] } + min={ [ 0, 0, 0, 0 ] } + sliderMax={ [ 200, 30, 30, 100 ] } + units={ [ 'px', 'em', 'rem', '%' ] } unit={ getValue( 'block-background-padding', STATES.ALL_UNIT ) || 'px' } onChangeUnit={ value => onChange( 'block-background-padding', value, STATES.ALL_UNIT ) } top={ getValue( 'block-background-padding', STATES.ALL )?.top } @@ -204,6 +219,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- video: 'inner-block-padding', description: __( 'Sets the block paddings, i.e the space between the inner columns and the block border', i18n ), } } + marks={ sizePresetMarks } /> onChange( 'container-padding', value, STATES.ALL_UNIT ) } top={ getValue( 'container-padding', STATES.ALL )?.top } @@ -291,7 +308,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-spacing-and- video: 'inner-block-padding', description: __( 'Sets the block paddings, i.e the space between the inner columns and the block border', i18n ), } } - + marks={ sizePresetMarks } /> array( 'container-border-style' => $string_properties, 'container-border-width' => $four_range_properties, - 'container-border-radius' => $four_range_properties, + 'container-border-radius' => $string_four_range_properties, 'container-box-shadow' => $string_properties, - 'container-padding' => $four_range_properties, + 'container-padding' => $string_four_range_properties, 'block-background-border-style' => $string_properties, 'block-background-border-width' => $four_range_properties, - 'block-background-border-radius' => $four_range_properties, + 'block-background-border-radius' => $string_four_range_properties, 'block-background-box-shadow' => $string_properties, - 'block-background-padding' => $four_range_properties, + 'block-background-padding' => $string_four_range_properties, - 'block-margin-bottom' => $number_properties, + 'block-margin-bottom' => $string_properties, 'column-margin' => $number_properties, - 'columns-column-gap' => $number_properties, - 'columns-row-gap' => $number_properties, + 'columns-column-gap' => $string_properties, + 'columns-row-gap' => $string_properties, 'image-drop-shadow' => $string_properties, - 'image-border-radius' => $four_range_properties, + 'image-border-radius' => $string_four_range_properties, ) ) ), diff --git a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js index 409dba75dd..6320229663 100644 --- a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js +++ b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js @@ -15,6 +15,10 @@ import { useEffect, useState } from '@wordpress/element' import { compact } from 'lodash' import { useBlockHoverState, useBlockLayoutDefaults } from '~stackable/hooks' +function appendUnitIfNeeded( value, unit ) { + return ( typeof value === 'string' && value.trim().startsWith( 'var' ) ) ? value : `${ value }${ unit }` +} + const renderGlobalStyles = ( blockLayouts, blockLayoutDefaults, @@ -51,7 +55,7 @@ const renderGlobalStyles = ( } let style = '' - if ( typeof value === 'string' ) { + if ( typeof value === 'string' && isNaN( Number( value ) ) ) { style = `${ property }: ${ value };` } else if ( typeof value === 'object' ) { let defaultValue = getDefault( blockLayoutDefaults, _property, device ) @@ -72,7 +76,7 @@ const renderGlobalStyles = ( const bottom = value.bottom !== undefined ? value.bottom : defaultValue.bottom const left = value.left !== undefined ? value.left : defaultValue.left - style = `${ property }: ${ top }${ unit } ${ right }${ unit } ${ bottom }${ unit } ${ left }${ unit };` + style = `${ property }: ${ appendUnitIfNeeded( top, unit ) } ${ appendUnitIfNeeded( right, unit ) } ${ appendUnitIfNeeded( bottom, unit ) } ${ appendUnitIfNeeded( left, unit ) };` } else { style = `${ property }: ${ value }${ unit };` } From feb3274193dc43c893b88dd39861a3a44de6eb43 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 15 May 2025 17:16:50 +0800 Subject: [PATCH 065/124] fix: typography font size steps --- src/components/font-size-control/index.js | 4 ++-- src/global-settings.php | 6 +++--- .../typography/typography-picker.js | 16 +++++++++++++++- src/util/typography/styles.js | 14 +++++++++++--- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/components/font-size-control/index.js b/src/components/font-size-control/index.js index 415578e748..2c7649a0e4 100644 --- a/src/components/font-size-control/index.js +++ b/src/components/font-size-control/index.js @@ -41,9 +41,9 @@ const FontSizeControl = props => { // Change font-size so as not to surprise the user. if ( props.value !== '' ) { if ( value === 'em' || value === 'rem' ) { - props.onChange( pxToEm( props.value ) ) + props.onChange( String( pxToEm( props.value ) ) ) } else if ( value === 'px' ) { - props.onChange( emToPx( props.value ) ) + props.onChange( String( emToPx( props.value ) ) ) } } diff --git a/src/global-settings.php b/src/global-settings.php index e12013367f..f2571a3133 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -299,13 +299,13 @@ public function register_global_settings() { 'type' => 'string', ), 'fontSize' => array( - 'type' => 'number', + 'type' => 'string', ), 'tabletFontSize' => array( - 'type' => 'number', + 'type' => 'string', ), 'mobileFontSize' => array( - 'type' => 'number', + 'type' => 'string', ), 'fontSizeUnit' => array( 'type' => 'string', diff --git a/src/plugins/global-settings/typography/typography-picker.js b/src/plugins/global-settings/typography/typography-picker.js index bf949662ef..2eeaeaa8de 100644 --- a/src/plugins/global-settings/typography/typography-picker.js +++ b/src/plugins/global-settings/typography/typography-picker.js @@ -9,6 +9,7 @@ import { i18n } from 'stackable' import { upperFirst, omit } from 'lodash' import classnames from 'classnames' import { generateStyles } from '~stackable/block-components' +import { usePresetControls } from '~stackable/hooks' /** * WordPress dependencies @@ -45,9 +46,22 @@ const TypographyPicker = props => { 'ugb-global-settings-typography-control--with-description': createDescription( value ), } ) + const useTypographyAsPresets = useSelect( select => + select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false + ) + + const presetMarks = usePresetControls( 'fontSizes' ) + ?.getPresetMarks( { customOnly: useTypographyAsPresets } ) || null + return ( { + return typeof value === 'string' && value.startsWith( 'var' ) +} + export const createTypographyStyles = ( attrNameTemplate = '%s', screen = 'desktop', blockAttributes = {}, options = {} ) => { const getAttrName = attrName => camelCase( sprintf( attrNameTemplate, attrName ) ) const getValue = __getValue( blockAttributes, getAttrName, '' ) @@ -34,10 +38,14 @@ export const createTypographyStyles = ( attrNameTemplate = '%s', screen = 'deskt const tabletFontSize = getValue( 'TabletFontSize' ) const mobileFontSize = getValue( 'MobileFontSize' ) + const desktopFontSizeUnit = isCSSVarValue( desktopFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px' + const tabletFontSizeUnit = isCSSVarValue( tabletFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px' + const mobileFontSizeUnit = isCSSVarValue( mobileFontSize ) ? '' : getValue( 'FontSizeUnit' ) || 'px' + if ( screen !== 'tablet' && screen !== 'mobile' ) { // Desktop. styles = { fontFamily: getValue( 'FontFamily' ) !== '' ? getFontFamily( getValue( 'FontFamily' ) ) : undefined, - fontSize: desktopFontSize !== '' ? appendImportant( `${ desktopFontSize }${ getValue( 'FontSizeUnit' ) || 'px' }`, importantSize ) : undefined, + fontSize: desktopFontSize !== '' ? appendImportant( `${ desktopFontSize }${ desktopFontSizeUnit }`, importantSize ) : undefined, fontWeight: getValue( 'FontWeight' ) !== '' ? getValue( 'FontWeight' ) : undefined, textTransform: getValue( 'TextTransform' ) !== '' ? getValue( 'TextTransform' ) : undefined, letterSpacing: getValue( 'LetterSpacing' ) !== '' ? `${ getValue( 'LetterSpacing' ) }px` : undefined, @@ -56,7 +64,7 @@ export const createTypographyStyles = ( attrNameTemplate = '%s', screen = 'deskt } } if ( tabletFontSize ) { - styles.fontSize = getValue( 'TabletFontSize', `%s${ getValue( 'TabletFontSizeUnit' ) || 'px' }` ) + styles.fontSize = getValue( 'TabletFontSize', `%s${ tabletFontSizeUnit }` ) } } else { // Mobile. styles = { @@ -79,7 +87,7 @@ export const createTypographyStyles = ( attrNameTemplate = '%s', screen = 'deskt } } if ( mobileFontSize ) { - styles.fontSize = getValue( 'MobileFontSize', `%s${ getValue( 'MobileFontSizeUnit' ) || 'px' }` ) + styles.fontSize = getValue( 'MobileFontSize', `%s${ mobileFontSizeUnit }` ) } } From 7352dd2c2c69e32d9cd38bc752b177c31b53fcbe Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 15 May 2025 17:19:33 +0800 Subject: [PATCH 066/124] fix: sync mark mode --- src/components/advanced-range-control/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 33f1316ed1..136ae6bf21 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -130,10 +130,6 @@ const AdvancedRangeControl = props => { // On reset, allow overriding the value. if ( newValue === '' ) { - // Reset should also change from mark mode - if ( isMarkMode ) { - setIsMarkMode( false ) - } const overrideValue = props.onOverrideReset?.() if ( typeof overrideValue !== 'undefined' ) { newValue = overrideValue From b596683628c265a3f48b8fa41ef2a9977d8415c1 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Thu, 15 May 2025 18:38:39 +0800 Subject: [PATCH 067/124] fix: sync state when succeeding call to change styles --- src/components/font-size-control/index.js | 2 +- .../global-settings/typography/index.js | 51 +++++++++---------- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/components/font-size-control/index.js b/src/components/font-size-control/index.js index 2c7649a0e4..49035e6351 100644 --- a/src/components/font-size-control/index.js +++ b/src/components/font-size-control/index.js @@ -39,7 +39,7 @@ const FontSizeControl = props => { placeholder={ passedPlaceholder } onChangeUnit={ value => { // Change font-size so as not to surprise the user. - if ( props.value !== '' ) { + if ( props.value !== '' && ! isNaN( Number( value ) ) ) { if ( value === 'em' || value === 'rem' ) { props.onChange( String( pxToEm( props.value ) ) ) } else if ( value === 'px' ) { diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index bbaa0c6110..fc6fa6f69e 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -293,40 +293,35 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', } const changeStyles = _typography => { - const typography = cloneDeep( _typography ) - const newSettings = { ...typographySettings } + setTypographySettings( prevTypographySettings => { + const typography = cloneDeep( _typography ) + const newSettings = { ...prevTypographySettings } - Object.entries( typography ).forEach( ( [ selector, styles ] ) => { - if ( ! selector || typeof styles !== 'object' ) { - return - } + Object.entries( typography ).forEach( ( [ selector, styles ] ) => { + if ( ! selector || typeof styles !== 'object' ) { + return + } - // Merge the new styles with the previous, while overwritting similar styles. - // This allow adding styles without removing the previous ones. - // Check if the object is empty, used for resetting the whole setting. - if ( Object.keys( styles ).length !== 0 ) { - styles = { ...newSettings[ selector ], ...styles } - } - /** - * Delete the object keys with empty strings. - * Otherwise, the API will throw an error code 400 - * because of incompatible schema type. - */ - Object.keys( styles ).forEach( key => { - if ( styles[ key ] === '' ) { - delete styles[ key ] + if ( Object.keys( styles ).length !== 0 ) { + styles = { ...newSettings[ selector ], ...styles } } - } ) - newSettings[ selector ] = styles - } ) + Object.keys( styles ).forEach( key => { + if ( styles[ key ] === '' ) { + delete styles[ key ] + } + } ) - // Update the global styles immediately when reset font size is triggered. - if ( Object.values( typography ).some( styles => styles && ! styles.fontSize ) ) { - doAction( 'stackable.global-settings.typography-update-global-styles', newSettings ) - } + newSettings[ selector ] = styles + } ) - updateTypography( newSettings ) + // Update the global styles immediately when reset font size is triggered. + if ( Object.values( typography ).some( styles => styles && ! styles.fontSize ) ) { + doAction( 'stackable.global-settings.typography-update-global-styles', newSettings ) + } + + return newSettings + } ) } const resetStyles = selector => { From 02de0dc0c3b58a69a83b2cac9e4c7859e85f0d02 Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Thu, 15 May 2025 20:51:07 +0800 Subject: [PATCH 068/124] fixed value preview --- .../global-settings/typography/typography-picker.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/plugins/global-settings/typography/typography-picker.js b/src/plugins/global-settings/typography/typography-picker.js index 2eeaeaa8de..3e8779d681 100644 --- a/src/plugins/global-settings/typography/typography-picker.js +++ b/src/plugins/global-settings/typography/typography-picker.js @@ -6,7 +6,9 @@ import { createTypographyStyles, getFontFamilyLabel, loadGoogleFont, } from '~stackable/util' import { i18n } from 'stackable' -import { upperFirst, omit } from 'lodash' +import { + upperFirst, omit, startCase, last, +} from 'lodash' import classnames from 'classnames' import { generateStyles } from '~stackable/block-components' import { usePresetControls } from '~stackable/hooks' @@ -165,7 +167,14 @@ const createDescription = ( styleObject, device = 'desktop' ) => { description.push( upperFirst( styleObject.textTransform ) ) } - return description.join( ', ' ) + // If a css custom property, get just the name names + return description.map( value => { + if ( value.includes( 'var(' ) ) { + const propName = value.match( /var\(([^\),]*)/ )?.[ 1 ] + return startCase( last( propName.split( '--' ) ) ) + } + return value + } ).join( ', ' ) } const TypographyPreview = props => { From 43159bbff7989a1c22036a3ebf4538e51f140272 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 19 May 2025 15:42:49 +0800 Subject: [PATCH 069/124] fix: error in getting css var name --- src/util/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/index.js b/src/util/index.js index f060aaa231..a196c3bcf0 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -420,6 +420,6 @@ export const extractNumbersAndUnits = value => { // Return only the CSS variable name given a CSS variable export const getCSSVarName = value => { - const match = value.match( /var\(\s*([^,)\s]+)/ ) + const match = value?.match( /var\(\s*([^,)\s]+)/ ) return match ? match[ 1 ] : null } From 3b6e30e52f3bfaf0f17d6991159cc13783e7f751 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 19 May 2025 22:14:42 +0800 Subject: [PATCH 070/124] feat: add use size preset by default option --- .../advanced-range-control/index.js | 16 +++--- src/components/four-range-control/index.js | 15 +++--- .../global-settings/preset-controls/index.php | 50 +++++++++++++++++++ src/welcome/admin.js | 14 +++++- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 136ae6bf21..d941127fa0 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -6,6 +6,10 @@ import { useControlHandlers } from '../base-control2/hooks' import AdvancedControl, { extractControlProps } from '../base-control2' import DynamicContentControl, { useDynamicContentControlProps } from '../dynamic-content-control' import { ResetButton } from '../base-control2/reset-button' +/** + * External dependencies + */ +import { isEqual } from 'lodash' import { useAttributeName, useBlockAttributesContext, @@ -14,11 +18,7 @@ import { useDeviceType, } from '~stackable/hooks' import { extractNumbersAndUnits, getCSSVarName } from '~stackable/util' - -/** - * External dependencies - */ -import { isEqual } from 'lodash' +import { settings as stackableSettings } from 'stackable' /** * WordPress dependencies @@ -57,6 +57,8 @@ const AdvancedRangeControl = props => { ? ( props.unit || props.units?.[ 0 ] || 'px' ) : ( unitAttribute || '' ) + const isMarkModeDefault = !! ( stackableSettings?.stackable_use_size_presets_by_default ?? true ) + // Change the min, max & step values depending on the unit used. if ( hasUnits ) { const i = props.units.indexOf( unit ) < 0 ? 0 : props.units.indexOf( unit ) @@ -109,7 +111,8 @@ const AdvancedRangeControl = props => { // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom - let isMarkValue = !! props.marks + // If no initial value, use the given default from the settings + let isMarkValue = !! props.marks && isMarkModeDefault if ( props.marks && derivedValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. @@ -285,6 +288,7 @@ AdvancedRangeControl.defaultProps = { marks: undefined, // [{ value: 'var(--stk-preset-font-size-small', name: 'S' }] allowCustom: true, isCustomPreset: false, + isMarkModeDefault: true, } export default memo( AdvancedRangeControl, isEqual ) diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 492a9bd115..1cb06df5e4 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -35,7 +35,7 @@ import { dispatch } from '@wordpress/data' */ import { isEqual } from 'lodash' import classnames from 'classnames' -import { i18n } from 'stackable' +import { i18n, settings as stackableSettings } from 'stackable' import { Button } from '~stackable/components' import { useAttributeName, @@ -122,6 +122,8 @@ const FourRangeControl = memo( props => { } } ) + const isMarkModeDefault = !! ( stackableSettings?.stackable_use_size_presets_by_default ?? true ) + // Change the min, max & step values depending on the unit used. if ( hasUnits ) { const i = props.units.indexOf( unit ) < 0 ? 0 : props.units.indexOf( unit ) @@ -187,12 +189,13 @@ const FourRangeControl = memo( props => { // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom + // If no initial value, use the given default from the settings const isMarkValue = { - first: !! props.marks, - top: !! props.marks, - right: !! props.marks, - bottom: !! props.marks, - left: !! props.marks, + first: !! props.marks && isMarkModeDefault, + top: !! props.marks && isMarkModeDefault, + right: !! props.marks && isMarkModeDefault, + bottom: !! props.marks && isMarkModeDefault, + left: !! props.marks && isMarkModeDefault, } if ( props.marks && firstValue ) { // Check if the current value exists in the marks only by their CSS variable name diff --git a/src/plugins/global-settings/preset-controls/index.php b/src/plugins/global-settings/preset-controls/index.php index 682ea3baa9..2e16f04258 100644 --- a/src/plugins/global-settings/preset-controls/index.php +++ b/src/plugins/global-settings/preset-controls/index.php @@ -42,10 +42,54 @@ class Stackable_Size_And_Spacing_Preset_Controls { * Initialize */ function __construct() { + add_action( 'register_stackable_global_settings', array( $this, 'register_use_size_presets_by_default' ) ); + add_action( 'stackable_early_version_upgraded', array( $this, 'use_size_presets_by_default_set_default' ), 10, 2 ); + add_filter( 'stackable_js_settings', array( $this, 'add_setting' ) ); + add_filter( 'stackable_inline_styles_nodep', array( $this, 'add_preset_controls_styles' ) ); add_filter( 'stackable_inline_editor_styles', array( $this, 'add_preset_controls_styles' ) ); } + // Register the setting for using presets by default + function register_use_size_presets_by_default() { + register_setting( + 'stackable_global_settings', + 'stackable_use_size_presets_by_default', + array( + 'type' => 'boolean', + 'description' => __( 'If enabled, range controls will be on preset mode by default', STACKABLE_I18N ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => true, + ) + ); + } + + /** + * When upgrading to v3.16.0 and above, set option to false. + * If new installation, set option to true. + * + * @since 3.16.0 + */ + public function use_size_presets_by_default_set_default( $old_version, $new_version ) { + if ( ! empty( $old_version ) && version_compare( $old_version, "3.16.0", "<" ) ) { + if ( ! get_option( 'stackable_use_size_presets_by_default' ) ) { + update_option( 'stackable_use_size_presets_by_default', '' ); + } + } + } + + // Make the setting available in the editor + public function add_setting( $settings ) { + $settings['stackable_use_size_presets_by_default'] = get_option( 'stackable_use_size_presets_by_default' ); + return $settings; + } + + /** + * Loads the different preset values. + * + * @return void + */ public function load_presets() { $this->custom_presets = get_option( 'stackable_global_custom_preset_controls' ); $this->theme_presets = WP_Theme_JSON_Resolver::get_theme_data()->get_settings(); @@ -57,6 +101,12 @@ public static function sanitize_array_setting( $input ) { return ! is_array( $input ) ? array( array() ) : $input; } + /** + * Loads and decodes a JSON file, returning the settings array if available. + * + * @param string $json_path Absolute path to the JSON file. + * @return array The settings array from the decoded JSON file, or an empty array. + */ private function load_json_file( $json_path ) { if ( file_exists( $json_path ) ) { $decoded_data = wp_json_file_decode( $json_path, [ diff --git a/src/welcome/admin.js b/src/welcome/admin.js index a428c661f0..0f6e968f4b 100644 --- a/src/welcome/admin.js +++ b/src/welcome/admin.js @@ -77,12 +77,12 @@ const SEARCH_TREE = [ children: [ __( 'Nested Block Width', i18n ), __( 'Nested Wide Block Width', i18n ), - __( 'Stackable Text as Default Block', i18n ), ], }, { id: 'editor', children: [ + __( 'Stackable Text as Default Block', i18n ), __( 'Design Library', i18n ), __( 'Stackable Settings', i18n ), __( 'Block Linking (Beta)', i18n ), @@ -155,6 +155,7 @@ const SEARCH_TREE = [ id: 'global-settings', children: [ __( 'Force Typography Styles', i18n ), + __( 'Use Size Presets by Default', i18n ), ], }, ], @@ -672,7 +673,7 @@ const EditorSettings = props => {

    { __( 'You can customize some of the features and behavior of Stackable in the editor here.' ) }

    { handleSettingsChange( { stackable_enable_text_default_block: value } ) // eslint-disable-line camelcase @@ -1162,6 +1163,15 @@ const GlobalSettings = props => { disabled={ __( 'Not forced', i18n ) } enabled={ __( 'Force styles', i18n ) } /> + { + props.handleSettingsChange( { stackable_use_size_presets_by_default: value } ) // eslint-disable-line camelcase + } } + help={ __( 'If enabled, range controls will be on preset mode by default.', i18n ) } + /> } From c9287935822805cc5f875bff0fba2464c33e0139 Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sat, 24 May 2025 23:08:30 +0800 Subject: [PATCH 071/124] added more descriptive text --- src/welcome/admin.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/welcome/admin.js b/src/welcome/admin.js index 0f6e968f4b..aa5db65b36 100644 --- a/src/welcome/admin.js +++ b/src/welcome/admin.js @@ -1171,6 +1171,8 @@ const GlobalSettings = props => { props.handleSettingsChange( { stackable_use_size_presets_by_default: value } ) // eslint-disable-line camelcase } } help={ __( 'If enabled, range controls will be on preset mode by default.', i18n ) } + disabled={ __( 'Use custom values', i18n ) } + enabled={ __( 'Use presets', i18n ) } /> } From 12911644761c474646e6fe008ca4107b99792214 Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sat, 24 May 2025 23:14:14 +0800 Subject: [PATCH 072/124] temporarily skip since the test breaks now --- e2e/tests/global-settings.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 9f59b43091..af798e946f 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -78,7 +78,7 @@ test.describe( 'Global Settings', () => { expect( _count ).toBeLessThan( count ) } ) - test( 'Global Typography Styles should be applied when adding a heading', async ( { + test.skip( 'Global Typography Styles should be applied when adding a heading', async ( { page, editor, } ) => { From 9e2ee7d4eb88589a8f925bf84a2b47070c105701 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 10:42:38 +0800 Subject: [PATCH 073/124] fix: two-way value conversion between preset and custom mode --- src/components/advanced-range-control/index.js | 11 +++++++++-- src/components/four-range-control/index.js | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index d941127fa0..1b1b404f7f 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -192,8 +192,15 @@ const AdvancedRangeControl = props => { let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - return _value === derivedValue + let _unit, _value + // If the derivedValue is a CSS variable, compare with mark's CSS variable. + // Otherwise, the derivedValue is custom, so compare with raw size and units + if ( typeof derivedValue === 'string' && derivedValue.startsWith( 'var' ) ) { + [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + } else { + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + } + return _value === derivedValue && ( _unit === '' || _unit === unit ) } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 1cb06df5e4..06d3a3c903 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -349,8 +349,15 @@ const FourRangeControl = memo( props => { let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - return _value === initialValue + let _unit, _value + // If the initialValue is a CSS variable, compare with mark's CSS variable. + // Otherwise, the initialValue is custom, so compare with raw size and units + if ( typeof initialValue === 'string' && initialValue.startsWith( 'var' ) ) { + [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + } else { + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + } + return _value === initialValue && ( _unit === '' || _unit === unit ) } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { From 523668a843981ae35ac29fe5e9b22e55b4ef61ca Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sun, 25 May 2025 11:44:05 +0800 Subject: [PATCH 074/124] version bump to 3.16.0 for the default option to kick in --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 8ec5789ca3..dbac78389d 100644 --- a/plugin.php +++ b/plugin.php @@ -6,7 +6,7 @@ * Author: Gambit Technologies, Inc * Author URI: http://gambit.ph * Text Domain: stackable-ultimate-gutenberg-blocks - * Version: 3.15.3 + * Version: 3.16.0 * * @package Stackable */ From 85c3c9d3e77551911b37744402c18c356733b31e Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sun, 25 May 2025 11:51:02 +0800 Subject: [PATCH 075/124] version bump to 3.16.0 for the default option to kick in --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index dbac78389d..2bacde1da5 100644 --- a/plugin.php +++ b/plugin.php @@ -24,7 +24,7 @@ defined( 'STACKABLE_SHOW_PRO_NOTICES' ) || define( 'STACKABLE_SHOW_PRO_NOTICES', true ); defined( 'STACKABLE_BUILD' ) || define( 'STACKABLE_BUILD', 'free' ); -defined( 'STACKABLE_VERSION' ) || define( 'STACKABLE_VERSION', '3.15.3' ); +defined( 'STACKABLE_VERSION' ) || define( 'STACKABLE_VERSION', '3.16.0' ); defined( 'STACKABLE_FILE' ) || define( 'STACKABLE_FILE', __FILE__ ); defined( 'STACKABLE_I18N' ) || define( 'STACKABLE_I18N', 'stackable-ultimate-gutenberg-blocks' ); // Plugin slug. defined( 'STACKABLE_DESIGN_LIBRARY_URL' ) || define( 'STACKABLE_DESIGN_LIBRARY_URL', 'https://storage.googleapis.com/stackable-plugin-assets' ); // Design Library CDN URL From 71766f648a1002bd4534d5712d343787899b2d2f Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sun, 25 May 2025 12:13:44 +0800 Subject: [PATCH 076/124] prevent errors if upgrading and value is a number --- src/util/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/index.js b/src/util/index.js index a196c3bcf0..1cceafb327 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -420,6 +420,9 @@ export const extractNumbersAndUnits = value => { // Return only the CSS variable name given a CSS variable export const getCSSVarName = value => { + if ( typeof value !== 'string' ) { + return null + } const match = value?.match( /var\(\s*([^,)\s]+)/ ) return match ? match[ 1 ] : null } From d45d9cd4cbf7c279b5cf5de355868cf05c2b12ac Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Sun, 25 May 2025 12:14:12 +0800 Subject: [PATCH 077/124] optimized to prevent repetitive calls --- src/components/advanced-range-control/index.js | 3 ++- src/components/four-range-control/index.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 1b1b404f7f..33c35878b9 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -116,7 +116,8 @@ const AdvancedRangeControl = props => { if ( props.marks && derivedValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( derivedValue ) ) + const derivedValueCssVarName = getCSSVarName( derivedValue ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === derivedValueCssVarName ) isMarkValue = !! matchedMark if ( matchedMark ) { derivedValue = matchedMark.value diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 06d3a3c903..cd63cef175 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -200,14 +200,16 @@ const FourRangeControl = memo( props => { if ( props.marks && firstValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. - const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( firstValue ) ) + const firstValueCssVarName = getCSSVarName( firstValue ) + const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === firstValueCssVarName ) isMarkValue.first = !! firstMatchedMark if ( firstMatchedMark ) { firstValue = firstMatchedMark.value } [ 'top', 'right', 'bottom', 'left' ].forEach( side => { - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === getCSSVarName( value[ side ] ) ) + const sideCssVarName = getCSSVarName( value[ side ] ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === sideCssVarName ) isMarkValue[ side ] = !! matchedMark if ( matchedMark ) { value[ side ] = matchedMark.value From d8a2699b242f5ac53016b0b99001868efb308777 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 14:22:28 +0800 Subject: [PATCH 078/124] fix: update markmode when changing device type --- .../advanced-range-control/index.js | 27 +++++--- src/components/four-range-control/index.js | 62 ++++++++++--------- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 33c35878b9..4237a3e07e 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -23,7 +23,9 @@ import { settings as stackableSettings } from 'stackable' /** * WordPress dependencies */ -import { memo, useState } from '@wordpress/element' +import { + memo, useState, useEffect, +} from '@wordpress/element' import { Button } from '@wordpress/components' import { settings } from '@wordpress/icons' import { dispatch } from '@wordpress/data' @@ -112,18 +114,23 @@ const AdvancedRangeControl = props => { // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom // If no initial value, use the given default from the settings - let isMarkValue = !! props.marks && isMarkModeDefault - if ( props.marks && derivedValue ) { + const [ isMarkMode, setIsMarkMode ] = useState( false ) + + // Set the markMode when at first render and when device type changes + useEffect( () => { + let isMarkValue = !! props.marks && isMarkModeDefault + if ( props.marks && derivedValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. - const derivedValueCssVarName = getCSSVarName( derivedValue ) - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === derivedValueCssVarName ) - isMarkValue = !! matchedMark - if ( matchedMark ) { - derivedValue = matchedMark.value + const derivedValueCssVarName = getCSSVarName( derivedValue ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === derivedValueCssVarName ) + isMarkValue = !! matchedMark + if ( matchedMark ) { + derivedValue = matchedMark.value + } } - } - const [ isMarkMode, setIsMarkMode ] = useState( isMarkValue ) + setIsMarkMode( isMarkValue ) + }, [ deviceType ] ) // If this supports dynamic content, the value should be saved as a String. // Similar if using marks to accomodate CSS variable diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index cd63cef175..6bb260f255 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -25,7 +25,7 @@ import { useControlHandlers } from '../base-control2/hooks' import { Tooltip } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { - Fragment, useState, memo, + Fragment, useState, memo, useEffect, } from '@wordpress/element' import { settings } from '@wordpress/icons' import { dispatch } from '@wordpress/data' @@ -187,36 +187,42 @@ const FourRangeControl = memo( props => { : props.enableBottom ? { desktop: _valueDesktop?.bottom, tablet: _valueTablet?.bottom } : { desktop: _valueDesktop?.left, tablet: _valueTablet?.left } - // Is value at first render the same as a step value? If so, do mark mode - // at the start, or show custom - // If no initial value, use the given default from the settings - const isMarkValue = { - first: !! props.marks && isMarkModeDefault, - top: !! props.marks && isMarkModeDefault, - right: !! props.marks && isMarkModeDefault, - bottom: !! props.marks && isMarkModeDefault, - left: !! props.marks && isMarkModeDefault, - } - if ( props.marks && firstValue ) { - // Check if the current value exists in the marks only by their CSS variable name - // to match in case the fallback size changes. - const firstValueCssVarName = getCSSVarName( firstValue ) - const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === firstValueCssVarName ) - isMarkValue.first = !! firstMatchedMark - if ( firstMatchedMark ) { - firstValue = firstMatchedMark.value + const [ isFourMarkMode, setIsFourMarkMode ] = useState( false ) + + // Set the markMode when at first render and when device type changes + useEffect( () => { + // Is value at first render the same as a step value? If so, do mark mode + // at the start, or show custom + // If no initial value, use the given default from the settings + const isMarkValue = { + first: !! props.marks && isMarkModeDefault, + top: !! props.marks && isMarkModeDefault, + right: !! props.marks && isMarkModeDefault, + bottom: !! props.marks && isMarkModeDefault, + left: !! props.marks && isMarkModeDefault, } - [ 'top', 'right', 'bottom', 'left' ].forEach( side => { - const sideCssVarName = getCSSVarName( value[ side ] ) - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === sideCssVarName ) - isMarkValue[ side ] = !! matchedMark - if ( matchedMark ) { - value[ side ] = matchedMark.value + if ( props.marks && firstValue ) { + // Check if the current value exists in the marks only by their CSS variable name + // to match in case the fallback size changes. + const firstValueCssVarName = getCSSVarName( firstValue ) + const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === firstValueCssVarName ) + isMarkValue.first = !! firstMatchedMark + if ( firstMatchedMark ) { + firstValue = firstMatchedMark.value } - } ) - } - const [ isFourMarkMode, setIsFourMarkMode ] = useState( isMarkValue ) + + [ 'top', 'right', 'bottom', 'left' ].forEach( side => { + const sideCssVarName = getCSSVarName( value[ side ] ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === sideCssVarName ) + isMarkValue[ side ] = !! matchedMark + if ( matchedMark ) { + value[ side ] = matchedMark.value + } + } ) + setIsFourMarkMode( isMarkValue ) + } + }, [ deviceType ] ) const onChangeAll = _newValue => { const newValue = props.marks ? String( _newValue ) : _newValue From 7e588d25d8f3b440c5881e232f1a73022b4cae8f Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 14:29:00 +0800 Subject: [PATCH 079/124] fix: move use size presets by default to editor settings --- src/welcome/admin.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/welcome/admin.js b/src/welcome/admin.js index aa5db65b36..70a71699c1 100644 --- a/src/welcome/admin.js +++ b/src/welcome/admin.js @@ -85,6 +85,7 @@ const SEARCH_TREE = [ __( 'Stackable Text as Default Block', i18n ), __( 'Design Library', i18n ), __( 'Stackable Settings', i18n ), + __( 'Use Size Presets by Default', i18n ), __( 'Block Linking (Beta)', i18n ), ], }, @@ -155,7 +156,6 @@ const SEARCH_TREE = [ id: 'global-settings', children: [ __( 'Force Typography Styles', i18n ), - __( 'Use Size Presets by Default', i18n ), ], }, ], @@ -698,6 +698,17 @@ const EditorSettings = props => { } } help={ __( 'Adds a button on the top of the editor which gives access to Stackable settings. Note: You won\'t be able to access Stackable settings when this is disabled.', i18n ) } /> + { + props.handleSettingsChange( { stackable_use_size_presets_by_default: value } ) // eslint-disable-line camelcase + } } + help={ __( 'If enabled, range controls will be on preset mode by default.', i18n ) } + disabled={ __( 'Use custom values', i18n ) } + enabled={ __( 'Use presets', i18n ) } + /> { disabled={ __( 'Not forced', i18n ) } enabled={ __( 'Force styles', i18n ) } /> - { - props.handleSettingsChange( { stackable_use_size_presets_by_default: value } ) // eslint-disable-line camelcase - } } - help={ __( 'If enabled, range controls will be on preset mode by default.', i18n ) } - disabled={ __( 'Use custom values', i18n ) } - enabled={ __( 'Use presets', i18n ) } - /> } From 1384c6e5e71c883f818c212cd758468e850043a8 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 17:01:15 +0800 Subject: [PATCH 080/124] fix: saving of new typography --- .../global-settings/typography/index.js | 11 ++++++++++- .../global-settings/typography/utils.js | 18 +++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index fc6fa6f69e..40cb974310 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -302,10 +302,17 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', return } + // Merge the new styles with the previous, while overwritting similar styles. + // This allow adding styles without removing the previous ones. + // Check if the object is empty, used for resetting the whole setting. if ( Object.keys( styles ).length !== 0 ) { styles = { ...newSettings[ selector ], ...styles } } - + /** + * Delete the object keys with empty strings. + * Otherwise, the API will throw an error code 400 + * because of incompatible schema type. + */ Object.keys( styles ).forEach( key => { if ( styles[ key ] === '' ) { delete styles[ key ] @@ -320,6 +327,8 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', doAction( 'stackable.global-settings.typography-update-global-styles', newSettings ) } + updateTypography( newSettings ) + return newSettings } ) } diff --git a/src/plugins/global-settings/typography/utils.js b/src/plugins/global-settings/typography/utils.js index 5e98ee3775..0926745826 100644 --- a/src/plugins/global-settings/typography/utils.js +++ b/src/plugins/global-settings/typography/utils.js @@ -16,14 +16,14 @@ export const getAppliedTypeScale = value => { return } return { - h1: { fontSize: Number( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h2: { fontSize: Number( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h3: { fontSize: Number( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h4: { fontSize: Number( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h5: { fontSize: Number( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - h6: { fontSize: Number( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, - p: { fontSize: 1, fontSizeUnit: 'rem' }, - '.stk-subtitle': { fontSize: Number( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, - '.stk-button__inner-text': { fontSize: 1, fontSizeUnit: 'rem' }, + h1: { fontSize: String( Math.pow( typeScale, 6 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h2: { fontSize: String( Math.pow( typeScale, 5 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h3: { fontSize: String( Math.pow( typeScale, 4 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h4: { fontSize: String( Math.pow( typeScale, 3 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h5: { fontSize: String( Math.pow( typeScale, 2 ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + h6: { fontSize: String( typeScale.toFixed( 3 ) ), fontSizeUnit: 'rem' }, + p: { fontSize: '1', fontSizeUnit: 'rem' }, + '.stk-subtitle': { fontSize: String( ( 1 / typeScale ).toFixed( 3 ) ), fontSizeUnit: 'rem' }, + '.stk-button__inner-text': { fontSize: '1', fontSizeUnit: 'rem' }, } } From 30cc1d35d12349433bb26582cb4adc18af17e34b Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 18:19:21 +0800 Subject: [PATCH 081/124] fix: only cofirm when font family is changed --- .../global-settings/typography/index.js | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 40cb974310..84202b780b 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -328,7 +328,6 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', } updateTypography( newSettings ) - return newSettings } ) } @@ -372,15 +371,29 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', return false } - const getIsChangeConfirmed = () => { + const getIsFontPairChangeConfirmed = () => { // No need to confirm when the current font pair is custom // since changes are saved if ( customFontPairs.find( fontPair => fontPair.name === selectedFontPairName ) ) { return true } + // The confirmation should only occur if the font family has been edited + // since selecting font pair only changes font family + const currentFontPair = getCurrentFontPair() const isDirty = TYPOGRAPHY_TAGS.some( ( { selector } ) => { - return getIsAllowReset( selector ) + if ( isEditingFontPair || ! currentFontPair ) { + return false + } + const fontPairStyle = currentFontPair.typography[ selector ] + const typographyStyle = typographySettings[ selector ] + + if ( ! Array.isArray( typographyStyle ) && + fontPairStyle.fontFamily && + fontPairStyle.fontFamily !== typographyStyle.fontFamily ) { + return true + } + return false } ) if ( isDirty ) { @@ -455,7 +468,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', fontPair={ FONT_PAIRS[ 0 ] } isSelected={ selectedFontPairName === FONT_PAIRS[ 0 ].name } onClick={ () => { - if ( ! getIsChangeConfirmed() ) { + if ( ! getIsFontPairChangeConfirmed() ) { return } updateSelectedFontPair( FONT_PAIRS[ 0 ].name ) @@ -467,7 +480,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', customFontPairs={ customFontPairs } selected={ selectedFontPairName } onClick={ ( name, typography ) => { - if ( ! getIsChangeConfirmed() ) { + if ( ! getIsFontPairChangeConfirmed() ) { return } updateSelectedFontPair( name ) @@ -486,7 +499,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', fontPair={ fontPair } isSelected={ selectedFontPairName === fontPair.name } onClick={ () => { - if ( ! getIsChangeConfirmed() ) { + if ( ! getIsFontPairChangeConfirmed() ) { return } updateSelectedFontPair( fontPair.name ) From 277341f04c5cff216a9087c2c4f21dd04241e41d Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Sun, 25 May 2025 21:43:41 +0800 Subject: [PATCH 082/124] fix: clean style object for comparison --- .../global-settings/typography/index.js | 20 +++++++++---------- .../global-settings/typography/utils.js | 16 +++++++++++++++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 84202b780b..60abc00cab 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -5,7 +5,7 @@ import { GlobalTypographyStyles } from './editor-loader' import TypographyPicker from './typography-picker' import { getThemeStyles } from './get-theme-styles' import FREE_FONT_PAIRS from './font-pairs.json' -import { getAppliedTypeScale } from './utils' +import { getAppliedTypeScale, cleanTypographyStyle } from './utils' /** * External dependencies @@ -313,13 +313,9 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', * Otherwise, the API will throw an error code 400 * because of incompatible schema type. */ - Object.keys( styles ).forEach( key => { - if ( styles[ key ] === '' ) { - delete styles[ key ] - } - } ) + const cleanStyles = cleanTypographyStyle( styles ) || {} - newSettings[ selector ] = styles + newSettings[ selector ] = cleanStyles } ) // Update the global styles immediately when reset font size is triggered. @@ -353,9 +349,10 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const getIsAllowReset = selector => { const currentFontPair = getCurrentFontPair() - const typographyStyle = typographySettings[ selector ] + const typographyStyle = typographySettings[ selector ] || {} if ( ! isEditingFontPair && currentFontPair ) { - const fontPairStyle = currentFontPair.typography[ selector ] + // Clean style object to be consistent with changeStyles operation + const fontPairStyle = cleanTypographyStyle( currentFontPair.typography?.[ selector ] ) || {} if ( ! isEqual( fontPairStyle, typographyStyle ) && ! Array.isArray( typographyStyle ) ) { return true } @@ -385,8 +382,9 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', if ( isEditingFontPair || ! currentFontPair ) { return false } - const fontPairStyle = currentFontPair.typography[ selector ] - const typographyStyle = typographySettings[ selector ] + // Clean style object to be consistent with changeStyles operation + const fontPairStyle = cleanTypographyStyle( currentFontPair.typography?.[ selector ] ) || {} + const typographyStyle = typographySettings[ selector ] || {} if ( ! Array.isArray( typographyStyle ) && fontPairStyle.fontFamily && diff --git a/src/plugins/global-settings/typography/utils.js b/src/plugins/global-settings/typography/utils.js index 0926745826..9758c63c80 100644 --- a/src/plugins/global-settings/typography/utils.js +++ b/src/plugins/global-settings/typography/utils.js @@ -27,3 +27,19 @@ export const getAppliedTypeScale = value => { '.stk-button__inner-text': { fontSize: '1', fontSizeUnit: 'rem' }, } } + +/** + * Remove empty string properties from the given styles object + * + * @param {Object} styles - The typography styles object + * @return {Object} An object with removed empty string properties + */ + +export const cleanTypographyStyle = styles => { + if ( ! styles ) { + return {} + } + return Object.fromEntries( + Object.entries( styles ).filter( ( [ , value ] ) => value !== '' ) + ) +} From 96190c7c2b83a1605ea7230e912b01ef19c0e2cb Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Mon, 26 May 2025 09:47:53 +0800 Subject: [PATCH 083/124] update test for global typography --- e2e/tests/global-settings.spec.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index af798e946f..c4fe99e360 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -78,25 +78,25 @@ test.describe( 'Global Settings', () => { expect( _count ).toBeLessThan( count ) } ) - test.skip( 'Global Typography Styles should be applied when adding a heading', async ( { + test( 'Global Typography Styles should be applied when adding a heading', async ( { page, editor, } ) => { await page.getByLabel( 'Stackable Settings' ).click() await page.getByRole( 'button', { name: 'Global Typography' } ).click() - // Set Global Typography Styles of Heading 2 to have a font-size of 32 + // Set Global Typography Styles of Heading 2 to have a text-transform uppercase await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() - await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) + await page.locator( '.stk-popover .components-base-control:nth-of-type(4)', { hasText: /Transform/ } ).getByRole( 'listbox' ).selectOption( 'uppercase' ) await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() - // Verify if the Heading 2 in Global Typography Styles has correct font size - await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) + // Verify if the Heading 2 in Global Typography Styles has correct text-transform + await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'text-transform', 'uppercase' ) // Open Block Settings await page.getByLabel( 'Settings', { exact: true } ).click() - // Check if the added Stackable Heading Block has a font-size of 32 + // Check if the added Stackable Heading Block has a text-transform uppercase editor.insertBlock( { name: 'stackable/heading', attributes: { @@ -104,7 +104,7 @@ test.describe( 'Global Settings', () => { }, } ) - await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) + await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'text-transform', 'uppercase' ) // Reset Global Typography Styles await page.getByLabel( 'Stackable Settings' ).click() From 961c7d2248b2edb9308a86e92898612165adc300 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 11:23:38 +0800 Subject: [PATCH 084/124] fix: remove unused hasCSSVariableValue --- src/block-components/button/edit.js | 1 - src/block-components/columns/edit.js | 2 -- src/block-components/helpers/borders/edit.js | 1 - src/block-components/helpers/size/edit.js | 3 --- src/block-components/typography/edit.js | 1 - 5 files changed, 8 deletions(-) diff --git a/src/block-components/button/edit.js b/src/block-components/button/edit.js index 9a58b50e3f..3c8fb034fc 100644 --- a/src/block-components/button/edit.js +++ b/src/block-components/button/edit.js @@ -234,7 +234,6 @@ const SizeControls = props => { description: __( 'Adjusts the space between the button text and button borders', i18n ), } } marks={ presetMarks } - hasCSSVariableValue={ true } /> ) } diff --git a/src/block-components/columns/edit.js b/src/block-components/columns/edit.js index 363a0a95ae..3614674539 100644 --- a/src/block-components/columns/edit.js +++ b/src/block-components/columns/edit.js @@ -288,7 +288,6 @@ export const Controls = props => { description: __( 'Sets the distance between two or more columns', i18n ), } } marks={ presetMarks } - hasCSSVariableValue={ true } /> { description: __( 'Sets the distance between two or more columns', i18n ), } } marks={ presetMarks } - hasCSSVariableValue={ true } /> ) } diff --git a/src/block-components/helpers/borders/edit.js b/src/block-components/helpers/borders/edit.js index ffd96697d2..5eefbb78d1 100644 --- a/src/block-components/helpers/borders/edit.js +++ b/src/block-components/helpers/borders/edit.js @@ -135,7 +135,6 @@ export const BorderControls = props => { sliderMax={ props.borderSliderMax } placeholder={ props.borderRadiusPlaceholder } marks={ presetMarks } - hasCSSVariableValue={ true } /> } { } } visualGuide={ props.visualGuide } marks={ presetMarks } - hasCSSVariableValue={ true } /> } { props.hasContentVerticalAlign && @@ -166,7 +165,6 @@ const Spacing = props => { visualGuide={ paddingVisualGuide } placeholder={ props.paddingPlaceholder } marks={ presetMarks } - hasCSSVariableValue={ true } /> { props.enableMargin && @@ -185,7 +183,6 @@ const Spacing = props => { } } visualGuide={ marginVisualGuide } marks={ presetMarks } - hasCSSVariableValue={ true } /> } diff --git a/src/block-components/typography/edit.js b/src/block-components/typography/edit.js index 53d7590e92..fc34d1fb3b 100644 --- a/src/block-components/typography/edit.js +++ b/src/block-components/typography/edit.js @@ -277,7 +277,6 @@ export const Controls = props => { description: __( 'Sets the size of text characters', i18n ), } } marks={ presetMarks } - hasCSSVariableValue={ true } /> { hasColor && ( From 0b671a4a2768ead5f4c34ff94eee2c946505d637 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 13:25:18 +0800 Subject: [PATCH 085/124] fix: add inner column spacing presets --- src/block-components/columns/attributes.js | 12 ++++++------ src/block-components/columns/deprecated/index.js | 11 ++++++++++- src/block-components/columns/edit.js | 1 + src/block-components/columns/style.js | 8 +++++++- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/block-components/columns/attributes.js b/src/block-components/columns/attributes.js index 253a6ebf28..87ece45466 100644 --- a/src/block-components/columns/attributes.js +++ b/src/block-components/columns/attributes.js @@ -5,12 +5,6 @@ export const addAttributes = ( attrObject, attrNameTemplate = '%s' ) => { attrObject.add( { attributes: { - columnSpacing: { - stkResponsive: true, - stkUnits: 'px', - type: 'number', - default: '', - }, columnWrapDesktop: { // Only applies to desktops type: 'boolean', default: false, @@ -32,6 +26,12 @@ export const addAttributes = ( attrObject, attrNameTemplate = '%s' ) => { type: 'string', default: '', }, + columnSpacing: { + stkResponsive: true, + stkUnits: 'px', + type: 'string', + default: '', + }, }, attrNameTemplate, versionAdded: '3.15.3', diff --git a/src/block-components/columns/deprecated/index.js b/src/block-components/columns/deprecated/index.js index efce36ad69..a489565059 100644 --- a/src/block-components/columns/deprecated/index.js +++ b/src/block-components/columns/deprecated/index.js @@ -13,6 +13,12 @@ export const deprecatedAddAttributes = ( attrObject, attrNameTemplate = '%s' ) = type: 'number', default: '', }, + columnSpacing: { + stkResponsive: true, + stkUnits: 'px', + type: 'number', + default: '', + }, }, attrNameTemplate, versionAdded: '3.0.0', @@ -25,20 +31,23 @@ export const deprecateColumnAndRowGap = { const getAttrName = getAttrNameFunction( attrNameTemplate ) const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + const columnSpacing = getAttribute( 'columnSpacing' ) const columnGap = getAttribute( 'columnGap' ) const rowGap = getAttribute( 'rowGap' ) - return typeof columnGap === 'number' || typeof rowGap === 'number' + return typeof columnSpacing === 'number' || typeof columnGap === 'number' || typeof rowGap === 'number' }, migrate: attrNameTemplate => attributes => { const getAttrName = getAttrNameFunction( attrNameTemplate ) const getAttribute = _attrName => attributes[ getAttrName( _attrName ) ] + const columnSpacing = getAttribute( 'columnSpacing' ) const columnGap = getAttribute( 'columnGap' ) const rowGap = getAttribute( 'rowGap' ) const newAttributes = { ...attributes, + [ getAttrName( 'columnSpacing' ) ]: String( columnSpacing ), [ getAttrName( 'columnGap' ) ]: String( columnGap ), [ getAttrName( 'rowGap' ) ]: String( rowGap ), } diff --git a/src/block-components/columns/edit.js b/src/block-components/columns/edit.js index 3614674539..eec4bc4d6a 100644 --- a/src/block-components/columns/edit.js +++ b/src/block-components/columns/edit.js @@ -270,6 +270,7 @@ export const Controls = props => { // Add a working video description: __( 'Sets column paddings, the space inside the block between the block elements and the column container border', i18n ), } } + marks={ presetMarks } /> { selector: '.%s-column', styleRule: '--stk-columns-spacing', attrName: 'columnSpacing', - hasUnits: 'px', responsive: 'all', + valueCallback: value => { + // Substitute with using format to work with preset controls + if ( typeof value === 'string' && value.startsWith( 'var' ) ) { + return value + } + return value + 'px' + }, } ] ) blockStyleGenerator.addBlockStyles( 'columnGap', [ { From abe4a876857928133f43db6c0086c51575d0d152 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 14:05:21 +0800 Subject: [PATCH 086/124] fix: add inner column > inner column spacing presets --- src/block/column/edit.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/block/column/edit.js b/src/block/column/edit.js index 72b8f7e085..a2c2a6707e 100644 --- a/src/block/column/edit.js +++ b/src/block/column/edit.js @@ -8,7 +8,7 @@ import blockStyles from './style' */ import classnames from 'classnames' import { i18n, version as VERSION } from 'stackable' -import { useBlockLayoutDefaults } from '~stackable/hooks' +import { useBlockLayoutDefaults, usePresetControls } from '~stackable/hooks' import { AdvancedToggleControl, FourRangeControl, @@ -164,6 +164,8 @@ const Edit = props => { // props used by controls to prevent rerenders of all the inspector controls. const InspectorControls = memo( props => { const { getPlaceholder } = useBlockLayoutDefaults() + const presets = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null + return ( <> @@ -187,6 +189,7 @@ const InspectorControls = memo( props => { highlight: 'column-spacing', defaultValue: '12px', } } + marks={ presets } /> From 1cb15b0fd8cdebb812722e9716ee0733b39ec6d5 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 16:46:39 +0800 Subject: [PATCH 087/124] fix: also clean typography style for consistency --- src/plugins/global-settings/typography/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/global-settings/typography/index.js b/src/plugins/global-settings/typography/index.js index 60abc00cab..11d3ad98a7 100644 --- a/src/plugins/global-settings/typography/index.js +++ b/src/plugins/global-settings/typography/index.js @@ -349,7 +349,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', const getIsAllowReset = selector => { const currentFontPair = getCurrentFontPair() - const typographyStyle = typographySettings[ selector ] || {} + const typographyStyle = cleanTypographyStyle( typographySettings[ selector ] ) || {} if ( ! isEditingFontPair && currentFontPair ) { // Clean style object to be consistent with changeStyles operation const fontPairStyle = cleanTypographyStyle( currentFontPair.typography?.[ selector ] ) || {} @@ -384,7 +384,7 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-typography', } // Clean style object to be consistent with changeStyles operation const fontPairStyle = cleanTypographyStyle( currentFontPair.typography?.[ selector ] ) || {} - const typographyStyle = typographySettings[ selector ] || {} + const typographyStyle = cleanTypographyStyle( typographySettings[ selector ] ) || {} if ( ! Array.isArray( typographyStyle ) && fontPairStyle.fontFamily && From 34ef9b91fabd4ae1c0355a282be30d450d261b29 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 16:49:00 +0800 Subject: [PATCH 088/124] fix: consider unit conversion during two-way value conversion between preset and custom mode --- src/components/advanced-range-control/index.js | 18 +++++++++++------- src/components/four-range-control/index.js | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 4237a3e07e..08ea202c36 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -17,7 +17,9 @@ import { useBlockSetAttributesContext, useDeviceType, } from '~stackable/hooks' -import { extractNumbersAndUnits, getCSSVarName } from '~stackable/util' +import { + extractNumbersAndUnits, getCSSVarName, convertToPxIfUnsupported, +} from '~stackable/util' import { settings as stackableSettings } from 'stackable' /** @@ -207,6 +209,9 @@ const AdvancedRangeControl = props => { [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] } else { [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit } return _value === derivedValue && ( _unit === '' || _unit === unit ) } ) @@ -218,12 +223,11 @@ const AdvancedRangeControl = props => { const markValue = props.marks[ value ]?.[ property ] || '0' let [ newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] - // If the attribute has no support for rem, and the - // preset units is rem, convert to px - if ( ( ! hasUnits || ( hasUnits && ! props.units.includes( 'rem' ) ) ) && unit === 'rem' ) { - newValue = `${ parseFloat( newValue ) * 16 }` - unit = 'px' - } + // If the attribute has no support for rem or em, and the + // preset units is rem or em, convert to px + const converted = convertToPxIfUnsupported( props.units, unit, newValue ) + newValue = converted.value + unit = converted.unit // Update the unit. if ( unit ) { diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 6bb260f255..afb19b3bf4 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -44,7 +44,9 @@ import { useBlockHoverState, useBlockSetAttributesContext, } from '~stackable/hooks' -import { extractNumbersAndUnits, getCSSVarName } from '~stackable/util' +import { + extractNumbersAndUnits, getCSSVarName, convertToPxIfUnsupported, +} from '~stackable/util' const isEqualInitial = ( props, value, firstValue ) => { let isEqual = true @@ -364,6 +366,9 @@ const FourRangeControl = memo( props => { [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] } else { [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit } return _value === initialValue && ( _unit === '' || _unit === unit ) } ) @@ -376,12 +381,11 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ value ]?.[ property ] || '0' let [ newValue, unit ] = extractNumbersAndUnits( markValue )[ 0 ] - // If the attribute has no support for rem, and the - // preset units is rem, convert to px - if ( ( ! hasUnits || ( hasUnits && ! props.units.includes( 'rem' ) ) ) && unit === 'rem' ) { - newValue = `${ parseFloat( newValue ) * 16 }` - unit = 'px' - } + // If the attribute has no support for rem or em, and the + // preset units is rem or em, convert to px + const converted = convertToPxIfUnsupported( props.units, unit, newValue ) + newValue = converted.value + unit = converted.unit // Update the unit. if ( unit ) { From 825b58fc04712d9f58b3155fc8fb6d95e735375e Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 21:11:10 +0800 Subject: [PATCH 089/124] fix: consider unit conversion during two-way value conversion between preset and custom mode --- src/util/index.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/util/index.js b/src/util/index.js index 1cceafb327..a727607cb6 100644 --- a/src/util/index.js +++ b/src/util/index.js @@ -426,3 +426,36 @@ export const getCSSVarName = value => { const match = value?.match( /var\(\s*([^,)\s]+)/ ) return match ? match[ 1 ] : null } + +/** + * Convert a value from rem/em to px if the units array doesn't support it. + * + * @param {string[]} units - The list of supported units. + * @param {string} currentUnit - The current unit of the value. + * @param {string|number} currentValue - The current value to convert. + * + * @return {Object} An object containing the converted value and unit. + */ +export function convertToPxIfUnsupported( units, currentUnit, currentValue ) { + const unitMultipliers = { + rem: 16, + em: 16, + } + + const normalizedUnit = currentUnit?.toLowerCase() + + if ( + ( ! units?.length || ! units.includes( normalizedUnit ) ) && + unitMultipliers[ normalizedUnit ] + ) { + return { + value: `${ parseFloat( currentValue ) * unitMultipliers[ normalizedUnit ] }`, + unit: 'px', + } + } + + return { + value: currentValue, + unit: currentUnit, + } +} From 9bd6a279ddd599921c7e7e44a29e5b4d0a6901e0 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Mon, 26 May 2025 21:11:54 +0800 Subject: [PATCH 090/124] fix: remove placeholder for custom css in number input --- src/components/advanced-range-control/range-control.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/advanced-range-control/range-control.js b/src/components/advanced-range-control/range-control.js index cf137a20e4..a3b025a64e 100644 --- a/src/components/advanced-range-control/range-control.js +++ b/src/components/advanced-range-control/range-control.js @@ -144,6 +144,11 @@ const StackableRangeControl = memo( props => { placeholderValue = initialPosition } + // Remove placeholder if it's a custom CSS + if ( typeof placeholderValue === 'string' && placeholderValue.startsWith( 'var' ) ) { + placeholderValue = '' + } + return
    Date: Mon, 26 May 2025 21:12:32 +0800 Subject: [PATCH 091/124] fix: add fallback tooltip for theme.json data with no name like blocksy --- src/components/advanced-range-control/index.js | 2 +- src/components/four-range-control/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index 08ea202c36..c8dc8a47fc 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -175,7 +175,7 @@ const AdvancedRangeControl = props => { ] }, [] ) propsToPass.renderTooltipContent = value => { - return props.marks[ value ]?.name || '' + return props.marks[ value ]?.name || props.marks[ value ]?.slug || '' } // Other necessary props for steps. diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index afb19b3bf4..73c05d3da5 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -339,7 +339,7 @@ const FourRangeControl = memo( props => { ] }, [] ) newProps.renderTooltipContent = value => { - return props.marks[ value ]?.name || '' + return props.marks[ value ]?.name || props.marks[ value ]?.slug || '' } // Other necessary props for steps. From e853ebd73201120c9e78e6ee2ec5459a1dfd296d Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 27 May 2025 09:41:18 +0800 Subject: [PATCH 092/124] fix: bug fixes --- .../advanced-range-control/index.js | 48 +++--- src/components/four-range-control/index.js | 150 +++++++++++++----- src/global-settings.php | 20 ++- 3 files changed, 149 insertions(+), 69 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index c8dc8a47fc..dbfc373777 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -118,19 +118,20 @@ const AdvancedRangeControl = props => { // If no initial value, use the given default from the settings const [ isMarkMode, setIsMarkMode ] = useState( false ) - // Set the markMode when at first render and when device type changes - useEffect( () => { - let isMarkValue = !! props.marks && isMarkModeDefault - if ( props.marks && derivedValue ) { + let isMarkValue = !! props.marks && isMarkModeDefault + if ( props.marks && derivedValue ) { // Check if the current value exists in the marks only by their CSS variable name // to match in case the fallback size changes. - const derivedValueCssVarName = getCSSVarName( derivedValue ) - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === derivedValueCssVarName ) - isMarkValue = !! matchedMark - if ( matchedMark ) { - derivedValue = matchedMark.value - } + const derivedValueCssVarName = getCSSVarName( derivedValue ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === derivedValueCssVarName ) + isMarkValue = !! matchedMark + if ( matchedMark ) { + derivedValue = matchedMark.value } + } + + // Set the markMode when device type changes + useEffect( () => { setIsMarkMode( isMarkValue ) }, [ deviceType ] ) @@ -202,18 +203,8 @@ const AdvancedRangeControl = props => { let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - let _unit, _value - // If the derivedValue is a CSS variable, compare with mark's CSS variable. - // Otherwise, the derivedValue is custom, so compare with raw size and units - if ( typeof derivedValue === 'string' && derivedValue.startsWith( 'var' ) ) { - [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - } else { - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - } - return _value === derivedValue && ( _unit === '' || _unit === unit ) + const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + return _value === derivedValue } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { @@ -266,6 +257,19 @@ const AdvancedRangeControl = props => { // Set the value when changing from mark mode to custom if ( isMarkMode && rangeValue !== -1 ) { rangeOnChange( rangeValue, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + + return _value === derivedValue && ( _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + _onChange( markValue ) } setIsMarkMode( ! isMarkMode ) } } diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 73c05d3da5..644ffef0e5 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -191,39 +191,40 @@ const FourRangeControl = memo( props => { const [ isFourMarkMode, setIsFourMarkMode ] = useState( false ) - // Set the markMode when at first render and when device type changes - useEffect( () => { - // Is value at first render the same as a step value? If so, do mark mode - // at the start, or show custom - // If no initial value, use the given default from the settings - const isMarkValue = { - first: !! props.marks && isMarkModeDefault, - top: !! props.marks && isMarkModeDefault, - right: !! props.marks && isMarkModeDefault, - bottom: !! props.marks && isMarkModeDefault, - left: !! props.marks && isMarkModeDefault, + // Is value at first render the same as a step value? If so, do mark mode + // at the start, or show custom + // If no initial value, use the given default from the settings + const isMarkValue = { + first: !! props.marks && isMarkModeDefault, + top: !! props.marks && isMarkModeDefault, + right: !! props.marks && isMarkModeDefault, + bottom: !! props.marks && isMarkModeDefault, + left: !! props.marks && isMarkModeDefault, + } + + if ( props.marks && firstValue ) { + // Check if the current value exists in the marks only by their CSS variable name + // to match in case the fallback size changes. + const firstValueCssVarName = getCSSVarName( firstValue ) + const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === firstValueCssVarName ) + isMarkValue.first = !! firstMatchedMark + if ( firstMatchedMark ) { + firstValue = firstMatchedMark.value } - if ( props.marks && firstValue ) { - // Check if the current value exists in the marks only by their CSS variable name - // to match in case the fallback size changes. - const firstValueCssVarName = getCSSVarName( firstValue ) - const firstMatchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === firstValueCssVarName ) - isMarkValue.first = !! firstMatchedMark - if ( firstMatchedMark ) { - firstValue = firstMatchedMark.value + [ 'top', 'right', 'bottom', 'left' ].forEach( side => { + const sideCssVarName = getCSSVarName( value[ side ] ) + const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === sideCssVarName ) + isMarkValue[ side ] = !! matchedMark + if ( matchedMark ) { + value[ side ] = matchedMark.value } + } ) + } - [ 'top', 'right', 'bottom', 'left' ].forEach( side => { - const sideCssVarName = getCSSVarName( value[ side ] ) - const matchedMark = props.marks.find( mark => getCSSVarName( mark.value ) === sideCssVarName ) - isMarkValue[ side ] = !! matchedMark - if ( matchedMark ) { - value[ side ] = matchedMark.value - } - } ) - setIsFourMarkMode( isMarkValue ) - } + // Set the markMode when device type changes + useEffect( () => { + setIsFourMarkMode( isMarkValue ) }, [ deviceType ] ) const onChangeAll = _newValue => { @@ -359,18 +360,8 @@ const FourRangeControl = memo( props => { let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - let _unit, _value - // If the initialValue is a CSS variable, compare with mark's CSS variable. - // Otherwise, the initialValue is custom, so compare with raw size and units - if ( typeof initialValue === 'string' && initialValue.startsWith( 'var' ) ) { - [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - } else { - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - } - return _value === initialValue && ( _unit === '' || _unit === unit ) + const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + return _value === initialValue } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { @@ -506,6 +497,17 @@ const FourRangeControl = memo( props => { // Set the value when changing from mark mode to custom if ( isFourMarkMode.first && rangeValueFirst !== -1 ) { rangeOnChangeFirst( rangeValueFirst, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === firstValue && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeAll( markValue ) } setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) } } @@ -569,6 +571,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.top && rangeValueTop !== -1 ) { rangeOnChangeTop( rangeValueTop, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.top && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeVertical( markValue ) } setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } } @@ -628,6 +641,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { rangeOnChangeLeft( rangeValueLeft, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.left && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeHorizontal( markValue ) } setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } } @@ -693,6 +717,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.top && rangeValueTop !== -1 ) { rangeOnChangeTop( rangeValueTop, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.top && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeTop( markValue ) } setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } } @@ -755,6 +790,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.right && rangeValueRight !== -1 ) { rangeOnChangeRight( rangeValueRight, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.right && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeRight( markValue ) } setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) } } @@ -817,6 +863,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.bottom && rangeValueBottom !== -1 ) { rangeOnChangeBottom( rangeValueBottom, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.bottom && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeBottom( markValue ) } setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) } } @@ -879,6 +936,17 @@ const FourRangeControl = memo( props => { onClick={ () => { if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { rangeOnChangeLeft( rangeValueLeft, 'size' ) + } else { + const rangeValue = props.marks.findIndex( mark => { + let _unit, _value + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + const converted = convertToPxIfUnsupported( props.units, _unit, _value ) + _value = converted.value + _unit = converted.unit + return _value === value.left && ( ! unit || _unit === '' || _unit === unit ) + } ) + const markValue = props.marks[ rangeValue ]?.value || '0' + onChangeLeft( markValue ) } setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } } diff --git a/src/global-settings.php b/src/global-settings.php index f2571a3133..490aff3f1f 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -697,6 +697,14 @@ public function form_paragraph_selector() { return $selectors; } + public function clean_font_size( $font_size, $font_size_unit = '' ) { + if ( is_string( $font_size ) && str_starts_with( $font_size, 'var' ) ) { + return $font_size; + } + + return $font_size . $font_size_unit; + } + /** * Creates a CSS style rule with an added !important if necessary. * @@ -753,7 +761,7 @@ public function generate_typography_styles( $selector, $styles ) { $css['desktop'][] = $this->create_style( 'font-family', $this->get_font_family( $styles['fontFamily'] ) ); } if ( isset( $styles['fontSize'] ) ) { - $css['desktop'][] = $this->create_style( 'font-size', $styles['fontSize'] . $styles['fontSizeUnit'] ); + $css['desktop'][] = $this->create_style( 'font-size', $this->clean_font_size( $styles['fontSize'], $styles['fontSizeUnit'] ) ); } if ( isset( $styles['fontWeight'] ) ) { $css['desktop'][] = $this->create_style( 'font-weight', $styles['fontWeight'] ); @@ -782,12 +790,12 @@ public function generate_typography_styles( $selector, $styles ) { if ( isset( $styles['fontSize'] ) ) { $clamp_desktop_value = $this->clamp_inherited_style( $styles['fontSize'], $inherit_max ); if ( ! empty( $clamp_desktop_value ) ) { - $font_size = $this->create_style( 'font-size', $clamp_desktop_value . $styles['fontSizeUnit'] ); + $font_size = $this->create_style( 'font-size', $this->clean_font_size( $clamp_desktop_value . $styles['fontSizeUnit'] ) ); } } } if ( isset( $styles['tabletFontSize'] ) ) { - $font_size = $this->create_style( 'font-size', $styles['tabletFontSize'] . $styles['tabletFontSizeUnit'] ); + $font_size = $this->create_style( 'font-size', $this->clean_font_size( $styles['tabletFontSize'], $styles['tabletFontSizeUnit'] ) ); } if ( ! empty( $font_size ) ) { $css['tablet'][] = $font_size; @@ -810,7 +818,7 @@ public function generate_typography_styles( $selector, $styles ) { if ( isset( $styles['fontSize'] ) ) { $clamp_desktop_value = $this->clamp_inherited_style( $styles['fontSize'], $inherit_max ); if ( ! empty( $clamp_desktop_value ) ) { - $font_size = $this->create_style( 'font-size', $clamp_desktop_value . $styles['fontSizeUnit'] ); + $font_size = $this->create_style( 'font-size', $this->clean_font_size( $clamp_desktop_value, $styles['fontSizeUnit'] ) ); } } @@ -818,7 +826,7 @@ public function generate_typography_styles( $selector, $styles ) { if ( isset( $styles['tabletFontSize'] ) ) { $clamp_tablet_value = $this->clamp_inherited_style( $styles['tabletFontSize'], $inherit_max ); if ( ! empty( $clamp_tablet_value ) ) { - $font_size = $this->create_style( 'font-size', $clamp_tablet_value . $styles['tabletFontSizeUnit'] ); + $font_size = $this->create_style( $this->clean_font_size( 'font-size', $clamp_tablet_value, $styles['tabletFontSizeUnit'] ) ); } } if ( empty( $clamp_tablet_value ) ) { @@ -831,7 +839,7 @@ public function generate_typography_styles( $selector, $styles ) { } } if ( isset( $styles['mobileFontSize'] ) ) { - $font_size = $this->create_style( 'font-size', $styles['mobileFontSize'] . $styles['mobileFontSizeUnit'] ); + $font_size = $this->create_style( 'font-size', $this->clean_font_size( $styles['mobileFontSize'], $styles['mobileFontSizeUnit'] ) ); } if ( ! empty( $font_size ) ) { $css['mobile'][] = $font_size; From 3a660e2d07451ad5468bfa751218ecac911f37a1 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 27 May 2025 17:49:52 +0800 Subject: [PATCH 093/124] fix: bug fix --- src/global-settings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global-settings.php b/src/global-settings.php index 490aff3f1f..2859bb62a2 100644 --- a/src/global-settings.php +++ b/src/global-settings.php @@ -790,7 +790,7 @@ public function generate_typography_styles( $selector, $styles ) { if ( isset( $styles['fontSize'] ) ) { $clamp_desktop_value = $this->clamp_inherited_style( $styles['fontSize'], $inherit_max ); if ( ! empty( $clamp_desktop_value ) ) { - $font_size = $this->create_style( 'font-size', $this->clean_font_size( $clamp_desktop_value . $styles['fontSizeUnit'] ) ); + $font_size = $this->create_style( 'font-size', $this->clean_font_size( $clamp_desktop_value, $styles['fontSizeUnit'] ) ); } } } @@ -898,7 +898,7 @@ public function get_font_family( $font_name ) { * @param {Object} options */ public function clamp_inherited_style( $value, $max = 999999, $min = -999999 ) { - if ( isset( $value ) ) { + if ( isset( $value ) && is_numeric( $value ) ) { $clamped_value = max( $min, min( $max, $value ) ); return $clamped_value !== $value ? $clamped_value : null; } From 218c7b6117c72b2619495351874b8b31e19079e6 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 27 May 2025 21:58:46 +0800 Subject: [PATCH 094/124] fix: four range reactivity --- src/components/four-range-control/index.js | 43 ++++++++++++++-------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 644ffef0e5..0d0c191d0d 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -494,8 +494,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - // Set the value when changing from mark mode to custom - if ( isFourMarkMode.first && rangeValueFirst !== -1 ) { + const previousMarkMode = isFourMarkMode.first + setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) + + if ( previousMarkMode && rangeValueFirst !== -1 ) { rangeOnChangeFirst( rangeValueFirst, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -509,7 +511,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeAll( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) } } icon={ settings } > @@ -569,7 +570,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.top && rangeValueTop !== -1 ) { + const previousMarkMode = isFourMarkMode.top + setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) + + if ( previousMarkMode && rangeValueTop !== -1 ) { rangeOnChangeTop( rangeValueTop, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -583,7 +587,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeVertical( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } } icon={ settings } > @@ -639,7 +642,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { + const previousMarkMode = isFourMarkMode.left + setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) + + if ( previousMarkMode && rangeValueLeft !== -1 ) { rangeOnChangeLeft( rangeValueLeft, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -653,7 +659,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeHorizontal( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } } icon={ settings } > @@ -715,7 +720,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.top && rangeValueTop !== -1 ) { + const previousMarkMode = isFourMarkMode.top + setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) + + if ( previousMarkMode && rangeValueTop !== -1 ) { rangeOnChangeTop( rangeValueTop, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -729,7 +737,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeTop( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) } } icon={ settings } > @@ -788,7 +795,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.right && rangeValueRight !== -1 ) { + const previousMarkMode = isFourMarkMode.right + setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) + + if ( previousMarkMode && rangeValueRight !== -1 ) { rangeOnChangeRight( rangeValueRight, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -802,7 +812,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeRight( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) } } icon={ settings } > @@ -861,7 +870,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.bottom && rangeValueBottom !== -1 ) { + const previousMarkMode = isFourMarkMode.bottom + setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) + + if ( previousMarkMode && rangeValueBottom !== -1 ) { rangeOnChangeBottom( rangeValueBottom, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -875,7 +887,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeBottom( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) } } icon={ settings } > @@ -934,7 +945,10 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - if ( isFourMarkMode.left && rangeValueLeft !== -1 ) { + const previousMarkMode = isFourMarkMode.left + setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) + + if ( previousMarkMode && rangeValueLeft !== -1 ) { rangeOnChangeLeft( rangeValueLeft, 'size' ) } else { const rangeValue = props.marks.findIndex( mark => { @@ -948,7 +962,6 @@ const FourRangeControl = memo( props => { const markValue = props.marks[ rangeValue ]?.value || '0' onChangeLeft( markValue ) } - setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) } } icon={ settings } > From 11a204e5bf632d97439900d844fc4e4ccd38caa9 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 28 May 2025 11:46:28 +0800 Subject: [PATCH 095/124] init --- src/block/design-library/edit.js | 323 +++++++---- .../color-scheme-preview/editor.scss | 12 + src/components/color-scheme-preview/index.js | 7 +- src/components/color-schemes-help/index.js | 16 +- .../design-library-list/default.json | 30 ++ .../design-library-list-item.js | 312 +++++++++-- .../design-library-list/editor.scss | 119 ++-- src/components/design-library-list/index.js | 133 +++-- src/components/design-library-list/util.js | 204 +++++++ .../modal-design-library/block-list.js | 207 ++----- .../modal-design-library/color-list.js | 87 --- .../modal-design-library/editor.scss | 196 ++++++- src/components/modal-design-library/modal.js | 506 +++++++++++------- src/design-library/index.js | 72 +-- src/design-library/init.php | 390 +++++++++++--- src/hooks/use-block-color-schemes.js | 25 +- src/init.php | 5 + .../color-schemes/color-scheme-picker.js | 24 + .../global-settings/color-schemes/index.js | 69 ++- src/plugins/global-settings/editor-loader.js | 15 + .../typography/editor-loader.js | 2 +- src/util/admin.js | 8 +- src/welcome/admin.js | 100 +++- 23 files changed, 1969 insertions(+), 893 deletions(-) create mode 100644 src/components/design-library-list/default.json create mode 100644 src/components/design-library-list/util.js delete mode 100644 src/components/modal-design-library/color-list.js diff --git a/src/block/design-library/edit.js b/src/block/design-library/edit.js index a1f62895e4..78202a7b30 100644 --- a/src/block/design-library/edit.js +++ b/src/block/design-library/edit.js @@ -6,15 +6,13 @@ import { i18n, srcUrl, settings, + cdnUrl, } from 'stackable' import { Button, ModalDesignLibrary, } from '~stackable/components' import { SVGStackableIcon } from '~stackable/icons' -import { - deprecateBlockBackgroundColorOpacity, deprecateContainerBackgroundColorOpacity, deprecateTypographyGradientColor, -} from '~stackable/block-components' import { substituteCoreIfDisabled, BLOCK_STATE } from '~stackable/util' import { substitutionRules } from '../../blocks' @@ -24,17 +22,61 @@ import { substitutionRules } from '../../blocks' import { __ } from '@wordpress/i18n' import { dispatch } from '@wordpress/data' import { - createBlock, parse, createBlocksFromInnerBlocksTemplate, getBlockVariations, getBlockType, + createBlock, createBlocksFromInnerBlocksTemplate, getBlockVariations, getBlockType, } from '@wordpress/blocks' -import { useState } from '@wordpress/element' -import { addFilter, applyFilters } from '@wordpress/hooks' -import { Placeholder } from '@wordpress/components' +import { useRef, useState } from '@wordpress/element' +import { applyFilters } from '@wordpress/hooks' +import { + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + Placeholder, Modal, __experimentalVStack as VStack, Flex, +} from '@wordpress/components' import { useBlockProps } from '@wordpress/block-editor' +import apiFetch from '@wordpress/api-fetch' +const convertBlocksToArray = block => { + const innerBlocks = block.innerBlocks.map( innerBlock => convertBlocksToArray( innerBlock ) ) + return [ block.name, block.attributes, innerBlocks ] +} + +const checkIfImageUrl = async value => { + if ( typeof value !== 'string' ) { + return value + } + + let attributeUrl, libraryUrl + + try { + attributeUrl = new URL( value ) + libraryUrl = new URL( cdnUrl ) + } catch ( error ) { + return value + } + + if ( attributeUrl.origin !== libraryUrl.origin || ! attributeUrl.pathname.startsWith( libraryUrl.pathname ) ) { + return value + } + + const matches = attributeUrl.pathname.match( /\/([^/]+\.(jpe?g|gif|png|mp4|webp))$/i ) + + if ( matches ) { + const result = await apiFetch( { + path: '/stackable/v3/design_library_image', + method: 'POST', + // eslint-disable-next-line camelcase + data: { image_url: attributeUrl.href }, + } ) + if ( result.success ) { + return result.new_url + } + + console.error( 'Stackable Design Library:', result.message ) // eslint-disable-line no-console + return value + } + return value +} // Replaces the current block with a block made out of attributes. -const createBlockWithAttributes = ( blockName, attributes, innerBlocks, design ) => { +const createBlockWithAttributes = async ( blockName, attributes, innerBlocks, substituteBlocks ) => { const disabledBlocks = settings.stackable_block_states || {} // eslint-disable-line camelcase - let hasSubstituted = false // Recursively substitute core blocks to disabled Stackable blocks const traverseBlocksAndSubstitute = blocks => { @@ -45,13 +87,9 @@ const createBlockWithAttributes = ( blockName, attributes, innerBlocks, design ) // Check if the new substituted block is still disabled while ( isDisabled && attempts > 0 ) { - const previousBlockName = block[ 0 ] - block = substituteCoreIfDisabled( ...block, substitutionRules ) + const _blockName = block[ 1 ].originalName || block[ 0 ] + block = substituteCoreIfDisabled( _blockName, block[ 1 ], block[ 2 ], substitutionRules ) isDisabled = block[ 0 ] in disabledBlocks && disabledBlocks[ block[ 0 ] ] === BLOCK_STATE.DISABLED - // If the previous block is different from the new block, substitution has been made - if ( ! hasSubstituted && previousBlockName !== block[ 0 ] ) { - hasSubstituted = true - } attempts-- } @@ -63,83 +101,68 @@ const createBlockWithAttributes = ( blockName, attributes, innerBlocks, design ) if ( ! Array.isArray( block[ 2 ] ) ) { block[ 2 ] = [] } - return block + + const _block = { + name: block[ 0 ], + attributes: block[ 1 ], + innerBlocks: block[ 2 ], + isValid: true, + } + return _block } ) } - // Substitute from the root of the design - [ blockName, attributes, innerBlocks ] = traverseBlocksAndSubstitute( [ [ blockName, attributes, innerBlocks ] ] )[ 0 ] + if ( ! Array.isArray( disabledBlocks ) && substituteBlocks ) { + let block = convertBlocksToArray( { + name: blockName, attributes, innerBlocks, + } ) - if ( hasSubstituted ) { - // eslint-disable-next-line no-alert - alert( 'Notice: Disabled blocks in the design will be substituted with other Stackable or core blocks' ) + block = traverseBlocksAndSubstitute( [ block ] )[ 0 ] + blockName = block.name + attributes = block.attributes + innerBlocks = block.innerBlocks } - // const { replaceBlock } = dispatch( 'core/block-editor' ) + const cleanBlockAttributes = async blocks => { + for ( const block of blocks ) { + const blockName = block.name - // For wireframes, we'll need to apply any default block attributes to - // the blocks. We do this by ensuring that all uniqueIds are removed, - // this prompts the block to generate a new one and give itself a - // default styling. - if ( design.uikit === 'Wireframes' ) { - const hasVariations = getBlockVariations( blockName ).length > 0 - if ( ! hasVariations ) { - attributes.uniqueId = '' - } + // For blocks with variations, do not remove the uniqueId + // since that will prompt the layout picker to show. + const hasVariations = !! getBlockType( blockName ) && getBlockVariations( blockName ).length > 0 + if ( ! hasVariations && block.attributes.uniqueId ) { + delete block.attributes.uniqueId + } + + const customAttributes = block.attributes.customAttributes - // Recursively remove all uniqueIds from all inner blocks. - const removeUniqueId = blocks => { - blocks.forEach( block => { - const blockName = block[ 0 ] + const indexToDelete = customAttributes?.findIndex( attribute => attribute[ 0 ] === 'stk-design-library__bg-target' ) + if ( customAttributes && indexToDelete !== -1 ) { + block.attributes.customAttributes.splice( indexToDelete, 1 ) + } - // For blocks with variations, do not remove the uniqueId - // since that will prompt the layout picker to show. - const hasVariations = !! getBlockType( blockName ) && getBlockVariations( blockName ).length > 0 - if ( ! hasVariations && block[ 1 ].uniqueId ) { - delete block[ 1 ].uniqueId + for ( const attributeName in block.attributes ) { + if ( typeof block.attributes[ attributeName ] === 'string' ) { + const value = String( block.attributes[ attributeName ] ) + block.attributes[ attributeName ] = await checkIfImageUrl( value ) } + } - removeUniqueId( block[ 2 ] ) - } ) + block.innerBlocks = await cleanBlockAttributes( block.innerBlocks ) } - removeUniqueId( innerBlocks ) - } - - const shortBlockName = blockName.replace( /^\w+\//g, '' ) - - // Recursively update the attributes of all inner blocks for the new Color Picker - const migrateToNewColorPicker = blocks => { - blocks?.forEach( block => { - try { - let newAttributes = block[ 1 ] - newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) - newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) - newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) - block[ 1 ] = newAttributes - migrateToNewColorPicker( block[ 2 ] ) - } catch ( error ) { - } - } ) + return blocks } - migrateToNewColorPicker( innerBlocks ) - - addFilter( `stackable.${ shortBlockName }.design.filtered-block-attributes`, 'stackable.design-library.attributes--migrate-to-new-color-picker', attributes => { - let newAttributes = { ...attributes } - newAttributes = deprecateContainerBackgroundColorOpacity.migrate( newAttributes ) - newAttributes = deprecateBlockBackgroundColorOpacity.migrate( newAttributes ) - newAttributes = deprecateTypographyGradientColor.migrate( '%s' )( newAttributes ) - return newAttributes - } ) - - const blockAttributes = applyFilters( `stackable.${ shortBlockName }.design.filtered-block-attributes`, attributes ) + const block = await cleanBlockAttributes( [ { + name: blockName, attributes, innerBlocks, + } ] ) - return createBlock( blockName, blockAttributes, createBlocksFromInnerBlocksTemplate( innerBlocks ) ) -} + blockName = block[ 0 ].name + attributes = block[ 0 ].attributes + innerBlocks = block[ 0 ].innerBlocks -const createBlockWithContent = serializedBlock => { - return parse( serializedBlock ) + return createBlock( blockName, attributes, createBlocksFromInnerBlocksTemplate( innerBlocks ) ) } const Edit = props => { @@ -149,11 +172,76 @@ const Edit = props => { } = props const [ isLibraryOpen, setIsLibraryOpen ] = useState( false ) + const [ isDialogOpen, setIsDialogOpen ] = useState( false ) + + const designsRef = useRef( [] ) + const disabledBlocksRef = useRef( [] ) + const callbackRef = useRef( null ) const blockProps = useBlockProps( { className: 'ugb-design-library-block', } ) + const addDesigns = async substituteBlocks => { + if ( ! designsRef.current?.length ) { + console.error( 'Design library selection failed: No designs found' ) // eslint-disable-line no-console + } + + const designs = designsRef.current + const blocks = [] + + for ( const designData of designs ) { + const { + name, attributes, innerBlocks, + } = designData + if ( name && attributes ) { + const block = await createBlockWithAttributes( name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], substituteBlocks ) + blocks.push( block ) + } else { + console.error( 'Design library selection failed: No block data found' ) // eslint-disable-line no-console + } + } + + if ( blocks.length ) { + dispatch( 'core/block-editor' ).replaceBlocks( clientId, blocks ) + if ( callbackRef.current ) { + callbackRef.current() + } + } + } + + const onClickTertiary = () => { + const disabledBlocks = disabledBlocksRef.current + + const settingsPageUrl = `/wp-admin/options-general.php?page=stackable` + const newWindow = window.open( settingsPageUrl, '_blank' ) + + if ( newWindow ) { + newWindow.onload = () => { // Wait for the new page to fully load + setTimeout( () => { + try { + const blocksTab = newWindow.document.getElementById( 'stk-tab__blocks' ) + if ( blocksTab ) { + blocksTab.click() + + newWindow.postMessage( { + type: 'STK_ENABLE_BLOCKS', blocks: disabledBlocks, source: 'STK_DESIGN_LIBRARY', + }, window.location.origin ) + } + } catch ( error ) {} + }, 5 ) + } + newWindow.focus() + } + } + + const onClickSecondary = async () => { + await addDesigns( false ) + } + const onClickPrimary = async () => { + await addDesigns( true ) + } + if ( attributes.previewMode ) { const src = previewImage.match( /https?:/i ) ? previewImage : srcUrl ? `${ srcUrl }/${ previewImage }` @@ -187,35 +275,74 @@ const Edit = props => { onClose={ () => { setIsLibraryOpen( false ) } } - onSelect={ ( _designData, _design, callback = null ) => { - const designData = ! Array.isArray( _designData ) ? [ _designData ] : _designData - const designs = ! Array.isArray( _design ) ? [ _design ] : _design - - const blocks = designData.reduce( ( blocks, designData, i ) => { - const design = designs[ i ] - const { - name, attributes, innerBlocks, serialized, - } = designData - if ( name && attributes ) { - const block = createBlockWithAttributes( name, applyFilters( 'stackable.design-library.attributes', attributes ), innerBlocks || [], design ) - blocks.push( block ) - } else if ( serialized ) { - blocks.push( createBlockWithContent( serialized ) ) - } else { - console.error( 'Design library selection failed: No block data found' ) // eslint-disable-line no-console - } - return blocks - }, [] ) + onSelect={ async ( _designs, callback ) => { + const designs = [] + let disabledBlocks = new Set() + + _designs.forEach( design => { + const { designData, blocksForSubstitution } = design - if ( blocks.length ) { - dispatch( 'core/block-editor' ).replaceBlocks( clientId, blocks ) - if ( callback ) { - callback() + if ( blocksForSubstitution.size ) { + disabledBlocks = disabledBlocks.union( blocksForSubstitution ) } + + designs.push( designData ) + } ) + + designsRef.current = designs + disabledBlocksRef.current = disabledBlocks + callbackRef.current = callback + + if ( disabledBlocks.size ) { + setIsDialogOpen( true ) + return } + + await addDesigns( false ) } } /> } + { isDialogOpen && + setIsDialogOpen( false ) } + > + +
    + { __( 'The designs you have selected contain the following disabled blocks:', i18n ) } +
      + { disabledBlocksRef.current && [ ...disabledBlocksRef.current ].map( ( block, i ) =>
    • { block }
    • ) } +
    + { __( 'These blocks can be enabled in the Stackable settings page. Do you want to keep the disabled blocks or substitute them with other Stackable or core blocks?', i18n ) } +
    + + + + + +
    + +
    + }
    ) } diff --git a/src/components/color-scheme-preview/editor.scss b/src/components/color-scheme-preview/editor.scss index 3960c92a55..6f7347181d 100644 --- a/src/components/color-scheme-preview/editor.scss +++ b/src/components/color-scheme-preview/editor.scss @@ -76,3 +76,15 @@ .stk-scheme--is-disabled { opacity: 0.1; } + +.stk-scheme--is-collapsed { + flex-direction: row; + gap: 4px; + align-items: center; + max-height: 30px; + + .stk-global-color-scheme__preview__typography { + margin-bottom: 0; + font-size: 12px; + } +} diff --git a/src/components/color-scheme-preview/index.js b/src/components/color-scheme-preview/index.js index 2389b92799..3dcff64a4b 100644 --- a/src/components/color-scheme-preview/index.js +++ b/src/components/color-scheme-preview/index.js @@ -14,13 +14,18 @@ export const DEFAULT_COLOR_SCHEME_COLORS = { const NOOP = () => {} const ColorSchemePreview = ( { - colors, withWrapper = false, onClick = NOOP, isDisabled = false, + colors, + withWrapper = false, + onClick = NOOP, + isDisabled = false, + isCollapsed = false, } ) => { const TagName = onClick === NOOP ? 'div' : Button const additionalProps = onClick === NOOP ? {} : { onClick } const classNames = classnames( 'stk-global-color-scheme__preview__background', { 'stk-scheme--is-disabled': isDisabled, + 'stk-scheme--is-collapsed': isCollapsed, } ) return ( diff --git a/src/components/color-schemes-help/index.js b/src/components/color-schemes-help/index.js index caeef807a1..d390c8222b 100644 --- a/src/components/color-schemes-help/index.js +++ b/src/components/color-schemes-help/index.js @@ -4,8 +4,15 @@ import { Link } from '~stackable/components' import { __ } from '@wordpress/i18n' import { dispatch } from '@wordpress/data' -export const ColorSchemesHelp = () => { +export const ColorSchemesHelp = props => { + const { + customText, callback, renderComponent, + } = props const onClick = () => { + if ( callback ) { + callback() + } + // Open the global settings sidebar. dispatch( 'core/edit-post' )?.openGeneralSidebar( 'stackable-global-settings/sidebar' ) // For Block Editor dispatch( 'core/edit-site' )?.openGeneralSidebar( 'stackable-global-settings/sidebar' ) // For Site Editor @@ -30,8 +37,13 @@ export const ColorSchemesHelp = () => { }, 10 ) } + if ( renderComponent ) { + return renderComponent( onClick ) + } + return <> - { __( 'Change the color scheme.', i18n ) } + { customText ? customText + : { __( 'Change the color scheme.', i18n ) } }   { __( 'Manage your color schemes.', i18n ) } diff --git a/src/components/design-library-list/default.json b/src/components/design-library-list/default.json new file mode 100644 index 0000000000..14d092695c --- /dev/null +++ b/src/components/design-library-list/default.json @@ -0,0 +1,30 @@ +{ + "FAQ": { + "heading_placeholder": "Frequently Asked Questions (FAQs)", + "description_placeholder": "Find answers to the most common questions about [Your Product/Service]. If you can't find what you're looking for here, please contact us.", + + "question-1_placeholder": "What features does your service offer?", + "answer-1_placeholder": "Our service offers a comprehensive set of features designed to help you manage your tasks. Key features include Feature A, Feature B, and Feature C. You can find a full list and details on our Features Page.", + + "question-2_placeholder": "Is our Service integrated with other products?", + "answer-2_placeholder": "Yes, our Service offers integrations with various popular products and services to streamline your workflow. We currently integrate with Product A, Product B, and Product C. You can find more information about our current integrations and how to set them up on our [Integrations Page Link]. We are continuously working on adding more integrations.", + + "question-3_placeholder": "What are the pricing options involved?", + "answer-3_placeholder": "We offer several pricing options to suit different needs. You can view a detailed breakdown of our plans and features on our Pricing Page. We have a free tier, monthly subscriptions, and annual plans.", + + "question-4_placeholder": "What payment methods do you accept?", + "answer-4_placeholder": "We accept various convenient payment methods, including major credit cards (Visa, MasterCard, American Express), and in some cases, bank transfers. You can select your preferred payment method during the checkout or subscription process.", + + "question-5_placeholder": "What is your cancellation/refund policy?", + "answer-5_placeholder": "Our cancellation policy allows you to cancel your subscription at any time through your account settings. Regarding refunds, our policy is we offer a 30-day money-back guarantee. Please refer to our Terms of Service Page for complete details.", + + "question-6_placeholder": "What are your hours of operation?", + "answer-6_placeholder": "Our standard hours of operation are Monday to Friday, 9:00 AM to 5:00 PM in Pacific Standard Time (PST).", + + "question-7_placeholder": "How can I contact customer support?", + "answer-7_placeholder": "You can contact our customer support team through several channels: Submit a request via our Support Contact Form. Email us directly at support@email.com. Use our live chat feature available on our website during business hours. Our support hours are Monday-Friday, 9 AM - 5 PM Pacific Standard Time (PST).", + + "question-8_placeholder": "How can I provide feedback?", + "answer-8_placeholder": "We highly value your feedback as it helps us improve. You can provide feedback through several methods: Use the feedback form on our website, or email your suggestions or comments to feedback@email.com. You can also participate in surveys. We review all feedback submitted." + } +} \ No newline at end of file diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index e079b9ce86..7a351bcb83 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -2,71 +2,255 @@ * Internal dependencies. */ import ProControl from '../pro-control' +import DEFAULT from './default.json' +import { + addBackgroundScheme, addContainerScheme, cleanParse, parseDisabledBlocks, +} from './util' /** * External dependencies. */ -import { getDesign } from '~stackable/design-library' -import { isPro, i18n } from 'stackable' +import { createRoot } from '~stackable/util' +import { + isPro, i18n, wpGlobalStylesInlineCss, +} from 'stackable' import classnames from 'classnames' +import { Tooltip } from '~stackable/components' /** * WordPress dependencies. */ -import { useState } from '@wordpress/element' -import { Spinner } from '@wordpress/components' +import { + forwardRef, useEffect, useRef, useState, +} from '@wordpress/element' +import { useSelect } from '@wordpress/data' +import { Dashicon, Spinner } from '@wordpress/components' import { __ } from '@wordpress/i18n' +import { serialize } from '@wordpress/blocks' +import { applyFilters } from '@wordpress/hooks' +import { cloneDeep } from 'lodash' -const DesignLibraryListItem = props => { +const DEFAULT_CONTENT = { ...DEFAULT } + +const DesignLibraryListItem = forwardRef( ( props, ref ) => { const { designId, - image, label, onClick, + template = '', + category = '', plan, - isPro, - apiVersion, - isMultiSelectMode = false, selectedNum = false, + isBusy, + containerScheme, + backgroundScheme, + enableBackground, + forceUpdate, + cardHeight, + setCardHeight, + previewSize, + setPreviewSize, } = props - const [ isBusy, setIsBusy ] = useState( false ) - // const [ isFavorite, setIsFavorite ] = useState( props.isFavorite ) + const [ parsedBlocks, setParsedBlocks ] = useState( null ) + const [ content, setContent ] = useState( '' ) + + const hostRef = useRef( null ) + const previewRef = useRef( null ) + const prevEnableBackgroundRef = useRef( null ) + const blocksForSubstitutionRef = useRef( false ) + const hasBackgroundTargetRef = useRef( false ) + + const { getEditorDom } = useSelect( 'stackable/editor-dom' ) + const editorDom = getEditorDom() const mainClasses = classnames( [ 'ugb-design-library-item', + 'ugb-design-library-item--toggle', ], { 'ugb--is-busy': isBusy, [ `ugb--is-${ plan }` ]: ! isPro && plan !== 'free', - 'ugb-design-library-item--toggle': isMultiSelectMode, - 'ugb--is-toggled': isMultiSelectMode && selectedNum, + 'ugb--is-toggled': selectedNum, } ) + const STYLE_IDS = applyFilters( 'stackable.global-styles.ids', [ + 'ugb-dep-native-global-style-css-nodep-inline-css', + 'ugb-style-css-css', + 'ugb-style-css-responsive-css', + 'ugb-block-style-inheritance-nodep-inline-css', + 'ugb-style-css-premium-css', + ] ) + + const adjustScale = () => { + if ( ref.current && hostRef.current?.shadowRoot && ! selectedNum ) { + const newPreviewSize = { ...previewSize } + const newCardHeight = { ...cardHeight } + const cardRect = ref.current.getBoundingClientRect() + + const cardWidth = cardRect.width // Get width of the card + const scaleFactor = cardWidth > 0 ? cardWidth / 1200 : 1 // Divide by 1200, which is the width of preview in the shadow DOM + newPreviewSize.scale = scaleFactor + + const shadowBody = hostRef.current.shadowRoot.querySelector( 'body' ) + if ( shadowBody ) { + const _height = parseFloat( shadowBody.offsetHeight ) * scaleFactor // Also adjust the height + + const heightKey = enableBackground ? 'heightBackground' : 'heightNoBackground' + newPreviewSize[ heightKey ] = _height + setPreviewSize( newPreviewSize ) + } + + const CardHeightKey = enableBackground ? 'background' : 'noBackground' + newCardHeight[ CardHeightKey ] = cardRect.height + setTimeout( () => setCardHeight( newCardHeight ), 500 ) + } + } + + const renderPreview = () => { + let blocks = cloneDeep( content ) + + // No need to add the color scheme attribute if the selected scheme is the default + if ( containerScheme !== '' ) { + blocks = addContainerScheme( blocks, containerScheme ) + } + + // Only add a background scheme if it is enabled + if ( enableBackground ) { + blocks = addBackgroundScheme( blocks, enableBackground, backgroundScheme ) + } + + setParsedBlocks( blocks ) + + const preview = serialize( blocks ) + const cleanedBlock = preview.replace( //g, '' ) // removes comment + + previewRef.current.render( ) + } + + // Replace the placeholders with the default content + useEffect( () => { + const defaultValues = DEFAULT_CONTENT[ category ] + let _content = template + if ( defaultValues ) { + Object.keys( defaultValues ).forEach( key => { + _content = _content.replace( key, defaultValues[ key ] ) + } ) + } + + if ( _content.includes( 'stk-design-library__bg-target="true"' ) ) { + hasBackgroundTargetRef.current = true + } + + // We need to parse the content because this is what we use to insert the blocks in the Editor + const _block = cleanParse( _content )[ 0 ] + const { block, blocksForSubstitution } = parseDisabledBlocks( _block ) + blocksForSubstitutionRef.current = blocksForSubstitution + + setContent( block ) + }, [ template ] ) + + useEffect( () => { + if ( ! content || + ! hostRef.current || + selectedNum // Do not re-render if the design has been selected + ) { + return + } + + const shadowRoot = hostRef.current.shadowRoot || hostRef.current.attachShadow( { mode: 'open' } ) + + // Initialize the shadow DOM for the first time + if ( ! previewRef.current ) { + // Get all styles needed and make a copy for the shadow DOM + const styleNodes = STYLE_IDS.map( id => { + let style = null + let node = document.getElementById( id ) + if ( ! node && editorDom ) { + node = editorDom.querySelector( `#${ id }` ) + } + + if ( node ) { + style = node.cloneNode( true ) + } + + return style + } ).filter( node => node !== null ) + + // Add global and theme styles + const globalStylesNode = document.createElement( 'style' ) + globalStylesNode.setAttribute( 'id', 'global-styles-inline-css' ) + globalStylesNode.innerHTML = wpGlobalStylesInlineCss + styleNodes.push( globalStylesNode ) + + const hostStyles = document.createElement( 'style' ) + hostStyles.setAttribute( 'id', 'stk-design-library-styles' ) + hostStyles.innerHTML = ! hasBackgroundTargetRef.current + ? `body > .stk-block-columns { padding: 75px; } body > .stk-block-background:not(.stk--no-padding) { padding: calc(75px + var(--stk-block-background-padding)); }` + : `[stk-design-library__bg-target="true"] { padding: 25px; } [stk-design-library__bg-target="true"].stk-block-background:not(.stk--no-padding) { padding: calc(25px + var(--stk-block-background-padding)); }` + styleNodes.push( hostStyles ) + + styleNodes.forEach( node => { + if ( node.textContent ) { + // we use :host in the shadow DOM to target the root + node.textContent = node.textContent.replace( /:root/g, ':host' ) + } + shadowRoot.appendChild( node ) + } ) + + previewRef.current = createRoot( shadowRoot ) + } + + renderPreview() + + prevEnableBackgroundRef.current = enableBackground + }, [ content, containerScheme, backgroundScheme, enableBackground ] ) + + useEffect( () => { + setTimeout( adjustScale, 50 ) + }, [ forceUpdate ] ) + + useEffect( () => { + if ( selectedNum === 0 && content ) { + renderPreview() + setTimeout( adjustScale, 50 ) + } + }, [ selectedNum ] ) + + const getPreviewHeight = () => { + return selectedNum ? previewSize.heightSelected : ( enableBackground ? previewSize.heightBackground : previewSize.heightNoBackground ) + } + return ( -
    { + if ( ! isPro && plan !== 'free' ) { + return + } + const cardRect = ref.current.getBoundingClientRect() + + const newPreviewSize = { ...previewSize } + const newCardHeight = { ...cardHeight } + newPreviewSize.heightSelected = enableBackground ? previewSize.heightBackground : previewSize.heightNoBackground + newCardHeight.selected = cardRect.height + + setPreviewSize( newPreviewSize ) + setCardHeight( newCardHeight ) + + onClick( designId, parsedBlocks, blocksForSubstitutionRef.current ) + } } > { isBusy && } { ! isPro && plan !== 'free' && } - +
    + +
    +
    +
    -
    - { label } - { /* */ } +
    +
    +

    { label }

    + { blocksForSubstitutionRef.current !== false && blocksForSubstitutionRef.current.size !== 0 && + + + + } +
    +
    + { selectedNum !== 0 && + + + + } + { selectedNum === 0 ? '' : selectedNum } +
    -
    + ) -} +} ) DesignLibraryListItem.defaultProps = { designId: '', @@ -91,10 +301,26 @@ DesignLibraryListItem.defaultProps = { label: '', onClick: () => {}, plan: 'free', - isPro, premiumLabel: __( 'Go Premium', i18n ), - apiVersion: '', - // isFavorite: false, } export default DesignLibraryListItem + +const DesignPreview = ( { + blocks, adjustScale, prevEnableBackground, enableBackground, +} ) => { + useEffect( () => { + if ( prevEnableBackground !== enableBackground ) { + // Adjust scale if the background was toggled + setTimeout( adjustScale, 50 ) + } + }, [ blocks ] ) + + return ( + + ) +} diff --git a/src/components/design-library-list/editor.scss b/src/components/design-library-list/editor.scss index 28c05411dc..c6d4e6430d 100644 --- a/src/components/design-library-list/editor.scss +++ b/src/components/design-library-list/editor.scss @@ -3,18 +3,9 @@ break-inside: avoid; gap: 40px; display: grid; - grid-template-columns: 1fr; + grid-template-columns: 1fr 1fr 1fr; align-items: center; font-size: 13px; - &.ugb-design-library-items--columns-2 { - grid-template-columns: 1fr 1fr; - } - &.ugb-design-library-items--columns-3 { - grid-template-columns: 1fr 1fr 1fr; - } - &.ugb-design-library-items--columns-4 { - grid-template-columns: 1fr 1fr 1fr 1fr; - } .components-base-control__help { text-align: center; @@ -37,7 +28,12 @@ .ugb-design-library-item { display: grid; grid-template-columns: 1fr; - grid-template-rows: auto 40px; + // grid-template-rows: auto 40px; + padding: 0; + border: 0 ; + text-align: start; + cursor: pointer; + background: #fff; position: relative; transition: all 0.3s cubic-bezier(0.2, 0.6, 0.4, 1); overflow: hidden; @@ -46,20 +42,11 @@ &:hover { box-shadow: rgba(0, 0, 0, 0.25) 0px 25px 50px -12px; } - img { - display: block; - pointer-events: none; // Prevent dragging the image. - width: 100%; - height: auto; - min-height: 50px; - background: #fff; - } footer { - padding-inline: 20px; line-height: 18px; - display: flex; - align-items: center; - justify-content: space-between; + // display: flex; + // align-items: center; + // justify-content: space-between; } .ugb-design-library-item__spinner { position: absolute; @@ -100,37 +87,50 @@ &[class*="ugb--is-premium"]:hover { .ugb-design-control-pro-note { opacity: 1; + z-index: 2; + } + .stk-block-design__host { + opacity: 0; } } // Toggle mode - &.ugb-design-library-item--toggle { + &.ugb-design-library-item--toggle footer { position: relative; - &::after { - content: ""; - position: absolute; - top: 16px; - left: 16px; + padding: 16px 16px 24px; + display: flex; + justify-content: space-between; + > div { + display: flex; + gap: 8px; + align-items: center; + } + h4 { + margin: 0; + } + .stk-block-design__selected-num { + display: inline-block; height: 24px; width: 24px; background: #fff; border: 1px solid #777; + border-radius: 24px; + } + .dashicon { + color: #bbb; } + } - &.ugb--is-toggled { - &::after { - content: attr(data-selected-num); - height: 24px; - width: 24px; - background: #f00069; - color: #fff; - font-size: 14px; - display: flex; - align-items: center; - justify-content: center; - border: none; - font-weight: bold; - } + &.ugb--is-toggled footer { + .stk-block-design__selected-num { + background: #f00069; + color: #fff; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + border: none; + font-weight: bold; } } @@ -141,16 +141,12 @@ z-index: 9; } } -.ugb-design-library-item__image { - padding: 0 !important; - border: 0 !important; - text-align: start !important; - width: 100% !important; - margin: 0 !important; - outline: none !important; - cursor: pointer; - display: block; -} +// .ugb-design-library-item__image { +// width: 100% !important; +// margin: 0 !important; +// outline: none !important; +// display: block; +// } .ugb-design-library-item__favorite { border: 0; @@ -184,3 +180,20 @@ // } // } } +// new design library styles +.stk-block-design__host { + min-width: 1200px; + position: absolute; + top: 0; + left: 0; + opacity: 1; + transition: all 0.3s cubic-bezier(0.2, 0.6, 0.4, 1); +} + +.stk-block-design__host-container { + position: relative; +} + +:host body { + min-height: 500px; +} \ No newline at end of file diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js index a52b3d4fc6..dda1a2d59c 100644 --- a/src/components/design-library-list/index.js +++ b/src/components/design-library-list/index.js @@ -14,67 +14,118 @@ import classnames from 'classnames' */ import { Spinner } from '@wordpress/components' import { __ } from '@wordpress/i18n' +import { + useState, useEffect, useRef, +} from '@wordpress/element' const DesignLibraryList = props => { const { className = '', designs, isBusy, - onSelect, onSelectMulti, - apiVersion, - isMultiSelectMode = false, selectedDesigns = [], } = props + const containerRef = useRef( null ) + + const [ scrollTop, setScrollTop ] = useState( 0 ) + const [ forceUpdate, setForceUpdate ] = useState( 0 ) const listClasses = classnames( [ 'ugb-design-library-items', className, - ], { - [ `ugb-design-library-items--columns-${ props.columns }` ]: ! isBusy && props.columns, - } ) - - return
    - { ( designs || [] ).map( ( design, i ) => { - const selectedNum = isMultiSelectMode ? selectedDesigns.indexOf( design.id ) + 1 : false - return ( - { - if ( ! isMultiSelectMode ) { - onSelect( designData, design, callback ) - } else if ( onSelectMulti ) { - onSelectMulti( designData, callback ) - } - } } - /> - ) - } ) } - - { isBusy &&
    } - - { ! isBusy && ! ( designs || [] ).length && -

    { __( 'No designs found', i18n ) }

    - } + ] ) + + useEffect( () => { + setForceUpdate( v => v + 1 ) + containerRef.current.scrollTop = 0 + }, [ designs ] ) + + return
    { + setScrollTop( e.currentTarget.scrollTop ) + } } + > +
    + { ( designs || [] ).map( ( design, i ) => { + const selectedNum = selectedDesigns.indexOf( design.designId ) + 1 + return ( + onSelectMulti( designId, parsedBlocks, hasDisabledBlocks ) } + scrollTop={ scrollTop } + /> + ) + } ) } + + { isBusy &&
    } + + { ! isBusy && ! ( designs || [] ).length && +

    { __( 'No designs found', i18n ) }

    + } +
    } DesignLibraryList.defaultProps = { designs: [], - columns: 1, + columns: 3, onSelect: () => {}, isBusy: false, - apiVersion: '', } export default DesignLibraryList + +const DesignLibraryItem = props => { + const { scrollTop, ...propsToPass } = props + const itemRef = useRef( null ) + const [ cardHeight, setCardHeight ] = useState( {} ) + const [ previewSize, setPreviewSize ] = useState( {} ) + const [ shouldRender, setShouldRender ] = useState( props.testKey < 9 ) + + useEffect( () => { + if ( ! itemRef.current ) { + return + } + + const containerRect = document.querySelector( '.ugb-modal-design-library__designs' ).getBoundingClientRect() + const itemRect = itemRef.current.getBoundingClientRect() + + const render = ( itemRect.top > containerRect.top - 250 && itemRect.top < containerRect.bottom + 250 ) || + ( itemRect.bottom > containerRect.top - 250 && itemRect.bottom < containerRect.bottom + 250 ) + + setShouldRender( render ) + }, [ scrollTop, props.enableBackground ] ) + + const getCardHeight = () => { + const key = props.enableBackground ? 'background' : 'noBackground' + return cardHeight?.[ key ] || 250 + } + + if ( ! shouldRender && ! props.selectedNum ) { + return
    + } + + return setPreviewSize( previewSize ) } + setCardHeight={ height => setCardHeight( height ) } + { ...propsToPass } + /> +} diff --git a/src/components/design-library-list/util.js b/src/components/design-library-list/util.js new file mode 100644 index 0000000000..93e018dc9c --- /dev/null +++ b/src/components/design-library-list/util.js @@ -0,0 +1,204 @@ +/* eslint-disable no-console */ +import DEFAULT from './default.json' +import { settings } from 'stackable' +import { parse, serialize } from '@wordpress/blocks' + +const DEFAULT_CONTENT = { ...DEFAULT } +const PARSER = new DOMParser() + +export const cleanParse = content => { + const originalConsoleError = console.error + const originalConsoleWarn = console.warn + + console.error = ( ...args ) => { + if ( args.length && typeof args[ 0 ] === 'string' && + args[ 0 ].includes( 'Block validation failed' ) + ) { + return + } + originalConsoleError( ...args ) + } + + console.warn = ( ...args ) => { + if ( args.length && typeof args[ 0 ] === 'string' && + args[ 0 ].includes( 'Block validation' ) + ) { + return + } + originalConsoleWarn( ...args ) + } + + let result + + try { + result = parse( content ) + } finally { + console.error = originalConsoleError + console.warn = originalConsoleWarn + } + + return result +} + +const replaceInnerContent = ( originalContent, newInnerHTML ) => { + const openTag = '', secondLastClose ) + + if ( openIndex === -1 || secondLastClose === -1 || closeEnd === -1 ) { + return originalContent + } + + const before = originalContent.slice( 0, openIndex ) + const after = originalContent.slice( closeEnd + 3 ) + + return `${ before }${ newInnerHTML }${ after }` +} + +export const addContainerScheme = ( + block, + containerScheme +) => { + const addScheme = blocks => { + return blocks.map( block => { + if ( block.innerBlocks?.length ) { + block.innerBlocks = addScheme( block.innerBlocks ) + } + + if ( block.attributes.hasContainer ) { + block.attributes.containerColorScheme = containerScheme + } + + if ( block.name === 'core/missing' && block.attributes.originalName ) { + const { originalAttributes } = block.attributes + + let innerHTML = '' + if ( block.innerBlocks.length ) { + innerHTML = serialize( block.innerBlocks ) + } else if ( originalAttributes.text ) { + innerHTML = originalAttributes.text + } + + const updatedContent = replaceInnerContent( block.attributes.originalContent, innerHTML ) + + block.attributes.originalContent = updatedContent + } + + return block + } ) + } + + const newBlock = addScheme( [ block ] )[ 0 ] + + return newBlock +} + +export const addBackgroundScheme = ( + block, + enableBackground, + backgroundScheme, +) => { + const addBackground = block => { + block.attributes.hasBackground = true + if ( backgroundScheme !== '' ) { + block.attributes.backgroundColorScheme = backgroundScheme + } + + return block + } + + const getTargetBlock = blocks => { + blocks.forEach( _block => { + const customAttributes = _block.attributes.customAttributes + + const isTarget = customAttributes?.find( attribute => attribute[ 0 ] === 'stk-design-library__bg-target' && attribute[ 1 ] === 'true' ) + if ( isTarget ) { + _block = addBackground( _block ) + } else if ( _block.innerBlocks.length ) { + getTargetBlock( _block.innerBlocks ) + } + } ) + } + + if ( ! enableBackground ) { + return block + } + + if ( block.attributes.hasBackground ) { + return block + } + + const customAttributes = block.attributes.customAttributes + + if ( ! customAttributes?.length ) { + block = addBackground( block ) + return block + } + + getTargetBlock( block.innerBlocks ) + + return block +} + +export const replacePlaceholders = ( block, category ) => { + const key = block.attributes.text + const defaultValues = DEFAULT_CONTENT[ category ] + if ( key && defaultValues ) { + const newValue = defaultValues[ key ] + block.attributes.text = newValue + } + + block.innerBlocks.forEach( innerBlock => { + replacePlaceholders( innerBlock, category ) + } ) + + return block +} + +const retrieveAttributes = ( blockName, hasNoInnerBlocks, content ) => { + const attrs = content.match( /{.*}/ ) + let parsedAttrs = {} + if ( attrs ) { + try { + parsedAttrs = JSON.parse( attrs[ 0 ] ) + if ( hasNoInnerBlocks ) { + content = content.replace( /]*>[\s\S]*?<\/style>/gi, '' ) + const doc = PARSER.parseFromString( content, 'text/html' ) + parsedAttrs.text = doc.body.textContent.trim() + } + } catch ( err ) {} + } + return parsedAttrs +} + +export const parseDisabledBlocks = parsedBlock => { + const disabledBlocks = settings.stackable_block_states || [] + const blocksForSubstitution = new Set() + + if ( Array.isArray( disabledBlocks ) && ! disabledBlocks.length ) { + return { block: parsedBlock, blocksForSubstitution } + } + + const addOriginalAttributes = blocks => { + return blocks.map( block => { + if ( block.name === 'core/missing' ) { + blocksForSubstitution.add( block.attributes.originalName ) + const newAttrs = retrieveAttributes( block.attributes.originalName, block.innerBlocks.length === 0, block.attributes.originalContent ) + const attrs = block.attributes + block.attributes = { + ...attrs, ...newAttrs, originalAttributes: newAttrs, + } + } + + block.innerBlocks = addOriginalAttributes( block.innerBlocks ) + return block + } ) + } + + const block = addOriginalAttributes( [ parsedBlock ] )[ 0 ] + return { block, blocksForSubstitution } +} diff --git a/src/components/modal-design-library/block-list.js b/src/components/modal-design-library/block-list.js index acd679dd30..8a05c92e04 100644 --- a/src/components/modal-design-library/block-list.js +++ b/src/components/modal-design-library/block-list.js @@ -10,215 +10,70 @@ * External dependencies */ import { sortBy } from 'lodash' -import { fetchDesignLibrary } from '~stackable/design-library' -import { isPro, i18n } from 'stackable' +import { i18n } from 'stackable' import classnames from 'classnames' /** * WordPress dependencies */ -import { - useEffect, useState, -} from '@wordpress/element' +import { useEffect, useState } from '@wordpress/element' import { __ } from '@wordpress/i18n' -import { useLocalStorage } from '~stackable/util' - -const isWireframe = design => design.uikit.toLowerCase() === 'wireframes' const BlockList = props => { - const [ uiKitList, setUiKitList ] = useState( [] ) const [ categoryList, setCategoryList ] = useState( [] ) - const [ wireframeList, setWireframeList ] = useState( [] ) - const [ selected, setSelected ] = useLocalStorage( 'stk__design_library__block-list__selected', '' ) - const { viewBy, apiVersion } = props + const { + viewBy, designs, plan, selected, onSelect, + } = props - // Create our block list. useEffect( () => { - fetchDesignLibrary( apiVersion ).then( designs => { - const designList = Object.keys( designs ).reduce( ( output, name ) => { - const design = designs[ name ] - const { categories, uikit } = design - - // Get all UI Kits. Don't include the Wireframe UI Kit. - if ( typeof output.uikits[ uikit ] === 'undefined' && ! isWireframe( design ) ) { - output.uikits[ uikit ] = { - id: uikit, - label: design.uikit, - plan: design.plan, - count: 0, - } - } - - // Get all wireframe categories. - if ( isWireframe( design ) ) { - categories.forEach( category => { - if ( typeof output.wireframes[ category ] === 'undefined' ) { - output.wireframes[ category ] = { - id: category, - label: category, - count: 0, - } - } - } ) - } else { - // Get all block design categories. - categories.forEach( category => { - if ( typeof output.categories[ category ] === 'undefined' ) { - output.categories[ category ] = { - id: category, - label: category, - count: 0, - } - } - } ) - } + let total = 0 + const _categories = designs.reduce( ( output, design ) => { + const category = design.categories[ 0 ] + if ( plan && plan !== design.plan ) { return output - }, { - uikits: {}, - categories: {}, - wireframes: {}, - } ) - - let uikitSort = [ 'label' ] - if ( ! isPro ) { - uikitSort = [ 'plan', 'label' ] - } - - const uikits = sortBy( Object.values( designList.uikits ), uikitSort ) - const categories = sortBy( Object.values( designList.categories ), 'label' ) - categories.unshift( { - id: 'all', - label: __( 'All', i18n ), - count: 0, - } ) - const wireframes = sortBy( Object.values( designList.wireframes ), 'label' ) - wireframes.unshift( { - id: 'all', - label: __( 'All', i18n ), - count: 0, - } ) - - setUiKitList( uikits ) - setCategoryList( categories ) - setWireframeList( wireframes ) - } ) - }, [ apiVersion ] ) - - // Update the counts of the designs, but don't update the list, only the counts. - useEffect( () => { - // If these are empty, then our component hasn't finished initializing. - if ( ! uiKitList.length || ! categoryList.length || ! wireframeList.length ) { - return - } - - const newUiKits = uiKitList.reduce( ( uiKits, uiKit ) => { - uiKits[ uiKit.id ] = { - ...uiKit, - count: 0, } - return uiKits - }, {} ) - const newCategories = categoryList.reduce( ( categories, category ) => { - categories[ category.id ] = { - ...category, - count: 0, - } - return categories - }, {} ) - const newWireframes = wireframeList.reduce( ( categories, category ) => { - categories[ category.id ] = { - ...category, - count: 0, - } - return categories - }, {} ) - props.designs.forEach( design => { - // Gather all wireframe designs. - if ( isWireframe( design ) ) { - design.categories.forEach( category => { - if ( category && newWireframes[ category ] ) { - newWireframes[ category ].count++ - } - } ) - return - } - - // Gather all ui kit designs. - if ( design.uikit && newUiKits[ design.uikit ] ) { - newUiKits[ design.uikit ].count++ - } - - // Gather all block design categories. - design.categories.forEach( category => { - if ( category && newCategories[ category ] ) { - newCategories[ category ].count++ + if ( category in output ) { + output[ category ].count++ + } else { + output[ category ] = { + id: category, + label: category, + count: 1, } - } ) - } ) - - let uikitSort = [ 'label' ] - if ( ! isPro ) { - uikitSort = [ 'plan', 'label' ] - } - setUiKitList( sortBy( Object.values( newUiKits ), uikitSort ) ) - - // Sort the categories so that the "All" is first. - if ( newCategories.all ) { - newCategories.all.count = props.designs.filter( design => ! isWireframe( design ) ).length - newCategories.all.label = ' ' // Spaces so we will be first when sorting. - } - const sorted = sortBy( Object.values( newCategories ), 'label' ) - if ( sorted[ 0 ] ) { - sorted[ 0 ].label = __( 'All', i18n ) - } - setCategoryList( sorted ) + } - // Sort the wireframes so that the "All" is first. - if ( newWireframes.all ) { - newWireframes.all.count = props.designs.filter( isWireframe ).length - newWireframes.all.label = ' ' // Spaces so we will be first when sorting. - } - const sortedWireframes = sortBy( Object.values( newWireframes ), 'label' ) - if ( sortedWireframes[ 0 ] ) { - sortedWireframes[ 0 ].label = __( 'All', i18n ) - } - setWireframeList( sortedWireframes ) - }, [ props.designs.length, JSON.stringify( uiKitList ), JSON.stringify( categoryList ), JSON.stringify( wireframeList ) ] ) + total++ + return output + }, {} ) - useEffect( () => { - // If these are empty, then our component hasn't finished initializing. - if ( ! uiKitList.length || ! categoryList.length || ! wireframeList.length ) { - return + _categories.all = { + id: '', + label: ' ', + count: total, } - setSelected( viewBy === 'uikit' ? uiKitList[ 0 ].id : 'all' ) - }, [ viewBy ] ) - - useEffect( () => { - props.onSelect( selected ) - }, [ selected ] ) + const sortedCategories = sortBy( Object.values( _categories ), 'label' ) + sortedCategories[ 0 ].label = __( 'All', i18n ) - const list = viewBy === 'uikit' - ? uiKitList : viewBy === 'category' - ? categoryList : wireframeList + setCategoryList( sortedCategories ) + }, [ designs, viewBy, plan ] ) return (
      - { list.reduce( ( list, itemData ) => { + { categoryList.reduce( ( list, itemData ) => { const { id, label, count, - plan, } = itemData const classes = classnames( [ 'stk-design-library__sidebar-item', ], { 'is-active': selected === id, - 'is-disabled': ! isPro && plan === 'premium', + // 'is-disabled': ! isPro && plan === 'premium', } ) list.push( @@ -226,7 +81,7 @@ const BlockList = props => {
      setSelected( id ) } + onClick={ () => onSelect( id ) } onKeyPress={ e => { if ( e.keyCode === 13 ) { this.click() diff --git a/src/components/modal-design-library/color-list.js b/src/components/modal-design-library/color-list.js deleted file mode 100644 index 5994bb0536..0000000000 --- a/src/components/modal-design-library/color-list.js +++ /dev/null @@ -1,87 +0,0 @@ -import { useState } from '@wordpress/element' -import { BaseControl, ColorPalette } from '@wordpress/components' - -const COLORS = { - red: { - color: '#f44336', - name: 'red', - }, - green: { - color: '#4caf50', - name: 'green', - }, - yellow: { - color: '#ffeb3b', - name: 'yellow', - }, - blue: { - color: '#2196f3', - name: 'blue', - }, - pink: { - color: '#e91e63', - name: 'pink', - }, - gray: { - color: '#9e9e9e', - name: 'gray', - }, - brown: { - color: '#795548', - name: 'brown', - }, - orange: { - color: '#ff9800', - name: 'orange', - }, - purple: { - color: '#9c27b0', - name: 'purple', - }, - black: { - color: '#212121', - name: 'black', - }, - white: { - color: '#fff', - name: 'white', - }, -} - -const ColorList = props => { - const [ selectedColors, setSelectedColors ] = useState( [] ) - - return ( - -
      - { Object.keys( props.colors ).map( id => { - const { color, name } = COLORS[ id ] - return { - let newColors - if ( ! color ) { - newColors = selectedColors.filter( n => n !== name ) - } else { - newColors = [ ...selectedColors, name ] - } - setSelectedColors( newColors ) - props.onSelect( newColors ) - } } - clearable={ false } - disableCustomColors={ true } - /> - } ) } -
      -
      - ) -} - -ColorList.defaultProps = { - onSelect: () => {}, - colors: COLORS, -} - -export default ColorList diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index 78642d9578..cc8c0bd4f9 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -2,6 +2,7 @@ width: 100%; max-width: 90%; height: 90%; + max-height: 90%; min-height: 500px; --wp-admin-theme-color: #f00069; @@ -26,20 +27,19 @@ margin: 0; height: 58px; border-bottom: 1px solid #ddd; - .stk-control-content { - padding-left: 14px; + + .components-modal__header-heading-container { + flex-grow: 0; + width: 300px; } - .components-button { - left: 0 !important; - &.has-icon { - margin: 10px; - } + > .components-button.has-icon { + margin: 10px; } } .ugb-modal-design-library__wrapper { display: grid; grid-template-columns: 300px auto; - grid-template-rows: 80px auto; + grid-template-rows: auto; flex: 1; height: 100%; } @@ -50,6 +50,7 @@ grid-row: 1 / 3; display: flex; flex-direction: column; + justify-content: space-between; overflow: hidden; z-index: 1; .ugb-control-separator { @@ -57,21 +58,30 @@ width: 100% !important; } } - .ugb-modal-design-library__topbar { + .ugb-modal-design-library__style-options { display: flex; - justify-content: flex-end; - padding: 0 24px; - height: 64px; - align-items: center; - grid-column: 2 / 3; - grid-row: 1 / 2; + flex-direction: column; + padding: 24px; + gap: 12px; background: #fff; - border-bottom: 1px solid #eee; + border-top: 1px solid #eee; + + .components-base-control__field { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0; + + .components-base-control__label { + margin-bottom: 0; + text-transform: none; + } + } } .ugb-modal-design-library__designs { padding: 24px; grid-column: 2 / 3; - grid-row: 2 / 3; + grid-row: 1 / 3; } .ugb-modal-design-library__search { @@ -123,7 +133,7 @@ margin-left: auto; } .ugb-modal-design-library__footer { - padding: 0 24px; + padding: 24px; grid-column: 2 / 3; grid-row: 3 / 4; display: flex; @@ -134,9 +144,6 @@ } // Make room for the footer buttons. &.ugb-modal-design-library--is-multiselect { - .ugb-modal-design-library__wrapper { - grid-template-rows: 80px auto 80px; - } .ugb-modal-design-library__sidebar { grid-row: 1 / 4; } @@ -160,6 +167,16 @@ margin: 0 0 0 18px !important; } } + + ::-webkit-scrollbar { + width: 10px; + } + ::-webkit-scrollbar-thumb { + background: #e0e0e0; + border-radius: 8px; + border: 2px solid rgba(0, 0, 0, 0); + background-clip: padding-box; + } } .ugb-modal-design-library__filters .ugb-block-list { @@ -246,6 +263,22 @@ margin-right: 10px; } +div.ugb-modal-design-library__enable-background { + margin-bottom: 0; + + .components-flex { + justify-content: space-between; + } + .components-toggle-control__label { + font-size: 11px; + font-weight: 500; + line-height: 1.4; + order: 1; + } + .components-form-toggle { + order: 2; + } +} .ugb-modal-design-library__dev-mode .components-base-control__field { margin: 2px 24px 0 !important; } @@ -271,22 +304,133 @@ // Design library top tabs, switch between block categories, ui kit and wireframe. .stk-design-library-tabs { margin-bottom: 0 !important; + margin: 0 auto; + + .components-base-control__field, .stk-control-label { + margin: 0; + } + .components-button-group { + background: #e0e0e08a; + border-radius: 6px !important; + box-shadow: none !important; + + } .components-button { - height: 60px; + height: fit-content; border: none !important; - border-left: 1px solid #ddd !important; - box-shadow: none !important; - padding: 0 24px; + box-shadow: none; + padding: 4px 24px; + margin: 6px; + border-radius: 4px !important; background: transparent; &:hover, + &.is-primary:hover, &.is-primary { color: #f00069 !important; - background: rgba(229, 67, 118, 0.1) !important; + background: #fff; + box-shadow: 1px 1px 3px 0px #1e1e1e1e; } } } +.ugb-modal-design-library .stk-design-library-tabs { + padding-right: 0 !important; +} + // Fix WP 6.3 a div wraps the .ugb-modal-design-library__wrapper .ugb-modal-design-library .components-modal__content > div:not(.components-modal__header) { height: 100%; } + + +.ugb-modal-design-library__stk-color-scheme { + display: flex; + align-items: center; + gap: 8px; + outline: 1px solid #e0e0e0; + border-radius: 2px; + margin-bottom: 8px; + padding: 0; + height: auto; + width: 100%; + &:hover { + &.stk-color-scheme__toggle .stk-color-scheme__none, + .stk-global-color-scheme__preview { + color: initial; + } + } + &:last-child { + margin-bottom: 0; + } + + &.stk-color-scheme__selected { + &, &:active { + outline: 2px solid var(--wp-admin-theme-color, #f00069); + color: var(--wp-admin-theme-color, #f00069); + } + .stk-global-color-scheme__preview { + color: initial; + } + } + + .stk-color-scheme-name { + text-wrap: nowrap; + margin-inline-end: 8px; + } + + &.stk-color-scheme__toggle .stk-color-scheme__none { + position: absolute; + right: 0; + left: 0; + top: 50%; + transform: translate(0, -50%); + font-size: 12px; + margin-inline-end: 0; + } +} + +.stk-design-library__header-settings { + position: absolute; + right: 60px; +} +.stk-design-library__plan-dropdown, .ugb-modal-design-library__stk-color-scheme-list { + --wp-admin-theme-color: #f00069; + --wp-admin-theme-color-darker-10: #{ darken(#f00069, 10%) }; + --wp-admin-theme-color-darker-20: #{ darken(#f00069, 20%) }; +} + +.ugb-modal-design-library__stk-color-scheme-list-wrapper { + max-height: 145px; +} +.ugb-modal-design-library__stk-color-scheme-list { + padding: 0 0 8px; + .stk-color-scheme__none { + padding: 8px; + min-height: 30px; + margin: 0 auto; + min-width: 200px; + } +} +.components-dropdown__content:has(.ugb-modal-design-library__stk-color-scheme-list) { + ::-webkit-scrollbar { + width: 10px; + } + ::-webkit-scrollbar-thumb { + background: #e0e0e0; + border-radius: 8px; + border: 2px solid rgba(0, 0, 0, 0); + background-clip: padding-box; + } +} + +.stk-design-library__plan-dropdown .ugb-button-component { + width: 100%; +} + +.ugb-design-library__confirm-dialog { + max-width: 800px; + ul { + list-style: circle; + margin-inline-start: 20px; + } +} \ No newline at end of file diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index d09346e5b4..96b832e3fa 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -1,21 +1,19 @@ /** * Internal deprendencies */ -import SVGViewSingle from './images/view-single.svg' -import SVGViewMany from './images/view-many.svg' -import SVGViewFew from './images/view-few.svg' +// import SVGViewSingle from './images/view-single.svg' +// import SVGViewMany from './images/view-many.svg' +// import SVGViewFew from './images/view-few.svg' import BlockList from './block-list' import Button from '../button' import AdvancedToolbarControl from '../advanced-toolbar-control' import DesignLibraryList from '~stackable/components/design-library-list' -import { - getDesign, getDesigns, setDevModeDesignLibrary, -} from '~stackable/design-library' +import { getDesigns, filterDesigns } from '~stackable/design-library' /** * External deprendencies */ -import { i18n, devMode } from 'stackable' +import { i18n } from 'stackable' import classnames from 'classnames' import { useLocalStorage } from '~stackable/util' @@ -23,261 +21,376 @@ import { useLocalStorage } from '~stackable/util' * WordPress deprendencies */ import { - Modal, Spinner, TextControl, ToggleControl, + BaseControl, + Dashicon, + Dropdown, + Modal, + Spinner, + ToggleControl, } from '@wordpress/components' -import { useEffect, useState } from '@wordpress/element' +import { + useEffect, useState, useRef, +} from '@wordpress/element' import { sprintf, __ } from '@wordpress/i18n' +import { useBlockColorSchemes } from '~stackable/hooks' +import ColorSchemePreview from '../color-scheme-preview' +import { ColorSchemesHelp } from '../color-schemes-help' + +const PLAN_OPTIONS = [ { key: '', label: __( 'All', i18n ) }, { key: 'free', label: __( 'Free', i18n ) }, { key: 'premium', label: __( 'Premium', i18n ) } ] +const popoverProps = { + placement: 'right-start', + shift: true, +} export const ModalDesignLibrary = props => { - const [ search, setSearch ] = useState( props.search ) - const [ columns, setColumns ] = useState( 3 ) + const { + backgroundModeColorScheme, containerModeColorScheme, colorSchemesCollection, + } = useBlockColorSchemes() const [ isBusy, setIsBusy ] = useState( true ) const [ doReset, setDoReset ] = useState( false ) - const [ isMultiSelectMode, setIsMultiSelectMode ] = useState( false ) + const [ selectedDesignIds, setSelectedDesignIds ] = useState( [] ) const [ selectedDesignData, setSelectedDesignData ] = useState( [] ) + const [ isMultiSelectBusy, setIsMultiSelectBusy ] = useState( false ) - const [ selectedId, setSelectedId ] = useState( '' ) - const [ selectedType, setSelectedType ] = useLocalStorage( 'stk__design_library__block-list__view_by', 'uikit' ) - const [ isDevMode, setIsDevMode ] = useLocalStorage( 'stk__design_library_dev_mode', false ) + + const [ selectedTab, setSelectedTab ] = useLocalStorage( 'stk__design_library__block-list__view_by', 'patterns' ) + const [ selectedCategory, setSelectedCategory ] = useLocalStorage( 'stk__design_library__block-list__selected', '' ) + const [ selectedPlan, setSelectedPlan ] = useLocalStorage( 'stk__design_library__view-plan', PLAN_OPTIONS[ 0 ] ) + // The sidebar designs are used to update the list of blocks in the sidebar. const [ sidebarDesigns, setSidebarDesigns ] = useState( [] ) // The display designs are used to list the available designs the user can choose. const [ displayDesigns, setDisplayDesigns ] = useState( [] ) - const [ searchDebounced, setSearchDebounced ] = useState( search ) - const [ debounceTimeout, setDebounceTimeout ] = useState( null ) + const [ enableBackground, setEnableBackground ] = useState( false ) + const [ selectedContainerScheme, setSelectedContainerScheme ] = useState( '' ) + const [ selectedBackgroundScheme, setSelectedBackgroundScheme ] = useState( '' ) - useEffect( () => { - if ( debounceTimeout ) { - clearTimeout( debounceTimeout ) - setDebounceTimeout( null ) - } - setDebounceTimeout( setTimeout( () => { - setSearchDebounced( search ) - }, 500 ) ) - }, [ search ] ) - - // Select the input field on open. - // Use this method since useRef isn't working. - useEffect( () => { - const input = document.querySelector( '.ugb-modal-design-library__search input' ) - if ( input ) { - input.focus() - } - }, [] ) + const openDropdownRef = useRef( null ) // Update the designs on the sidebar. (this will trigger the display designs update next) useEffect( () => { if ( doReset ) { setSidebarDesigns( [] ) - setDisplayDesigns( [] ) + // setDisplayDesigns( [] ) } getDesigns( { - search: searchDebounced, reset: doReset, - apiVersion: props.apiVersion, + tab: selectedTab, } ).then( designs => { setSidebarDesigns( designs ) } ).finally( () => { setDoReset( false ) } ) - }, [ searchDebounced, doReset, props.apiVersion ] ) + }, [ doReset, selectedTab ] ) // This updates the displayed designs the user can pick. useEffect( () => { setIsBusy( true ) - getDesigns( { - apiVersion: props.apiVersion, - search: searchDebounced, - uikit: selectedType === 'wireframe' ? 'Wireframes' : ( selectedType === 'uikit' ? selectedId : '' ), - categories: [ 'category', 'wireframe' ].includes( selectedType ) && selectedId !== 'all' ? [ selectedId ] : [], + filterDesigns( { + library: sidebarDesigns, + category: selectedCategory, + plan: selectedPlan.key, } ).then( designs => { setDisplayDesigns( designs ) } ).finally( () => { setIsBusy( false ) } ) - }, [ selectedId, selectedType, doReset, searchDebounced, props.apiVersion ] ) + }, [ sidebarDesigns, selectedPlan, selectedCategory ] ) return ( - { __( 'Stackable Design Library', i18n ) } - { props.hasVersionSwitcher && ( - + + ) } + renderContent={ ( { onClose } ) => ( +
      + { PLAN_OPTIONS.map( ( plan, i ) => { + return + } ) } +
      + ) } /> - ) } +
      ) } - className={ classnames( 'ugb-modal-design-library', { 'ugb-modal-design-library--is-multiselect': isMultiSelectMode } ) } + className={ classnames( 'ugb-modal-design-library', 'ugb-modal-design-library--is-multiselect' ) } onRequestClose={ props.onClose } >
      - - + + ) } + renderContent={ ( { onClose } ) => ( +
      +
      + + { Object.entries( colorSchemesCollection ).map( ( [ key, scheme ], i ) => { + return + } ) } + { Object.keys( colorSchemesCollection ).length + ? ( + + ) } /> + : + } +
      +
      + ) } + /> + + design.id === designId ) ) - setSelectedDesignData( newSelectedDesignData ) + className="ugb-modal-design-library__color-scheme-label"> + ( +
      + openDropdownRef.current = onClose + onToggle() + } } + className="ugb-modal-design-library__stk-color-scheme stk-color-scheme__toggle" + > + { selectedContainerScheme !== '' + ? + : + } + + ) } + renderContent={ ( { onClose } ) => ( +
      +
      + + { Object.entries( colorSchemesCollection ).map( ( [ key, scheme ], i ) => { + return + } ) } + { Object.keys( colorSchemesCollection ).length + ? ( + + ) } /> + : + } +
      +
      + ) } + /> + + +
    + - { isMultiSelectMode &&
    ) @@ -315,3 +420,10 @@ ModalDesignLibrary.defaultProps = { apiVersion: '', onChangeApiVersion: () => {}, } + +const ColorSchemeTextItem = props => { + return
    +
    + { props.label } +
    +} diff --git a/src/design-library/index.js b/src/design-library/index.js index 37e1d6f371..73fbf3164f 100644 --- a/src/design-library/index.js +++ b/src/design-library/index.js @@ -1,7 +1,8 @@ import apiFetch from '@wordpress/api-fetch' import { doAction, applyFilters } from '@wordpress/hooks' +// import TEST_DESIGN from './test.json' -const LATEST_API_VERSION = 'v3' +const LATEST_API_VERSION = 'v4' let designLibrary = null let designs = [] @@ -26,6 +27,7 @@ export const fetchDesignLibrary = async ( forceReset = false, version = '' ) => return designLibrary[ version || LATEST_API_VERSION ] } +// TODO: to remove export const fetchDesign = async ( designId, version = '' ) => { if ( ! designs[ designId ] ) { const results = await apiFetch( { @@ -37,6 +39,7 @@ export const fetchDesign = async ( designId, version = '' ) => { return designs[ designId ] } +// TODO: to remove export const setDevModeDesignLibrary = async ( devMode = false ) => { const results = await apiFetch( { path: `/stackable/v2/design_library_dev_mode/`, @@ -49,64 +52,29 @@ export const setDevModeDesignLibrary = async ( devMode = false ) => { } export const getDesigns = async ( { - type: isType = '', - block: isBlock = '', - mood: isMood = '', - plan: isPlan = '', - colors: hasColors = [], - categories: hasCategories = [], - uikit: isUiKit = '', - search = '', reset = false, - apiVersion = '', } ) => { - let library = Object.values( await fetchDesignLibrary( reset, apiVersion ) ) - - if ( isType ) { - library = library.filter( ( { type } ) => type === isType ) - } - - if ( isBlock ) { - const blockName = isBlock.replace( /^\w+\//, '' ) - library = library.filter( ( { block } ) => block.endsWith( `/${ blockName }` ) ) - } - - if ( isMood ) { - library = library.filter( ( { mood } ) => mood === isMood ) - } + let library = Object.values( await fetchDesignLibrary( reset ) ) + library = library.map( ( item, index ) => { + return { + ...item, + testId: `stk-design-${ index + 1 }`, + } + } ) + return library +} +export const filterDesigns = async ( { + library = [], + plan: isPlan = '', + category: isCategory = '', +} ) => { if ( isPlan ) { library = library.filter( ( { plan } ) => plan === isPlan ) } - if ( hasColors && hasColors.length ) { - library = library.filter( ( { colors } ) => colors.some( color => hasColors.includes( color ) ) ) - } - - if ( hasCategories && hasCategories.length ) { - library = library.filter( ( { categories } ) => categories.some( category => hasCategories.includes( category ) ) ) - } - - if ( isUiKit ) { - library = library.filter( ( { uikit } ) => uikit === isUiKit ) - } - - if ( search ) { - const terms = search.toLowerCase().replace( /\s+/, ' ' ).trim().split( ' ' ) - - // Every search term should match a property of a design. - terms.forEach( searchTerm => { - library = library.filter( design => { - // Our search term needs to match at least one of these properties. - const propertiesToSearch = applyFilters( 'stackable.design-library.search-properties', - [ 'label', 'plan', 'tags', 'categories', 'colors' ], apiVersion ) - - return propertiesToSearch.some( designProp => { - // Search whether the term matched. - return design[ designProp ].toString().toLowerCase().indexOf( searchTerm ) !== -1 - } ) - } ) - } ) + if ( isCategory ) { + library = library.filter( ( { categories } ) => categories[ 0 ] === isCategory ) } return library diff --git a/src/design-library/init.php b/src/design-library/init.php index 9bb305e90d..6990806e77 100644 --- a/src/design-library/init.php +++ b/src/design-library/init.php @@ -11,6 +11,16 @@ exit; } +if ( ! function_exists( 'media_handle_sideload' ) ) { + require_once ABSPATH . 'wp-admin/includes/media.php'; +} +if ( ! function_exists( 'download_url' ) ) { + require_once ABSPATH . 'wp-admin/includes/file.php'; +} +if ( ! function_exists( 'wp_read_image_metadata' ) ) { + require_once ABSPATH . 'wp-admin/includes/image.php'; +} + if ( ! class_exists( 'Stackable_Design_Library' ) ) { /** * Class Stackable Design Library @@ -21,7 +31,7 @@ class Stackable_Design_Library { * The current version of the API we're using. * @var String */ - const API_VERSION = 'v3'; + const API_VERSION = 'v4'; /** * Constructor @@ -29,6 +39,11 @@ class Stackable_Design_Library { public function __construct() { add_action( 'rest_api_init', array( $this, 'register_route' ) ); add_filter( 'stackable_design_library_retreive_body', array( $this, 'replace_dev_mode_urls' ) ); + + add_filter( 'stackable_design_library_get_premium_designs', array( $this, 'get_designs_with_disabled_blocks' ) ); + add_filter( 'stackable_design_library_get_premium_designs', array( $this, 'get_premium_designs' ) ); + add_action( 'init', array( $this, 'register_design_pattern' ) ); + add_action( 'stackable_delete_design_library_cache', array( $this, 'delete_cache_v3' ) ); } public static function validate_string( $value, $request, $param ) { @@ -45,6 +60,13 @@ public static function validate_boolean( $value, $request, $param ) { return true; } + public static function validate_url( $value, $request, $param ) { + if ( ! filter_var( $value, FILTER_VALIDATE_URL ) || ! wp_http_validate_url( $value ) ) { + return new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be a valid URL.', STACKABLE_I18N ), $param ) ); + } + return true; + } + /** * Register Rest API routes for the design library. */ @@ -61,41 +83,26 @@ public function register_route() { ), ), ) ); - - register_rest_route( 'stackable/v2', '/design/(?P[\w\d-]*)/(?P[\w\d-]+)', array( - 'methods' => 'GET', - 'callback' => array( $this, 'get_design' ), - 'permission_callback' => function () { - return current_user_can( 'edit_posts' ); - }, - 'args' => array( - 'version' => array( - 'validate_callback' => __CLASS__ . '::validate_string' - ), - 'design' => array( - 'validate_callback' => __CLASS__ . '::validate_string' - ), - ), - ) ); - - register_rest_route( 'stackable/v2', '/design_library_dev_mode', array( + register_rest_route( 'stackable/v3', '/design_library_image', array( 'methods' => 'POST', - 'callback' => array( $this, 'set_dev_mode' ), + 'callback' => array( $this, 'get_design_library_image' ), 'permission_callback' => function () { return current_user_can( 'edit_posts' ); }, 'args' => array( - 'devmode' => array( - 'validate_callback' => __CLASS__ . '::validate_boolean' + 'image_url' => array( + 'required' => true, + 'sanitize_callback' => 'esc_url_raw', + 'validate_callback' => __CLASS__ . '::validate_url' ), ), ) ); } /** - * Deletes all caches. + * Deletes all design library v3 caches. */ - public function delete_cache() { + public function delete_cache_v3() { // Delete design library. delete_transient( 'stackable_get_design_library' ); @@ -110,19 +117,144 @@ public function delete_cache() { delete_transient( $transient ); } } + } + + public function delete_cache() { + $designs = $this->get_design_library_from_cloud(); + + $library = $designs[ self::API_VERSION ]; + foreach ( $library as $design_id => $design ) { + if ( WP_Block_Patterns_Registry::get_instance()->is_registered( 'stackable/' . $design_id ) ) { + $res = unregister_block_pattern( 'stackable/' . $design_id ); + } + + if ( WP_Block_Pattern_Categories_Registry::get_instance()->is_registered( 'stackable/' . $design[ 'category' ] ) ) { + $res = unregister_block_pattern_category( 'stackable/' . $design[ 'category' ] ); + } + } + // Delete design library. + delete_transient( 'stackable_get_design_library_v4' ); + delete_transient( 'stackable_get_design_library_json_v4' ); + + $this->register_design_pattern(); do_action( 'stackable_delete_design_library_cache' ); } - public function _get_design_library() { - $designs = get_transient( 'stackable_get_design_library_v3' ); + public function get_design_library_image( $request ) { + $url = $request->get_param( 'image_url' ); + + $temp_filepath = download_url( $url ); + + if ( is_wp_error( $temp_filepath ) ) { + @unlink( $temp_filepath ); + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'Failed to retrieve image from the provided URL.' + ), 500 ); + } + + if ( ! file_exists( $temp_filepath ) || ! wp_filesize( $temp_filepath ) ) { + @unlink( $temp_filepath ); + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'Invalid file content retrieved from the provided URL.' + ), 400 ); + } + + $image_hash = hash_file( 'md5', $temp_filepath ); + + $args = array( + 'post_type' => 'attachment', + 'post_status' => 'inherit', + 'posts_per_page' => 1, + 'meta_query' => array( + array( + 'key' => 'stk_image_hash', + 'value' => $image_hash, + 'compare' => '=' + ) + ) + ); + + $attachments = new WP_Query( $args ); + + if ( $attachments->have_posts() ) { + $attachments->the_post(); + $media_id = get_the_ID(); + $media_url = wp_get_attachment_url( $media_id ); + + wp_reset_postdata(); + @unlink( $temp_filepath ); + + return new WP_REST_Response( array( + 'success' => true, + 'new_url' => $media_url, + 'old_url' => $url + ), 200 ); + } + + $valid_mimes = [ 'image/jpeg' => 1, 'image/jpg' => 1, 'image/png' => 1, 'image/gif' => 1, 'image/webp' => 1, 'video/mp4' => 1 ]; + + $file_array = array( + 'name' => sanitize_file_name( wp_basename( parse_url( $url, PHP_URL_PATH ) ) ), + 'type' => mime_content_type( $temp_filepath ), + 'tmp_name' => $temp_filepath, + 'size' => wp_filesize( $temp_filepath ) + ); + + if ( ! isset( $valid_mimes[ $file_array[ 'type' ] ] ) + || ( strpos( $file_array[ 'type' ], 'image/' ) === 0 + && ! wp_getimagesize( $temp_filepath ) + ) + ) { + @unlink( $temp_filepath ); + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'The file is not a valid image/video.' + ), 400 ); + } + + $media_id = media_handle_sideload( $file_array, 0, null, array( + 'post_mime_type' => $file_array[ 'type' ], + 'post_title' => sanitize_text_field( pathinfo( $file_array[ 'name' ], PATHINFO_FILENAME ) ), + 'post_status' => 'inherit' + ) ); + + if ( file_exists( $temp_filepath ) ) { + @unlink( $temp_filepath ); + } + + if ( is_wp_error( $media_id ) ) { + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'An error occured during media upload.' + ), 500 ); + } + + update_post_meta( $media_id, 'stk_image_hash', $image_hash ); + $media_url = wp_get_attachment_url( $media_id ); + + return new WP_REST_Response( array( + 'success' => true, + 'new_url' => $media_url, + 'old_url' => $url + ), 200 ); + } + + public function filter_patterns( $pattern ) { + return strpos( $pattern[ 'name' ], 'stackable/' ) !== false; + } + + public function get_design_library_from_cloud() { + $designs = get_transient( 'stackable_get_design_library_json_v4' ); // Fetch designs. if ( empty( $designs ) ) { $designs = array(); $content = null; - $response = wp_remote_get( self::get_cdn_url() . 'library-v3/library.json' ); + $response = wp_remote_get( self::get_cdn_url() . 'library-v4/library.json' ); if ( is_wp_error( $response ) ) { // Add our error message so we can see it in the network tab. @@ -144,14 +276,46 @@ public function _get_design_library() { } - // We add the latest designs in the `v3` area. + // We add the latest designs in the `v4` area. $designs[ self::API_VERSION ] = $content; // Allow deprecated code to fetch other designs $designs = apply_filters( 'stackable_fetch_design_library', $designs ); // Cache results. - set_transient( 'stackable_get_design_library_v3', $designs, DAY_IN_SECONDS ); + set_transient( 'stackable_get_design_library_json_v4', $designs, DAY_IN_SECONDS ); + } + + return apply_filters( 'stackable_design_library', $designs ); + } + + public function _get_design_library( $outside_init = false ) { + $designs = get_transient( 'stackable_get_design_library_v4' ); + // Fetch designs. + if ( empty( $designs ) ) { + $designs = array(); + $content = array(); + + $block_patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered( $outside_init ); + foreach ( $block_patterns as $pattern ) { + if ( strpos( $pattern[ 'name' ], 'stackable/' ) !== false ) { + $pattern[ 'categories' ][ 0 ] = str_replace( 'stackable/', '', $pattern[ 'categories' ][ 0 ] ); + $content[ $pattern[ 'designId' ] ] = $pattern; + } + } + + // Get premium designs for v4 + $content = apply_filters( 'stackable_design_library_get_premium_designs', $content ); + + // We add the latest designs in the `v4` area. + $designs[ self::API_VERSION ] = $content; + + // Allow deprecated code to fetch other designs + $designs = apply_filters( 'stackable_fetch_design_library', $designs ); + + // Cache results. + set_transient( 'stackable_get_design_library_v4', $designs, DAY_IN_SECONDS ); + } return apply_filters( 'stackable_design_library', $designs ); @@ -161,85 +325,145 @@ public function _get_design_library() { * Gets and caches library designs. */ public function get_design_library( $request ) { - if ( $request->get_param( 'reset' ) ) { + $reset = $request->get_param( 'reset' ); + if ( $reset ) { $this->delete_cache(); } - return rest_ensure_response( $this->_get_design_library() ); + return rest_ensure_response( $this->_get_design_library( $reset ) ); } - public function get_design( $request ) { - $design_id = $request->get_param( 'design' ); - // We don't do anything with this one yet. - $library_version = $request->get_param( 'version' ); + public function get_disabled_blocks() { + $disabled_blocks = get_option( 'stackable_block_states' ); + $disabled_blocks = array_filter( $disabled_blocks, function ( $block_state ) { return $block_state == 3; } ); + if ( count( $disabled_blocks ) ) { + $disabled_blocks = array_keys( $disabled_blocks ); + $disabled_blocks = array_map( function ( $block ) { return preg_quote( $block, '/' ); }, $disabled_blocks ); + $disabled_blocks = '/' . implode( '|', $disabled_blocks ) . '/i'; + return $disabled_blocks; + } - $design = get_transient( 'stackable_get_design_' . $design_id ); + return false; + } - // Fetch designs. - if ( empty( $design ) ) { + public function check_for_disabled_block( $design, $disabled_blocks ) { + if ( preg_match( $disabled_blocks, $design ) ) { + return true; + } - // Get the template Url. - $designs = $this->_get_design_library(); - $template_url = $designs[ self::API_VERSION ][ $design_id ]['template']; + return false; + } - $response = wp_remote_get( $template_url ); - $content = wp_remote_retrieve_body( $response ); - $content = apply_filters( 'stackable_design_library_retreive_body', $content ); - $design = json_decode( $content, true ); + public function get_premium_designs( $content ) { + $designs = $this->get_design_library_from_cloud(); - // Cache results. - set_transient( 'stackable_get_design_' . $design_id, $design, DAY_IN_SECONDS ); + $library = $designs[ self::API_VERSION ]; + + $premium_designs = array(); + foreach ( $library as $design_id => $design ) { + if ( $design[ 'plan' ] === 'premium' && sugb_fs()->can_use_premium_code() && STACKABLE_BUILD === 'premium' ) { + continue; + } + + $premium_designs[ $design_id ] = array( + 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), + 'content' => $design[ 'template' ], + 'categories' => array( $design[ 'category' ], 'stackable' ), + 'description' => $design[ 'description' ], + 'plan' => $design[ 'plan' ], + 'designId' => $design_id + ); } - $design = apply_filters( 'stackable_design_' . $design_id, $design ); + $merged = array_merge( $content, $premium_designs ); - return rest_ensure_response( $design ); + uasort($merged, function( $design_1, $design_2 ) { + return strnatcmp( $design_1[ 'title' ], $design_2[ 'title' ] ); + }); + + return $merged; } - /** - * Replaces all the URLs that use the CDN with local ones if dev mode is turned on. - */ - public function replace_dev_mode_urls( $content ) { - if ( self::is_dev_mode() ) { - $content = str_replace( - trailingslashit( STACKABLE_DESIGN_LIBRARY_URL ), - trailingslashit( plugin_dir_url( STACKABLE_DLH_LIBRARY_FILE ) ), - $content + public function get_designs_with_disabled_blocks( $content ) { + $designs = $this->get_design_library_from_cloud(); + + $library = $designs[ self::API_VERSION ]; + + $designs_with_disabled = array(); + foreach ( $library as $design_id => $design ) { + if ( isset( $content[ $design_id ] ) ) { + continue; + } + + $designs_with_disabled[ $design_id ] = array( + 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), + 'content' => $design[ 'template' ], + 'categories' => array( $design[ 'category' ], 'stackable' ), + 'description' => $design[ 'description' ], + 'plan' => $design[ 'plan' ], + 'designId' => $design_id, ); } - return $content; + + $merged = array_merge( $content, $designs_with_disabled ); + + uasort($merged, function( $design_1, $design_2 ) { + return strnatcmp( $design_1[ 'title' ], $design_2[ 'title' ] ); + }); + + return $merged; } - /** - * Gets the URL of the CDN where to load our design library data. When - * developer mode for the design library is turned on, the URL of the - * design library internal exporter tool will be used instead. - */ - public static function get_cdn_url() { - if ( self::is_dev_mode() ) { - return trailingslashit( plugin_dir_url( STACKABLE_DLH_LIBRARY_FILE ) ); - } else { - return trailingslashit( STACKABLE_DESIGN_LIBRARY_URL ); + public function register_design_pattern() { + $designs = $this->get_design_library_from_cloud(); + + $library = $designs[ self::API_VERSION ]; + $disabled_blocks = $this->get_disabled_blocks(); + + + if ( ! WP_Block_Pattern_Categories_Registry::get_instance()->is_registered( 'stackable' ) ) { + register_block_pattern_category( 'stackable', [ + 'label' => __( 'Stackable', STACKABLE_I18N ), + 'description' => __( 'Patterns for Stackable Design Library', STACKABLE_I18N ), + ] ); } - } - public static function is_dev_mode() { - $dev_env = defined( 'WP_ENV' ) ? WP_ENV === 'development' : false; - $export_tool_installed = defined( 'STACKABLE_DLH_LIBRARY_FILE' ); - $library_dev_mode_toggled = !! get_option( 'stackable_library_dev_mode' ); - return $dev_env && $export_tool_installed && $library_dev_mode_toggled; + foreach ( $library as $design_id => $design ) { + if ( $design[ 'plan' ] === 'premium' && ( ! sugb_fs()->can_use_premium_code() || STACKABLE_BUILD === 'free' ) ) { + continue; + } + + if ( $disabled_blocks ) { + $has_disabled = $this->check_for_disabled_block( $design[ 'template' ], $disabled_blocks ); + if ( $has_disabled ) continue; + } + + register_block_pattern_category( 'stackable/' . $design[ 'category' ], [ + 'label' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'category' ] ), + 'description' => sprintf( __( '%s patterns for Stackable Design Library', STACKABLE_I18N ), $design[ 'category' ] ), + ] ); + + register_block_pattern( + 'stackable/' . $design_id, + array( + 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), + 'content' => $design[ 'template' ], + 'categories' => array( 'stackable/' . $design[ 'category' ], 'stackable' ), + 'description' => $design[ 'description' ], + 'plan' => $design[ 'plan' ], + 'designId' => $design_id + ) + ); + } } /** - * Set developer mode for the design library. When in developer mode, - * the library that will be loaded will be from the design library - * internal exporter tool. + * Gets the URL of the CDN where to load our design library data. When + * developer mode for the design library is turned on, the URL of the + * design library internal exporter tool will be used instead. */ - public function set_dev_mode( $request ) { - $dev_mode = $request->get_param( 'devmode' ); - update_option( 'stackable_library_dev_mode', $dev_mode, 'no' ); - - return rest_ensure_response( true ); + public static function get_cdn_url() { + return trailingslashit( STACKABLE_DESIGN_LIBRARY_URL ); } } diff --git a/src/hooks/use-block-color-schemes.js b/src/hooks/use-block-color-schemes.js index 4968620f05..6241d459f1 100644 --- a/src/hooks/use-block-color-schemes.js +++ b/src/hooks/use-block-color-schemes.js @@ -1,5 +1,5 @@ import { i18n } from 'stackable' -import { kebabCase } from 'lodash' +import { cloneDeep, kebabCase } from 'lodash' import { useSelect } from '@wordpress/data' import { applyFilters } from '@wordpress/hooks' @@ -19,6 +19,7 @@ export const useBlockColorSchemes = () => { getScheme, getColorGroups, allColorSchemes, + colorSchemesCollection, COLOR_SCHEME_OPTIONS, baseColorScheme, backgroundModeColorScheme, @@ -44,6 +45,26 @@ export const useBlockColorSchemes = () => { disabled: ! schemeHasValue( scheme.colorScheme ), } ) ) ] + const colorSchemesCollection = allColorSchemes.reduce( ( obj, _scheme ) => { + const scheme = cloneDeep( _scheme ) + + if ( ! schemeHasValue( scheme.colorScheme ) ) { + return obj + } + // if ( scheme.key === 'scheme-default-2' && ! schemeHasValue( scheme.colorScheme ) ) { + // scheme.colorScheme.backgroundColor.desktop = 'var(--stk-block-background-color)' + // } + + const desktopColors = Object.fromEntries( + Object.entries( scheme.colorScheme ).map( ( [ key, value ] ) => [ key, value.desktop ] ) + ) + + obj[ scheme.key ] = scheme + obj[ scheme.key ].desktopColors = desktopColors + + return obj + }, {} ) + // Returns the color scheme slug if it exists, otherwise return a fallback value const getScheme = ( key, { mode = '', returnFallback = true } = {} ) => { const fallback = mode === 'background' ? 'scheme-default-2' : 'scheme-default-1' @@ -131,6 +152,7 @@ export const useBlockColorSchemes = () => { getScheme, getColorGroups, allColorSchemes, + colorSchemesCollection, COLOR_SCHEME_OPTIONS, baseColorScheme: getScheme( _baseColorScheme ), backgroundModeColorScheme: getScheme( _backgroundModeColorScheme, 'background' ), @@ -142,6 +164,7 @@ export const useBlockColorSchemes = () => { getScheme, getColorGroups, allColorSchemes, + colorSchemesCollection, COLOR_SCHEME_OPTIONS, baseColorScheme, backgroundModeColorScheme, diff --git a/src/init.php b/src/init.php index b1f91dae3e..9870f27f01 100644 --- a/src/init.php +++ b/src/init.php @@ -334,6 +334,8 @@ public function register_block_editor_assets() { $version_parts = explode( '-', STACKABLE_VERSION ); + $wp_global_styles = wp_get_global_stylesheet(); + global $content_width; global $wp_version; $args = apply_filters( 'stackable_localize_script', array( @@ -369,6 +371,9 @@ public function register_block_editor_assets() { 'settings' => apply_filters( 'stackable_js_settings', array() ), 'isContentOnlyMode' => apply_filters( 'stackable_editor_role_is_content_only', false ), 'blockCategoryIndex' => apply_filters( 'stackable_block_category_index', 0 ), + + // Global Styles for Design Library + 'wpGlobalStylesInlineCss' => $wp_global_styles, ) ); wp_localize_script( 'wp-blocks', 'stackable', $args ); } diff --git a/src/plugins/global-settings/color-schemes/color-scheme-picker.js b/src/plugins/global-settings/color-schemes/color-scheme-picker.js index 13bfc86709..9a1a8e4b3f 100644 --- a/src/plugins/global-settings/color-schemes/color-scheme-picker.js +++ b/src/plugins/global-settings/color-schemes/color-scheme-picker.js @@ -106,6 +106,12 @@ const ColorSchemePicker = props => { const currentState = `desktop${ hoverState[ currentHoverState ] }` + const changeDefaultName = ! itemInEdit?.key.startsWith( 'scheme-default' ) ? false + : ( itemInEdit?.key === 'scheme-default-1' + ? itemInEdit?.name === __( 'Default Scheme', i18n ) + : itemInEdit?.name === __( 'Background Scheme', i18n ) + ) + const showResetButton = item => { return ! isEqual( item.colorScheme, DEFAULT_COLOR_SCHEME_COLORS ) } @@ -197,6 +203,11 @@ const ColorSchemePicker = props => { } const currentItem = cloneDeep( itemInEdit ) + + if ( ! schemeHasValue( currentItem.colorScheme ) && changeDefaultName ) { + currentItem.name = currentItem?.key === 'scheme-default-1' ? 'Color Scheme 1' : 'Color Scheme 2' + } + currentItem.colorScheme[ property ][ currentState ] = color if ( property === 'backgroundColor' && isGradient( color ) ) { @@ -227,6 +238,11 @@ const ColorSchemePicker = props => { return } const currentItem = cloneDeep( itemInEdit ) + + if ( ! schemeHasValue( currentItem.colorScheme ) && changeDefaultName ) { + currentItem.name = currentItem?.key === 'scheme-default-1' ? __( 'Color Scheme 1', i18n ) : __( 'Color Scheme 2', i18n ) + } + if ( currentHoverState === 'normal' ) { currentItem.colorScheme = cloneDeep( DEFAULT_COLOR_SCHEME_COLORS ) } @@ -252,6 +268,14 @@ const ColorSchemePicker = props => { } const currentItem = cloneDeep( item ) + if ( currentItem.key === 'scheme-default-1' && currentItem.name === __( 'Color Scheme 1', i18n ) ) { + currentItem.name = __( 'Default Scheme', i18n ) + } + + if ( currentItem.key === 'scheme-default-2' && currentItem.name === __( 'Color Scheme 2', i18n ) ) { + currentItem.name = __( 'Background Scheme', i18n ) + } + currentItem.colorScheme = cloneDeep( DEFAULT_COLOR_SCHEME_COLORS ) if ( itemInEdit ) { diff --git a/src/plugins/global-settings/color-schemes/index.js b/src/plugins/global-settings/color-schemes/index.js index 74ebae6ed7..922c4b1db9 100644 --- a/src/plugins/global-settings/color-schemes/index.js +++ b/src/plugins/global-settings/color-schemes/index.js @@ -8,17 +8,17 @@ import ColorSchemePicker from './color-scheme-picker' /** * External dependencies */ +import { i18n } from 'stackable' import { - i18n, showProNotice, isPro, -} from 'stackable' -import { - PanelAdvancedSettings, ProControlButton, HelpTooltip, + PanelAdvancedSettings, HelpTooltip, + AdvancedSelectControl, SectionSettings, } from '~stackable/components' +import { useBlockColorSchemes } from '~stackable/hooks' /** * WordPress dependencies */ -import { addFilter, applyFilters } from '@wordpress/hooks' +import { addFilter } from '@wordpress/hooks' import { Fragment, useState } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { ToggleControl } from '@wordpress/components' @@ -27,11 +27,40 @@ import { models } from '@wordpress/api' export { GlobalColorSchemeStyles } from './editor-loader' +let saveTimeout = null + +const COLOR_SCHEME_OPTION_NAMES = { + baseColorScheme: 'stackable_global_base_color_scheme', + backgroundModeColorScheme: 'stackable_global_background_mode_color_scheme', + containerModeColorScheme: 'stackable_global_container_mode_color_scheme', +} + addFilter( 'stackable.global-settings.inspector', 'stackable/global-color-schemes', output => { const [ itemInEdit, setItemInEdit ] = useState( null ) const [ isOpen, setIsOpen ] = useState( false ) const [ displayHoverNotice, setDisplayHoverNotice ] = useState( false ) + const { + COLOR_SCHEME_OPTIONS, + baseColorScheme, + backgroundModeColorScheme, + containerModeColorScheme, + } = useBlockColorSchemes() + + const onChangeDefaultColorScheme = ( mode, colorSchemeKey ) => { + clearTimeout( saveTimeout ) + saveTimeout = setTimeout( () => { + const settings = new models.Settings( { + [ COLOR_SCHEME_OPTION_NAMES[ mode ] ]: colorSchemeKey, // eslint-disable-line camelcase + stackable_global_color_scheme_generated_css: '', // eslint-disable-line camelcase + } ) + settings.save() + }, 300 ) + + // Update our store. + dispatch( 'stackable/global-color-schemes' ).updateSettings( { [ mode ]: colorSchemeKey } ) + } + return ( { output } @@ -67,8 +96,34 @@ addFilter( 'stackable.global-settings.inspector', 'stackable/global-color-scheme setItemInEdit={ setItemInEdit } setDisplayHoverNotice={ setDisplayHoverNotice } /> - { isPro && applyFilters( 'stackable.global-settings.global-color-schemes.inspector', Fragment, itemInEdit ) } - { ! itemInEdit && showProNotice && } + { ! itemInEdit && <> + + onChangeDefaultColorScheme( 'baseColorScheme', colorScheme ) } + help={ __( 'Default color scheme to use for all blocks when no special options are enabled.', i18n ) } + /> + onChangeDefaultColorScheme( 'backgroundModeColorScheme', colorScheme ) } + help={ __( 'Colors applied when the background option is enabled for a block.', i18n ) } + /> + onChangeDefaultColorScheme( 'containerModeColorScheme', colorScheme ) } + help={ __( 'Colors applied when the container option is enabled for a block.', i18n ) } + /> + + } ) diff --git a/src/plugins/global-settings/editor-loader.js b/src/plugins/global-settings/editor-loader.js index c516283d8d..957f4f714e 100644 --- a/src/plugins/global-settings/editor-loader.js +++ b/src/plugins/global-settings/editor-loader.js @@ -25,6 +25,7 @@ import { useEffect } from '@wordpress/element' import { __ } from '@wordpress/i18n' import { useSelect } from '@wordpress/data' import domReady from '@wordpress/dom-ready' +import { addFilter } from '@wordpress/hooks' const GlobalSettingsLoader = () => { const deviceType = useDeviceType() @@ -70,6 +71,20 @@ globalColorWrapper?.setAttribute( 'id', 'stk-global-color-styles' ) globalSpacingAndBorderWrapper?.setAttribute( 'id', 'stk-global-spacing-and-borders-styles' ) globalButtonsAndIconsWrapper?.setAttribute( 'id', 'stk-global-buttons-and-icons-styles' ) globalColorSchemesWrapper?.setAttribute( 'id', 'stk-global-color-schemes-styles' ) + +addFilter( 'stackable.global-styles.ids', 'stackable/global-settings', styleIds => { + styleIds.push( + 'stk-global-typography-styles', + 'stk-global-color-styles', + 'stk-global-spacing-and-borders-styles', + 'stk-global-buttons-and-icons-styles', + 'stk-global-color-schemes-styles', + 'stk-global-preset-controls-styles' + ) + + return styleIds +} ) + domReady( () => { document?.body?.appendChild( globalTypographyWrapper ) document?.body?.appendChild( globalColorWrapper ) diff --git a/src/plugins/global-settings/typography/editor-loader.js b/src/plugins/global-settings/typography/editor-loader.js index 6c7f39da55..dd2a24e0d4 100644 --- a/src/plugins/global-settings/typography/editor-loader.js +++ b/src/plugins/global-settings/typography/editor-loader.js @@ -135,7 +135,7 @@ export const formClassOrTagSelectors = ( selector, applyTo ) => { const isClassSelector = selector.startsWith( '.' ) // Include Stackable blocks. - selectors.push( `[data-type^="stackable/"] ${ selector }` ) + selectors.push( `:is([data-type^="stackable/"], .stk-block) ${ selector }` ) // Include native blocks. if ( isClassSelector || applyTo !== 'blocks-stackable' ) { diff --git a/src/util/admin.js b/src/util/admin.js index e85265dcf7..e8403508b3 100644 --- a/src/util/admin.js +++ b/src/util/admin.js @@ -12,6 +12,7 @@ import { loadPromise, models } from '@wordpress/api' // them by type. export const importBlocks = r => { const blocks = {} + const blockDependencies = {} r.keys().forEach( key => { const meta = r( key ) const type = meta[ 'stk-type' ] @@ -35,11 +36,16 @@ export const importBlocks = r => { } ) } } ) + + if ( meta[ 'stk-block-dependency' ] ) { + blockDependencies[ meta.name ] = meta[ 'stk-block-dependency' ] + } } ) + Object.keys( blocks ).forEach( type => { blocks[ type ] = sortBy( blocks[ type ], 'name' ) } ) - return blocks + return [ blocks, blockDependencies ] } let fetchingPromise = null diff --git a/src/welcome/admin.js b/src/welcome/admin.js index a428c661f0..8d0bf27ebb 100644 --- a/src/welcome/admin.js +++ b/src/welcome/admin.js @@ -42,7 +42,7 @@ import { BLOCK_STATE } from '~stackable/util/blocks' import { BlockToggler, OptimizationSettings } from '~stackable/deprecated/v2/welcome/admin' import blockData from '~stackable/deprecated/v2/welcome/blocks' -const FREE_BLOCKS = importBlocks( require.context( '../block', true, /block\.json$/ ) ) +const [ FREE_BLOCKS, BLOCK_DEPENDENCIES ] = importBlocks( require.context( '../block', true, /block\.json$/ ) ) export const getAllBlocks = () => applyFilters( 'stackable.settings.blocks', FREE_BLOCKS ) @@ -270,6 +270,17 @@ const getParentBlocks = blockName => { return parents } +export const getBlockTitle = name => { + for ( const category in DERIVED_BLOCKS ) { + for ( const block of DERIVED_BLOCKS[ category ] ) { + if ( block.name === name ) { + return block.title + } + } + } + return name +} + const BlockList = () => { const DERIVED_BLOCKS = getAllBlocks() return ( @@ -331,25 +342,14 @@ const RestSettingsNotice = () => { // Confirmation dialog when disabling a block that is dependent on another block. const ToggleBlockDialog = ( { - blockName, + blockName = '', blockList, isDisabled, onConfirm, onCancel, + hideHeader = false, + customText = '', } ) => { - const DERIVED_BLOCKS = getAllBlocks() - - const getBlockTitle = name => { - for ( const category in DERIVED_BLOCKS ) { - for ( const block of DERIVED_BLOCKS[ category ] ) { - if ( block.name === name ) { - return block.title - } - } - } - return name - } - const blockTitle = getBlockTitle( blockName ) return ( @@ -360,10 +360,13 @@ const ToggleBlockDialog = ( { ? sprintf( __( 'Disable %s block?', i18n ), blockTitle ) : sprintf( __( 'Enable %s block?', i18n ), blockTitle ) } onRequestClose={ onCancel } + __experimentalHideHeader={ hideHeader } > - { isDisabled - ?

    { __( 'Disabling this block will also disable these blocks that require this block to function:', i18n ) }

    // eslint-disable-line @wordpress/i18n-no-variables - :

    { __( 'Enabling this block will also enable these blocks that are needed for this block to function:', i18n ) }

    // eslint-disable-line @wordpress/i18n-no-variables + { hideHeader ? customText + : ( isDisabled + ?

    { __( 'Disabling this block will also disable these blocks that require this block to function:', i18n ) }

    // eslint-disable-line @wordpress/i18n-no-variables + :

    { __( 'Enabling this block will also enable these blocks that are needed for this block to function:', i18n ) }

    // eslint-disable-line @wordpress/i18n-no-variables + ) }
      { blockList.map( ( block, i ) => ( @@ -443,6 +446,7 @@ const Sidenav = ( { onKeyDown={ () => handleTabChange( id ) } role="tab" tabIndex={ 0 } + id={ `stk-tab__${ id }` } > { label } @@ -500,6 +504,7 @@ const Settings = () => { const [ currentTab, setCurrentTab ] = useState( 'editor-settings' ) const [ currentSearch, setCurrentSearch ] = useState( '' ) const [ isSaving, setIsSaving ] = useState( false ) + const [ isFetching, setIsFetching ] = useState( false ) const [ isRecentlySaved, setIsRecentlySaved ] = useState( false ) const [ hasV2Tab, setHasV2Tab ] = useState( false ) @@ -534,10 +539,12 @@ const Settings = () => { }, [ unsavedChanges, settings ] ) useEffect( () => { + setIsFetching( true ) fetchSettings().then( response => { setSettings( response ) // Should only be set initially since we have to reload after setting for it to work with the backend setHasV2Tab( hasV2Compatibility( response ) ) + setIsFetching( false ) } ) }, [] ) @@ -589,6 +596,7 @@ const Settings = () => { handleSettingsChange, filteredSearchTree, currentTab, + isFetching, } return <> @@ -866,6 +874,7 @@ const Blocks = props => { settings, handleSettingsChange, filteredSearchTree, + isFetching, } = props const BLOCK_STATE_MAP = Object.freeze( { @@ -883,6 +892,33 @@ const Blocks = props => { const [ currentToggleBlock, setCurrentToggleBlock ] = useState( '' ) const [ currentToggleBlockList, setCurrentToggleBlockList ] = useState( [] ) + const [ blocksToEnable, setBlocksToEnable ] = useState( [] ) + + useEffect( () => { + // eslint-disable-next-line @wordpress/no-global-event-listener + window.addEventListener( 'message', ev => { + if ( typeof ev.data !== 'object' || ev.data.source !== 'STK_DESIGN_LIBRARY' || ev.origin !== window.location.origin ) { + return + } + + if ( ev.data.type === 'STK_ENABLE_BLOCKS' && ev.data.blocks instanceof Set && ev.data.blocks.size ) { + const blocks = [ ...ev.data.blocks ].map( block => { + if ( block in BLOCK_DEPENDENCIES ) { + return BLOCK_DEPENDENCIES[ block ] + } + return block + } ) + setBlocksToEnable( blocks ) + } + } ) + }, [] ) + + useEffect( () => { + if ( ! isFetching && blocksToEnable.length ) { + setIsEnabledDialogOpen( true ) + } + }, [ isFetching, blocksToEnable ] ) + // Map string states to integer block states const mapStringStates = states => { return states?.map( @@ -976,11 +1012,11 @@ const Blocks = props => { handleSettingsChange( { stackable_block_states: newDisabledBlocks } ) // eslint-disable-line camelcase } - const handleEnableDialogConfirm = () => { + const handleEnableDialogConfirm = ( blockList = currentToggleBlockList ) => { setIsEnabledDialogOpen( false ) const newDisabledBlocks = { ...disabledBlocks } delete newDisabledBlocks[ currentToggleBlock ] - currentToggleBlockList.forEach( block => { + blockList.forEach( block => { delete newDisabledBlocks[ block ] } ) handleSettingsChange( { stackable_block_states: newDisabledBlocks } ) // eslint-disable-line camelcase @@ -988,26 +1024,42 @@ const Blocks = props => { return ( <> - { isDisabledDialogOpen && currentToggleBlockList && ( + { isDisabledDialogOpen && currentToggleBlockList.length !== 0 && ( handleDisableDialogConfirm() } onCancel={ () => { setIsDisabledDialogOpen( false ) } } /> ) } - { isEnabledDialogOpen && currentToggleBlockList && ( + { isEnabledDialogOpen && currentToggleBlockList.length !== 0 && ( handleEnableDialogConfirm() } + onCancel={ () => { + setIsEnabledDialogOpen( false ) + } } + /> + ) } + { isEnabledDialogOpen && blocksToEnable.length !== 0 && ( + { + handleEnableDialogConfirm( blocksToEnable ) + setBlocksToEnable( [] ) + } } onCancel={ () => { setIsEnabledDialogOpen( false ) + setBlocksToEnable( [] ) } } /> ) } From 6ed023be261934a63a4573fa946bca2bfd95557f Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Wed, 28 May 2025 14:09:21 +0800 Subject: [PATCH 096/124] fix: do not actually change value when switching mode --- .../advanced-range-control/index.js | 57 ++++-- src/components/four-range-control/index.js | 175 ++++++------------ 2 files changed, 95 insertions(+), 137 deletions(-) diff --git a/src/components/advanced-range-control/index.js b/src/components/advanced-range-control/index.js index dbfc373777..874d1c52ed 100644 --- a/src/components/advanced-range-control/index.js +++ b/src/components/advanced-range-control/index.js @@ -26,7 +26,7 @@ import { settings as stackableSettings } from 'stackable' * WordPress dependencies */ import { - memo, useState, useEffect, + memo, useState, useEffect, useRef, } from '@wordpress/element' import { Button } from '@wordpress/components' import { settings } from '@wordpress/icons' @@ -117,6 +117,8 @@ const AdvancedRangeControl = props => { // at the start, or show custom // If no initial value, use the given default from the settings const [ isMarkMode, setIsMarkMode ] = useState( false ) + // Ensure the convesion of value from preset to custom with regards to the unit is donce once. + const isConversionDone = useRef( false ) let isMarkValue = !! props.marks && isMarkModeDefault if ( props.marks && derivedValue ) { @@ -203,8 +205,16 @@ const AdvancedRangeControl = props => { let rangeOnChange = _onChange if ( isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - return _value === derivedValue + let _unit, _value + // If the derivedValue is a CSS variable, compare with mark's CSS variable. + // Otherwise, the derivedValue is custom from the previous switch from custom to preset mode, + // so compare with raw size and units to convert to preset. + if ( typeof derivedValue === 'string' && derivedValue.startsWith( 'var' ) ) { + [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + } else { + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + } + return _value === derivedValue && ( _unit === '' || _unit === unit ) } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { @@ -230,6 +240,30 @@ const AdvancedRangeControl = props => { } _onChange( newValue ) + isConversionDone.current = false + } + } else if ( typeof derivedValue === 'string' && derivedValue.startsWith( 'var' ) ) { + // If the derivedValue is a preset and currently not in mark mode, the derivedValue is from + // the previous switch from preset to custom mode. Convert to custom. + const currentSize = props.marks.find( mark => { + return derivedValue === mark.value + } )?.size + const [ _newValue, _unit ] = extractNumbersAndUnits( currentSize )[ 0 ] + rangeValue = _newValue + + if ( _unit && ! isConversionDone.current ) { + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: _unit } ) + if ( props.onChangeUnit ) { + props.onChangeUnit( _unit ) + } + isConversionDone.current = true + } + // Since the actual previous value is a preset, force the new custom value + // when changing unit + controlProps.onChangeUnit = ( unit, unitAttrName ) => { + setAttributes( { [ unitAttrName ]: unit } ) + _onChange( _newValue ) } } @@ -254,23 +288,6 @@ const AdvancedRangeControl = props => { size="small" variant="tertiary" onClick={ () => { - // Set the value when changing from mark mode to custom - if ( isMarkMode && rangeValue !== -1 ) { - rangeOnChange( rangeValue, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - - return _value === derivedValue && ( _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - _onChange( markValue ) - } setIsMarkMode( ! isMarkMode ) } } icon={ settings } diff --git a/src/components/four-range-control/index.js b/src/components/four-range-control/index.js index 0d0c191d0d..132deb5897 100644 --- a/src/components/four-range-control/index.js +++ b/src/components/four-range-control/index.js @@ -25,7 +25,7 @@ import { useControlHandlers } from '../base-control2/hooks' import { Tooltip } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { - Fragment, useState, memo, useEffect, + Fragment, useState, memo, useEffect, useRef, } from '@wordpress/element' import { settings } from '@wordpress/icons' import { dispatch } from '@wordpress/data' @@ -190,6 +190,16 @@ const FourRangeControl = memo( props => { : { desktop: _valueDesktop?.left, tablet: _valueTablet?.left } const [ isFourMarkMode, setIsFourMarkMode ] = useState( false ) + // Ensure the convesion of value from preset to custom with regards to the unit is donce once. + const isConversionDone = useRef( { + first: false, + top: false, + right: false, + bottom: false, + left: false, + vertical: false, + horizontal: false, + } ) // Is value at first render the same as a step value? If so, do mark mode // at the start, or show custom @@ -318,7 +328,7 @@ const FourRangeControl = memo( props => { } ) ) } // Support for steps. Modify the props to make the range control show steps. - const stepSupport = ( isMarkMode, initialValue, initialOnChange ) => { + const stepSupport = ( isMarkMode, initialValue, initialOnChange, conversionKey = null ) => { const newProps = { ...propsToPass } if ( props.marks && isMarkMode ) { @@ -360,8 +370,16 @@ const FourRangeControl = memo( props => { let rangeOnChange = initialOnChange if ( props.marks && isMarkMode ) { rangeValue = props.marks.findIndex( mark => { - const [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] - return _value === initialValue + let _unit, _value + // If the initialValue is a CSS variable, compare with mark's CSS variable. + // Otherwise, the initialValue is custom from the previous switch from custom to preset mode, + // so compare with raw size and units to convert to preset. + if ( typeof initialValue === 'string' && initialValue.startsWith( 'var' ) ) { + [ _value, _unit ] = extractNumbersAndUnits( mark.value )[ 0 ] + } else { + [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] + } + return _value === initialValue && ( _unit === '' || _unit === unit ) } ) rangeOnChange = ( value, property = 'value' ) => { if ( value === '' ) { @@ -388,11 +406,39 @@ const FourRangeControl = memo( props => { } initialOnChange( newValue ) + isConversionDone.current[ conversionKey ] = false + } + } else if ( typeof initialValue === 'string' && initialValue.startsWith( 'var' ) && + ( ( isLocked && conversionKey === 'first' ) || + ( ! isLocked && [ 'top', 'right', 'left', 'bottom' ].includes( conversionKey ) ) || + ( isLocked && props.vhMode && [ 'vertical', 'horizontal' ].includes( conversionKey ) ) ) + ) { + // If the derivedValue is a preset and currently not in mark mode, the derivedValue is from + // the previous switch from preset to custom mode. Convert to custom. + const currentSize = props.marks.find( mark => { + return initialValue === mark.value + } )?.size + const [ _newValue, _unit ] = extractNumbersAndUnits( currentSize )[ 0 ] + rangeValue = _newValue + + if ( _unit && conversionKey && ! isConversionDone.current[ conversionKey ] ) { + isConversionDone.current[ conversionKey ] = true + dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent() + setAttributes( { [ unitAttrName ]: _unit } ) + if ( props.onChangeUnit ) { + props.onChangeUnit( _unit ) + } + } + // Since the actual previous value is a preset, force the new custom value + // when changing unit + controlProps.onChangeUnit = ( unit, unitAttrName ) => { + initialOnChange( _newValue ) + setAttributes( { [ unitAttrName ]: unit } ) } } return [ - newProps, rangeValue, rangeOnChange, + newProps, parseFloat( rangeValue ), rangeOnChange, ] } @@ -413,42 +459,49 @@ const FourRangeControl = memo( props => { isFourMarkMode.first, firstValue, onChangeAll, + 'first', ) const [ propsToPassTop, rangeValueTop, rangeOnChangeTop ] = stepSupport( isFourMarkMode.top, value.top, onChangeTop, + 'top' ) const [ propsToPassRight, rangeValueRight, rangeOnChangeRight ] = stepSupport( isFourMarkMode.right, value.right, onChangeRight, + 'right' ) const [ propsToPassBottom, rangeValueBottom, rangeOnChangeBottom ] = stepSupport( isFourMarkMode.bottom, value.bottom, onChangeBottom, + 'bottom' ) const [ propsToPassLeft, rangeValueLeft, rangeOnChangeLeft ] = stepSupport( isFourMarkMode.left, value.left, onChangeLeft, + 'left' ) const [ propsToPassVertical, rangeValueVertical, rangeOnChangeVertical ] = stepSupport( isFourMarkMode.top, value.top, onChangeVertical, + 'vertical' ) const [ propsToPassHorizontal, rangeValueHorizontal, rangeOnChangeHorizontal ] = stepSupport( isFourMarkMode.left, value.left, onChangeHorizontal, + 'horizontal' ) return ( @@ -494,23 +547,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.first setIsFourMarkMode( prev => ( { ...prev, first: ! prev.first } ) ) - - if ( previousMarkMode && rangeValueFirst !== -1 ) { - rangeOnChangeFirst( rangeValueFirst, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === firstValue && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeAll( markValue ) - } } } icon={ settings } > @@ -570,23 +607,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.top setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) - - if ( previousMarkMode && rangeValueTop !== -1 ) { - rangeOnChangeTop( rangeValueTop, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.top && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeVertical( markValue ) - } } } icon={ settings } > @@ -642,23 +663,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.left setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) - - if ( previousMarkMode && rangeValueLeft !== -1 ) { - rangeOnChangeLeft( rangeValueLeft, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.left && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeHorizontal( markValue ) - } } } icon={ settings } > @@ -720,23 +725,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.top setIsFourMarkMode( prev => ( { ...prev, top: ! prev.top } ) ) - - if ( previousMarkMode && rangeValueTop !== -1 ) { - rangeOnChangeTop( rangeValueTop, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.top && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeTop( markValue ) - } } } icon={ settings } > @@ -795,23 +784,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.right setIsFourMarkMode( prev => ( { ...prev, right: ! prev.right } ) ) - - if ( previousMarkMode && rangeValueRight !== -1 ) { - rangeOnChangeRight( rangeValueRight, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.right && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeRight( markValue ) - } } } icon={ settings } > @@ -870,23 +843,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.bottom setIsFourMarkMode( prev => ( { ...prev, bottom: ! prev.bottom } ) ) - - if ( previousMarkMode && rangeValueBottom !== -1 ) { - rangeOnChangeBottom( rangeValueBottom, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.bottom && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeBottom( markValue ) - } } } icon={ settings } > @@ -945,23 +902,7 @@ const FourRangeControl = memo( props => { size="small" variant="tertiary" onClick={ () => { - const previousMarkMode = isFourMarkMode.left setIsFourMarkMode( prev => ( { ...prev, left: ! prev.left } ) ) - - if ( previousMarkMode && rangeValueLeft !== -1 ) { - rangeOnChangeLeft( rangeValueLeft, 'size' ) - } else { - const rangeValue = props.marks.findIndex( mark => { - let _unit, _value - [ _value, _unit ] = extractNumbersAndUnits( mark.size )[ 0 ] - const converted = convertToPxIfUnsupported( props.units, _unit, _value ) - _value = converted.value - _unit = converted.unit - return _value === value.left && ( ! unit || _unit === '' || _unit === unit ) - } ) - const markValue = props.marks[ rangeValue ]?.value || '0' - onChangeLeft( markValue ) - } } } icon={ settings } > From 9b469a19738e8599391a5184df55d6645539a4ea Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 28 May 2025 20:58:00 +0800 Subject: [PATCH 097/124] remove error --- src/design-library/init.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/design-library/init.php b/src/design-library/init.php index 6990806e77..cb70833860 100644 --- a/src/design-library/init.php +++ b/src/design-library/init.php @@ -38,7 +38,6 @@ class Stackable_Design_Library { */ public function __construct() { add_action( 'rest_api_init', array( $this, 'register_route' ) ); - add_filter( 'stackable_design_library_retreive_body', array( $this, 'replace_dev_mode_urls' ) ); add_filter( 'stackable_design_library_get_premium_designs', array( $this, 'get_designs_with_disabled_blocks' ) ); add_filter( 'stackable_design_library_get_premium_designs', array( $this, 'get_premium_designs' ) ); From a7aea623198f873a6fc97e49d6180f5219f9d0fd Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 28 May 2025 21:07:11 +0800 Subject: [PATCH 098/124] fix render of preview --- .../design-library-list/design-library-list-item.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index 7a351bcb83..594b4bb3b2 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -312,9 +312,9 @@ const DesignPreview = ( { useEffect( () => { if ( prevEnableBackground !== enableBackground ) { // Adjust scale if the background was toggled - setTimeout( adjustScale, 50 ) + adjustScale() } - }, [ blocks ] ) + }, [ blocks, enableBackground ] ) return ( Date: Mon, 2 Jun 2025 10:04:50 +0800 Subject: [PATCH 099/124] updates - modal for adding designs with disabled blocks - fix icon list substitution - fix style options spacing - hide design library modal header tabs - fix php error if download fails --- src/block/design-library/edit.js | 36 +++++++++++++------ src/block/icon-list/substitute.js | 14 ++++---- .../design-library-list-item.js | 2 +- src/components/design-library-list/index.js | 2 -- src/components/modal-design-library/modal.js | 12 +++---- src/design-library/index.js | 9 +---- src/design-library/init.php | 1 - 7 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/block/design-library/edit.js b/src/block/design-library/edit.js index 78202a7b30..13f3256e89 100644 --- a/src/block/design-library/edit.js +++ b/src/block/design-library/edit.js @@ -162,6 +162,22 @@ const createBlockWithAttributes = async ( blockName, attributes, innerBlocks, su attributes = block[ 0 ].attributes innerBlocks = block[ 0 ].innerBlocks + if ( attributes.hasBackground ) { + attributes.blockMargin = { + top: '', + right: '', + bottom: '0', + left: '', + } + } else { + attributes.blockMargin = { + top: '120', + right: '', + bottom: '120', + left: '', + } + } + return createBlock( blockName, attributes, createBlocksFromInnerBlocksTemplate( innerBlocks ) ) } @@ -305,7 +321,7 @@ const Edit = props => { { isDialogOpen && setIsDialogOpen( false ) } > @@ -317,26 +333,26 @@ const Edit = props => { { __( 'These blocks can be enabled in the Stackable settings page. Do you want to keep the disabled blocks or substitute them with other Stackable or core blocks?', i18n ) } - + diff --git a/src/block/icon-list/substitute.js b/src/block/icon-list/substitute.js index ac1c1da6c1..ae4eecb46e 100644 --- a/src/block/icon-list/substitute.js +++ b/src/block/icon-list/substitute.js @@ -1,14 +1,16 @@ export const substitute = { from: 'stackable/icon-list', - transform: () => { + transform: ( oldAttributes, innerBlocks ) => { + const newInnerBlocks = innerBlocks.reduce( ( newInnerBlocks, innerBlock ) => { + const attributes = innerBlock[ 1 ] + + newInnerBlocks.push( [ 'core/list-item', { content: attributes.text } ] ) + return newInnerBlocks + }, [] ) return [ 'core/list', {}, - [ - [ 'core/list-item', { content: 'First item list' } ], - [ 'core/list-item', { content: 'Second item list' } ], - [ 'core/list-item', { content: 'Third item list' } ], - ], + newInnerBlocks, ] }, } diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index 594b4bb3b2..33715a4fa0 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -230,7 +230,7 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { ) } renderContent={ ( { onClose } ) => ( @@ -264,17 +273,7 @@ export const ModalDesignLibrary = props => { } ) } { Object.keys( colorSchemesCollection ).length - ? ( - - ) } /> + ? : } @@ -282,9 +281,14 @@ export const ModalDesignLibrary = props => { ) } /> - + + { __( 'Learn more about container schemes here.', i18n ) } + }> + { __( 'Container Scheme', i18n ) } + } + className="ugb-modal-design-library__color-scheme-label" + > { } ) } { Object.keys( colorSchemesCollection ).length - ? ( - - ) } /> + ? : } diff --git a/src/plugins/editor-device-preview-class/index.js b/src/plugins/editor-device-preview-class/index.js index f32c3fc7c5..06cf13c5d6 100644 --- a/src/plugins/editor-device-preview-class/index.js +++ b/src/plugins/editor-device-preview-class/index.js @@ -15,6 +15,7 @@ import { useDeviceType, useBlockHoverState } from '~stackable/hooks' import { useEffect } from '@wordpress/element' import { useSelect } from '@wordpress/data' import { registerPlugin } from '@wordpress/plugins' +import { addFilter } from '@wordpress/hooks' const EditorPreviewClass = () => { const deviceType = useDeviceType() @@ -45,6 +46,10 @@ const EditorPreviewClass = () => { if ( document.querySelector( 'body' ).className.match( themeRegex ) && ! editorEl.className.match( themeRegex ) ) { const theme = document.querySelector( 'body' ).className.match( themeRegex )[ 0 ] editorEl.classList.add( theme ) + addFilter( 'stackable.global-styles.classnames', 'stackable/theme-classname', styleIds => { + styleIds.push( theme ) + return styleIds + } ) } // At first load of the editor, the `stk-preview-device-*` and `stk--is-*-theme` are removed, so we have to re-add it. @@ -60,6 +65,10 @@ const EditorPreviewClass = () => { if ( document.querySelector( 'body' ).className.match( themeRegex ) && ! editorEl.className.match( themeRegex ) ) { const theme = document.querySelector( 'body' ).className.match( themeRegex )[ 0 ] editorEl.classList.add( theme ) + addFilter( 'stackable.global-styles.classnames', 'stackable/theme-classname', styleIds => { + styleIds.push( theme ) + return styleIds + } ) } } ) diff --git a/src/plugins/global-settings/color-schemes/editor-loader.js b/src/plugins/global-settings/color-schemes/editor-loader.js index 24489e2927..738f9690e1 100644 --- a/src/plugins/global-settings/color-schemes/editor-loader.js +++ b/src/plugins/global-settings/color-schemes/editor-loader.js @@ -17,6 +17,7 @@ import { useBlockColorSchemes, useBlockHoverState } from '~stackable/hooks' */ import { useEffect, useState } from '@wordpress/element' import { useSelect } from '@wordpress/data' +import { addFilter } from '@wordpress/hooks' const renderGlobalStyles = ( setStyles, colorSchemesArray, @@ -148,6 +149,10 @@ export const GlobalColorSchemeStyles = () => { const mo = onClassChange( editorEl, () => { if ( styles !== '' && editorEl?.classList.contains( 'stk-has-color-schemes' ) === false ) { editorEl?.classList.add( 'stk-has-color-schemes' ) + addFilter( 'stackable.global-styles.classnames', `stackable/global-settings.color-schemes`, classnames => { + classnames.push( 'stk-has-color-schemes' ) + return classnames + } ) } if ( styles === '' ) { editorEl?.classList.remove( 'stk-has-color-schemes' ) diff --git a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js index 6320229663..05317aab8f 100644 --- a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js +++ b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js @@ -8,6 +8,7 @@ import { getDefault, onClassChange } from './block-layout-utils' */ import { useSelect } from '@wordpress/data' import { useEffect, useState } from '@wordpress/element' +import { addFilter } from '@wordpress/hooks' /** * External dependencies @@ -167,6 +168,10 @@ export const useBlockLayoutEditorLoader = ( storeName, classSuffix ) => { const className = `stk-has-design-system-${ classSuffix }` if ( styles !== '' && editorEl.classList.contains( className ) === false ) { editorEl.classList.add( className ) + addFilter( 'stackable.global-styles.classnames', `stackable/global-settings.${ classSuffix }`, classnames => { + classnames.push( className ) + return classnames + } ) } if ( styles === '' ) { editorEl.classList.remove( className ) diff --git a/src/plugins/theme-block-style-inheritance/index.js b/src/plugins/theme-block-style-inheritance/index.js index 9f2eae02e2..53790a4e67 100644 --- a/src/plugins/theme-block-style-inheritance/index.js +++ b/src/plugins/theme-block-style-inheritance/index.js @@ -9,6 +9,7 @@ import { fetchSettings } from '~stackable/util' import { useEffect, useState } from '@wordpress/element' import { useSelect } from '@wordpress/data' import { registerPlugin } from '@wordpress/plugins' +import { addFilter } from '@wordpress/hooks' // Adds a body class for block style inheritance const ThemeBlockStyleInheritanceClass = () => { @@ -19,7 +20,15 @@ const ThemeBlockStyleInheritanceClass = () => { useEffect( () => { fetchSettings().then( response => { - setDisableBlockStyleInheritance( response.stackable_disable_block_style_inheritance ) + const isDisabled = response.stackable_disable_block_style_inheritance + setDisableBlockStyleInheritance( isDisabled ) + + if ( ! isDisabled ) { + addFilter( 'stackable.global-styles.classnames', `stackable/global-settings.block-style-inheritance`, classnames => { + classnames.push( 'stk-has-block-style-inheritance' ) + return classnames + } ) + } } ) }, [] ) From ce8013ea7f66b20013dd153b0a27fc8fd9bfe20c Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 4 Jun 2025 11:24:52 +0800 Subject: [PATCH 105/124] updates - remove "Stackable" from design labels - update tooltip for style options - update checking of image if it's already in media library --- src/components/design-library-list/index.js | 2 +- .../modal-design-library/block-list.js | 2 +- .../modal-design-library/editor.scss | 20 +++++- .../modal-design-library/images/help.svg | 1 + src/components/modal-design-library/modal.js | 32 ++++----- src/design-library/index.js | 2 +- src/design-library/init.php | 66 ++++++++++--------- 7 files changed, 74 insertions(+), 51 deletions(-) create mode 100644 src/components/modal-design-library/images/help.svg diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js index b654ca05af..a49beeb7af 100644 --- a/src/components/design-library-list/index.js +++ b/src/components/design-library-list/index.js @@ -58,7 +58,7 @@ const DesignLibraryList = props => { plan={ design.plan } designId={ design.designId } label={ design.title } - category={ design.categories[ 0 ] } + category={ design.category } containerScheme={ props.containerScheme } backgroundScheme={ props.backgroundScheme } enableBackground={ props.enableBackground } diff --git a/src/components/modal-design-library/block-list.js b/src/components/modal-design-library/block-list.js index 8a05c92e04..09535f6669 100644 --- a/src/components/modal-design-library/block-list.js +++ b/src/components/modal-design-library/block-list.js @@ -28,7 +28,7 @@ const BlockList = props => { useEffect( () => { let total = 0 const _categories = designs.reduce( ( output, design ) => { - const category = design.categories[ 0 ] + const category = design.category if ( plan && plan !== design.plan ) { return output diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index ed99e6bb6b..cd04fc0142 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -66,6 +66,20 @@ background: #fff; border-top: 1px solid #eee; + > div:first-child { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 1em; + h4 { + margin: 0; + } + svg { + fill: #bbb; + cursor: pointer; + } + } + .components-base-control__field { display: flex; justify-content: space-between; @@ -438,4 +452,8 @@ div.ugb-modal-design-library__enable-background { .ugb-design-library__manage-scheme { display: block; text-align: center; -} \ No newline at end of file +} + +.ugb-modal-design-library__style-options-tooltip .stk-tooltip__text a { + display: inline; +} diff --git a/src/components/modal-design-library/images/help.svg b/src/components/modal-design-library/images/help.svg new file mode 100644 index 0000000000..eb736bd38d --- /dev/null +++ b/src/components/modal-design-library/images/help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index fd45f36f56..1fba012c5a 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -1,6 +1,7 @@ /** * Internal deprendencies */ +import HelpSVG from './images/help.svg' import BlockList from './block-list' import Button from '../button' // import AdvancedToolbarControl from '../advanced-toolbar-control' @@ -195,25 +196,28 @@ export const ModalDesignLibrary = props => { />
      -

      { __( 'Style Options', i18n ) }

      +
      +

      { __( 'Style Options', i18n ) }

      + + { __( 'Customize patterns using the options below.', i18n ) } +   + + { __( 'Learn how to use style options.', i18n ) } + + }> + + +
      - { __( 'Learn more about section background here.', i18n ) } - }> - { __( 'Section Background', i18n ) } - } + label={ __( 'Section Background', i18n ) } checked={ enableBackground } onChange={ value => { setEnableBackground( value ) } } /> - { __( 'Learn more about background schemes here.', i18n ) } - }> - { __( 'Background Scheme', i18n ) } - } + label={ __( 'Background Scheme', i18n ) } className="ugb-modal-design-library__color-scheme-label" > { /> - { __( 'Learn more about container schemes here.', i18n ) } - }> - { __( 'Container Scheme', i18n ) } - } + label={ __( 'Container Scheme', i18n ) } className="ugb-modal-design-library__color-scheme-label" > categories[ 0 ] === isCategory ) + library = library.filter( ( { category } ) => category === isCategory ) } return library diff --git a/src/design-library/init.php b/src/design-library/init.php index 402fc7ea74..8657fd8ef5 100644 --- a/src/design-library/init.php +++ b/src/design-library/init.php @@ -143,24 +143,7 @@ public function delete_cache() { public function get_design_library_image( $request ) { $url = $request->get_param( 'image_url' ); - $temp_filepath = download_url( $url ); - - if ( is_wp_error( $temp_filepath ) ) { - return new WP_REST_Response( array( - 'success' => false, - 'message' => 'Failed to retrieve image from the provided URL.' - ), 500 ); - } - - if ( ! file_exists( $temp_filepath ) || ! wp_filesize( $temp_filepath ) ) { - @unlink( $temp_filepath ); - return new WP_REST_Response( array( - 'success' => false, - 'message' => 'Invalid file content retrieved from the provided URL.' - ), 400 ); - } - - $image_hash = hash_file( 'md5', $temp_filepath ); + $basename = sanitize_file_name( wp_basename( parse_url( $url, PHP_URL_PATH ) ) ); $args = array( 'post_type' => 'attachment', @@ -168,9 +151,9 @@ public function get_design_library_image( $request ) { 'posts_per_page' => 1, 'meta_query' => array( array( - 'key' => 'stk_image_hash', - 'value' => $image_hash, - 'compare' => '=' + 'key' => '_wp_attached_file', + 'value' => $basename, + 'compare' => 'LIKE' ) ) ); @@ -183,7 +166,6 @@ public function get_design_library_image( $request ) { $media_url = wp_get_attachment_url( $media_id ); wp_reset_postdata(); - @unlink( $temp_filepath ); return new WP_REST_Response( array( 'success' => true, @@ -192,10 +174,27 @@ public function get_design_library_image( $request ) { ), 200 ); } + $temp_filepath = download_url( $url ); + + if ( is_wp_error( $temp_filepath ) ) { + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'Failed to retrieve image from the provided URL.' + ), 500 ); + } + + if ( ! file_exists( $temp_filepath ) || ! wp_filesize( $temp_filepath ) ) { + @unlink( $temp_filepath ); + return new WP_REST_Response( array( + 'success' => false, + 'message' => 'Invalid file content retrieved from the provided URL.' + ), 400 ); + } + $valid_mimes = [ 'image/jpeg' => 1, 'image/jpg' => 1, 'image/png' => 1, 'image/gif' => 1, 'image/webp' => 1, 'video/mp4' => 1 ]; $file_array = array( - 'name' => sanitize_file_name( wp_basename( parse_url( $url, PHP_URL_PATH ) ) ), + 'name' => $basename, 'type' => mime_content_type( $temp_filepath ), 'tmp_name' => $temp_filepath, 'size' => wp_filesize( $temp_filepath ) @@ -230,7 +229,6 @@ public function get_design_library_image( $request ) { ), 500 ); } - update_post_meta( $media_id, 'stk_image_hash', $image_hash ); $media_url = wp_get_attachment_url( $media_id ); return new WP_REST_Response( array( @@ -297,7 +295,7 @@ public function _get_design_library( $outside_init = false ) { $block_patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered( $outside_init ); foreach ( $block_patterns as $pattern ) { if ( strpos( $pattern[ 'name' ], 'stackable/' ) !== false ) { - $pattern[ 'categories' ][ 0 ] = str_replace( 'stackable/', '', $pattern[ 'categories' ][ 0 ] ); + $pattern[ 'title' ] = str_replace( sprintf( __( 'Stackable ', STACKABLE_I18N ) ), '', $pattern[ 'title' ] ); $content[ $pattern[ 'designId' ] ] = $pattern; } } @@ -369,9 +367,9 @@ public function get_premium_designs( $content ) { } $premium_designs[ $design_id ] = array( - 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), + 'title' => $design[ 'label' ], 'content' => $design[ 'template' ], - 'categories' => array( $design[ 'category' ], 'stackable' ), + 'category' => $design[ 'category' ], 'description' => $design[ 'description' ], 'plan' => $design[ 'plan' ], 'designId' => $design_id @@ -399,9 +397,9 @@ public function get_designs_with_disabled_blocks( $content ) { } $designs_with_disabled[ $design_id ] = array( - 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), + 'title' => $design[ 'label' ], 'content' => $design[ 'template' ], - 'categories' => array( $design[ 'category' ], 'stackable' ), + 'category' => $design[ 'category' ], 'description' => $design[ 'description' ], 'plan' => $design[ 'plan' ], 'designId' => $design_id, @@ -417,6 +415,11 @@ public function get_designs_with_disabled_blocks( $content ) { return $merged; } + public function get_category_kebab_case( $category ) { + $category = trim( strtolower( $category ) ); + return preg_replace( '/[^a-z0-9-]+/', '-', $category ); + } + public function register_design_pattern() { $designs = $this->get_design_library_from_cloud(); @@ -441,7 +444,7 @@ public function register_design_pattern() { if ( $has_disabled ) continue; } - register_block_pattern_category( 'stackable/' . $design[ 'category' ], [ + register_block_pattern_category( 'stackable/' . $this->get_category_kebab_case( $design[ 'category' ] ), [ 'label' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'category' ] ), 'description' => sprintf( __( '%s patterns for Stackable Design Library', STACKABLE_I18N ), $design[ 'category' ] ), ] ); @@ -451,7 +454,8 @@ public function register_design_pattern() { array( 'title' => sprintf( __( 'Stackable %s', STACKABLE_I18N ), $design[ 'label' ] ), 'content' => $design[ 'template' ], - 'categories' => array( 'stackable/' . $design[ 'category' ], 'stackable' ), + 'categories' => array( 'stackable/' . $this->get_category_kebab_case( $design[ 'category' ] ), 'stackable' ), // used in Patterns + 'category' => $design[ 'category' ], // used in Design Library 'description' => $design[ 'description' ], 'plan' => $design[ 'plan' ], 'designId' => $design_id From 9ecdfd830f923d180abd0b90078de4b85bb317fe Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Wed, 4 Jun 2025 22:20:32 +0800 Subject: [PATCH 106/124] add fixes - fix footer size when there are no designs - fix designs not showing up unless a category is selected --- src/components/modal-design-library/editor.scss | 2 +- src/components/modal-design-library/modal.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index cd04fc0142..f1702fa764 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -39,7 +39,7 @@ .ugb-modal-design-library__wrapper { display: grid; grid-template-columns: 300px auto; - grid-template-rows: auto; + grid-template-rows: auto auto 80px; flex: 1; height: 100%; } diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index 1fba012c5a..74a20b95fe 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -53,7 +53,7 @@ export const ModalDesignLibrary = props => { const [ isMultiSelectBusy, setIsMultiSelectBusy ] = useState( false ) - const [ selectedTab, _setSelectedTab ] = useLocalStorage( 'stk__design_library__block-list__view_by', 'patterns' ) + const [ selectedTab, setSelectedTab ] = useLocalStorage( 'stk__design_library__block-list__view_by', 'patterns' ) const [ selectedCategory, setSelectedCategory ] = useLocalStorage( 'stk__design_library__block-list__selected', '' ) const [ selectedPlan, setSelectedPlan ] = useLocalStorage( 'stk__design_library__view-plan', PLAN_OPTIONS[ 0 ] ) @@ -68,6 +68,17 @@ export const ModalDesignLibrary = props => { const openDropdownRef = useRef( null ) + // For version 4, the default tab is now 'patterns' and for category, we use '' instead of 'All'. + // So we need to update the local storage values here. + useEffect( () => { + const version = window.localStorage.getItem( 'stk__design_library__version' ) + if ( ! version ) { + window.localStorage.setItem( 'stk__design_library__version', 'v4' ) + setSelectedTab( 'patterns' ) + setSelectedCategory( '' ) + } + }, [] ) + // Update the designs on the sidebar. (this will trigger the display designs update next) useEffect( () => { if ( doReset ) { From a6b68533d4fa6b674c4046868775d2eb2898790e Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 13:36:46 +0800 Subject: [PATCH 107/124] fix color schemes not being applied --- src/plugins/global-settings/color-schemes/editor-loader.js | 4 ++++ .../global-settings/utils/use-block-layout-editor-loader.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/plugins/global-settings/color-schemes/editor-loader.js b/src/plugins/global-settings/color-schemes/editor-loader.js index 738f9690e1..d324ecc0a3 100644 --- a/src/plugins/global-settings/color-schemes/editor-loader.js +++ b/src/plugins/global-settings/color-schemes/editor-loader.js @@ -140,6 +140,10 @@ export const GlobalColorSchemeStyles = () => { if ( editorEl ) { if ( styles !== '' && editorEl.classList.contains( 'stk-has-color-schemes' ) === false ) { editorEl.classList.add( 'stk-has-color-schemes' ) + addFilter( 'stackable.global-styles.classnames', `stackable/global-settings.color-schemes`, classnames => { + classnames.push( 'stk-has-color-schemes' ) + return classnames + } ) } if ( styles === '' ) { editorEl.classList.remove( 'stk-has-color-schemes' ) diff --git a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js index 05317aab8f..ff07be79ab 100644 --- a/src/plugins/global-settings/utils/use-block-layout-editor-loader.js +++ b/src/plugins/global-settings/utils/use-block-layout-editor-loader.js @@ -180,6 +180,10 @@ export const useBlockLayoutEditorLoader = ( storeName, classSuffix ) => { const mo = onClassChange( editorEl, () => { if ( styles !== '' && editorEl?.classList.contains( className ) === false ) { editorEl?.classList.add( className ) + addFilter( 'stackable.global-styles.classnames', `stackable/global-settings.${ classSuffix }`, classnames => { + classnames.push( className ) + return classnames + } ) } if ( styles === '' ) { editorEl?.classList.remove( className ) From 8f7f600416344251c75899a63842f314e3a148cf Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 14:04:42 +0800 Subject: [PATCH 108/124] add other categories, add background to category --- .../modal-design-library/block-list.js | 29 +++++++++++++++++++ .../modal-design-library/editor.scss | 1 + 2 files changed, 30 insertions(+) diff --git a/src/components/modal-design-library/block-list.js b/src/components/modal-design-library/block-list.js index 09535f6669..027d59d2e1 100644 --- a/src/components/modal-design-library/block-list.js +++ b/src/components/modal-design-library/block-list.js @@ -48,6 +48,35 @@ const BlockList = props => { return output }, {} ) + const toAdd = [ + [ 'Contact', 22 ], + [ 'Featured Products', 14 ], + [ 'Footer', 11 ], + [ 'Header', 12 ], + [ 'Map', 10 ], + [ 'Service Menu', 10 ], + [ 'Stats', 15 ], + [ 'Post Loop', 6 ], + [ 'Pricing Table', 15 ], + [ 'Quote', 10 ], + [ 'Carousel', 13 ], + [ 'Table of Contents', 8 ], + [ 'Tabs', 11 ], + [ 'Team Members', 10 ], + [ 'Testimonials', 25 ], + [ 'Timeline', 9 ], + [ 'Video', 11 ], + ] + + toAdd.forEach( ( [ label, count ] ) => { + _categories[ label ] = { + id: label, + label, + count, + } + total += count + } ) + _categories.all = { id: '', label: ' ', diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index f1702fa764..1dbeb26eb4 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -213,6 +213,7 @@ [aria-pressed="true"] { font-weight: bold; color: #f00069; + background: #f1f1f1; } .is-disabled { opacity: 0.3; From 415d0d6b5b51e6cdda5ae39663bd18170f15a3d2 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 14:25:43 +0800 Subject: [PATCH 109/124] fix spinner when fetching from cloud --- .../design-library-list-item.js | 8 +-- src/components/design-library-list/index.js | 58 +++++++++---------- .../modal-design-library/block-list.js | 5 ++ src/components/modal-design-library/modal.js | 6 +- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index 581483ed7b..bd9549da55 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -24,7 +24,7 @@ import { forwardRef, useEffect, useRef, useState, } from '@wordpress/element' import { useSelect } from '@wordpress/data' -import { Dashicon, Spinner } from '@wordpress/components' +import { Dashicon } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { serialize } from '@wordpress/blocks' import { applyFilters } from '@wordpress/hooks' @@ -41,7 +41,6 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { category = '', plan, selectedNum = false, - isBusy, containerScheme, backgroundScheme, enableBackground, @@ -68,7 +67,6 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { 'ugb-design-library-item', 'ugb-design-library-item--toggle', ], { - 'ugb--is-busy': isBusy, [ `ugb--is-${ plan }` ]: ! isPro && plan !== 'free', 'ugb--is-toggled': selectedNum, } ) @@ -250,7 +248,6 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { onClick( designId, parsedBlocks, blocksForSubstitutionRef.current ) } } > - { isBusy && } { ! isPro && plan !== 'free' && }
      { ! isPro && plan !== 'free' && ( @@ -267,14 +264,13 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { } } > -
      +

      { label }

      diff --git a/src/components/design-library-list/index.js b/src/components/design-library-list/index.js index a49beeb7af..aece541cfb 100644 --- a/src/components/design-library-list/index.js +++ b/src/components/design-library-list/index.js @@ -48,35 +48,35 @@ const DesignLibraryList = props => { setScrollTop( e.currentTarget.scrollTop ) } } > -
      - { ( designs || [] ).map( ( design, i ) => { - const selectedNum = selectedDesigns.indexOf( design.designId ) + 1 - return ( - onSelectMulti( designId, parsedBlocks, hasDisabledBlocks ) } - scrollTop={ scrollTop } - /> - ) - } ) } - - { isBusy &&
      } - - { ! isBusy && ! ( designs || [] ).length && -

      { __( 'No designs found', i18n ) }

      - } -
      + { isBusy && } + { ! isBusy && <> +
      + { ( designs || [] ).map( ( design, i ) => { + const selectedNum = selectedDesigns.indexOf( design.designId ) + 1 + return ( + onSelectMulti( designId, parsedBlocks, hasDisabledBlocks ) } + scrollTop={ scrollTop } + /> + ) + } ) } + + { ! ( designs || [] ).length && +

      { __( 'No designs found', i18n ) }

      + } +
      + }
      } diff --git a/src/components/modal-design-library/block-list.js b/src/components/modal-design-library/block-list.js index 027d59d2e1..97a7f42114 100644 --- a/src/components/modal-design-library/block-list.js +++ b/src/components/modal-design-library/block-list.js @@ -18,6 +18,7 @@ import classnames from 'classnames' */ import { useEffect, useState } from '@wordpress/element' import { __ } from '@wordpress/i18n' +import { Spinner } from '@wordpress/components' const BlockList = props => { const [ categoryList, setCategoryList ] = useState( [] ) @@ -89,6 +90,10 @@ const BlockList = props => { setCategoryList( sortedCategories ) }, [ designs, viewBy, plan ] ) + if ( props.isBusy ) { + return + } + return (
        { categoryList.reduce( ( list, itemData ) => { diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index 74a20b95fe..e2c70d3cd4 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -81,6 +81,7 @@ export const ModalDesignLibrary = props => { // Update the designs on the sidebar. (this will trigger the display designs update next) useEffect( () => { + setIsBusy( true ) if ( doReset ) { setSidebarDesigns( [] ) // setDisplayDesigns( [] ) @@ -92,20 +93,18 @@ export const ModalDesignLibrary = props => { setSidebarDesigns( designs ) } ).finally( () => { setDoReset( false ) + setIsBusy( false ) } ) }, [ doReset, selectedTab ] ) // This updates the displayed designs the user can pick. useEffect( () => { - setIsBusy( true ) filterDesigns( { library: sidebarDesigns, category: selectedCategory, plan: selectedPlan.key, } ).then( designs => { setDisplayDesigns( designs ) - } ).finally( () => { - setIsBusy( false ) } ) }, [ sidebarDesigns, selectedPlan, selectedCategory ] ) @@ -204,6 +203,7 @@ export const ModalDesignLibrary = props => { plan={ selectedPlan.key } selected={ selectedCategory } onSelect={ id => setSelectedCategory( id ) } + isBusy={ isBusy } />
      From 7806c7a76f3ad64a9a74c9acbe9c6f206fd7ab34 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 14:55:30 +0800 Subject: [PATCH 110/124] add confirm dialog when going to color schemes --- src/components/color-schemes-help/index.js | 7 ++++++- src/components/modal-design-library/modal.js | 20 ++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/components/color-schemes-help/index.js b/src/components/color-schemes-help/index.js index 9fe715cd0c..8401d9fb9f 100644 --- a/src/components/color-schemes-help/index.js +++ b/src/components/color-schemes-help/index.js @@ -9,8 +9,13 @@ export const ColorSchemesHelp = props => { customText, callback, className, } = props const onClick = () => { + let cancelOnClick = false if ( callback ) { - callback() + cancelOnClick = callback() + } + + if ( cancelOnClick ) { + return } // Open the global settings sidebar. diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index e2c70d3cd4..44acf843c7 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -108,6 +108,18 @@ export const ModalDesignLibrary = props => { } ) }, [ sidebarDesigns, selectedPlan, selectedCategory ] ) + const colorSchemeHelpCallback = () => { + if ( selectedDesignIds.length ) { + // eslint-disable-next-line no-alert + const confirmClose = window.confirm( sprintf( __( 'You have one or more designs selected. Navigating to %s will close the Design Library and your current selection will be lost. Do you want to continue?', i18n ), __( 'Color Schemes', i18n ) ) ) + if ( ! confirmClose ) { + return true + } + } + props.onClose() + return false + } + return ( { } ) } { Object.keys( colorSchemesCollection ).length - ? - : + ? + : }
      @@ -349,8 +361,8 @@ export const ModalDesignLibrary = props => { } ) } { Object.keys( colorSchemesCollection ).length - ? - : + ? + : } From c51f1d40a7928fabb291e41b402aecd61b07b8d7 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 16:07:04 +0800 Subject: [PATCH 111/124] add plain button compatibility for tove --- src/compatibility/tove/style.scss | 4 ++++ src/init.php | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 src/compatibility/tove/style.scss diff --git a/src/compatibility/tove/style.scss b/src/compatibility/tove/style.scss new file mode 100644 index 0000000000..d625512032 --- /dev/null +++ b/src/compatibility/tove/style.scss @@ -0,0 +1,4 @@ +:where(.stk-has-block-style-inheritance.stk--is-tove-theme) :is(.stk-block-button, .stk-block-icon-button, .stk-block-pagination):is(.is-style-plain) .stk-button { + border-width: 0; + box-shadow: none; +} diff --git a/src/init.php b/src/init.php index 9870f27f01..a01bb0850a 100644 --- a/src/init.php +++ b/src/init.php @@ -467,6 +467,8 @@ public function add_body_class_theme_compatibility( $classes ) { $classes[] = 'stk--is-twentytwentyfive-theme'; } else if ( function_exists( 'hello_elementor_setup' ) ) { // Taken from https://github.com/elementor/hello-theme/blob/master/functions.php $classes[] = 'stk--is-helloelementor-theme'; + } else if ( function_exists( 'tove_setup' ) ) { + $classes[] = 'stk--is-tove-theme'; } return $convert_to_string ? implode( ' ', $classes ) : $classes; From e14304dbb303da5428a73eda7c52fb8cd624e7e8 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 22:35:18 +0800 Subject: [PATCH 112/124] add default placeholders for testimonials --- .../design-library-list/default.json | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/components/design-library-list/default.json b/src/components/design-library-list/default.json index 0113adb778..108845ffe8 100644 --- a/src/components/design-library-list/default.json +++ b/src/components/design-library-list/default.json @@ -182,5 +182,28 @@ "text-5_placeholder": "Designing urban environments with green spaces and efficient infrastructure.", "title-6_placeholder": "Environmental Education", "text-6_placeholder": "Empowering communities with knowledge to adopt sustainable lifestyles." + }, + "Testimonials": { + "heading_placeholder": "What Our Clients Say", + "description_placeholder": "Here's a look at what people are saying about working with us. We're proud to have helped so many achieve their goals.", + "kicker_placeholder": "Testimonials", + "name-1_placeholder": "Emily Johnson", + "position-1_placeholder": "Marketing Manager", + "text-1_placeholder": "XYZ Company has been a game-changer. Our online engagement and conversions significantly increased, leading to a 30% rise in leads in three months!", + "name-2_placeholder": "David Smith", + "position-2_placeholder": "Small Business Owner", + "text-2_placeholder": "Struggling to grow online, XYZ Company provided the perfect solution. Their tailored approach made a huge difference; my sales have doubled.", + "name-3_placeholder": "Sophia Miller", + "position-3_placeholder": "E-commerce Specialist", + "text-3_placeholder": "XYZ Company transformed our platform. Their expertise boosted traffic and improved satisfaction, consistently giving us higher average order values.", + "name-4_placeholder": "William Brown", + "position-4_placeholder": "Tech Startup Founder", + "text-4_placeholder": "XYZ Company delivered exceptional value. They helped us develop a strong brand and launch our product effectively. We've gained valuable traction.", + "name-5_placeholder": "James Wilson", + "position-5_placeholder": "Non-profit Director", + "text-5_placeholder": "XYZ Company exceeded our expectations. Their dedication significantly increased our donations and volunteer sign-ups by improving our online presence.", + "name-6_placeholder": "Olivia Davis", + "position-6_placeholder": "Real Estate Broker", + "text-6_placeholder": "XYZ Company gave me the edge. Their innovative digital advertising brought consistent, qualified leads, directly leading to more property sales." } } \ No newline at end of file From d9a58f680ae8a77d992940bfb23f4f624346d21b Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Thu, 5 Jun 2025 22:35:55 +0800 Subject: [PATCH 113/124] update blocklist --- src/components/modal-design-library/block-list.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/modal-design-library/block-list.js b/src/components/modal-design-library/block-list.js index 97a7f42114..470638ea53 100644 --- a/src/components/modal-design-library/block-list.js +++ b/src/components/modal-design-library/block-list.js @@ -64,7 +64,6 @@ const BlockList = props => { [ 'Table of Contents', 8 ], [ 'Tabs', 11 ], [ 'Team Members', 10 ], - [ 'Testimonials', 25 ], [ 'Timeline', 9 ], [ 'Video', 11 ], ] From 8d8414b36bf4d0754f806db99bdd0de145c39846 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Tue, 10 Jun 2025 11:17:26 +0800 Subject: [PATCH 114/124] fix issues - modify color schemes dropdown - fix paddings disappearing when global settings have value --- .../design-library-list-item.js | 17 +++- .../modal-design-library/editor.scss | 31 ++++++-- src/components/modal-design-library/modal.js | 78 ++++++++----------- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index bd9549da55..731a076619 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -23,7 +23,7 @@ import { Tooltip } from '~stackable/components' import { forwardRef, useEffect, useRef, useState, } from '@wordpress/element' -import { useSelect } from '@wordpress/data' +import { select, useSelect } from '@wordpress/data' import { Dashicon } from '@wordpress/components' import { __ } from '@wordpress/i18n' import { serialize } from '@wordpress/blocks' @@ -187,9 +187,18 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { const hostStyles = document.createElement( 'style' ) hostStyles.setAttribute( 'id', 'stk-design-library-styles' ) - hostStyles.innerHTML = ! hasBackgroundTargetRef.current - ? `body > .stk-block-columns { padding: 75px; } body > .stk-block-background:not(.stk--no-padding) { padding: calc(75px + var(--stk-block-background-padding)); }` - : `[stk-design-library__bg-target="true"] { padding: 25px; } [stk-design-library__bg-target="true"].stk-block-background:not(.stk--no-padding) { padding: calc(25px + var(--stk-block-background-padding)); }` + + const blockLayouts = select( 'stackable/global-spacing-and-borders' ).getBlockLayouts() + hostStyles.innerHTML = ! hasBackgroundTargetRef.current ? 'body > .stk-block-columns { padding: 75px; }' : '[stk-design-library__bg-target="true"] { padding: 50px; }' + + if ( ( Array.isArray( blockLayouts ) && ! blockLayouts.length ) || + ( typeof blockLayouts === 'object' && ! blockLayouts[ 'block-background-padding' ] ) + ) { + hostStyles.innerHTML += ! hasBackgroundTargetRef.current + ? ` body > .stk-block-background:not(.stk--no-padding) { padding: calc(75px + var(--stk-block-background-padding)); }` + : ` [stk-design-library__bg-target="true"].stk-block-background:not(.stk--no-padding) { padding: calc(26px + var(--stk-block-background-padding)); }` + } + hostStyles.innerHTML += `.stk-block-count-up__text:not(.stk--count-up-active) { opacity: 1; }` styleNodes.push( hostStyles ) diff --git a/src/components/modal-design-library/editor.scss b/src/components/modal-design-library/editor.scss index 1dbeb26eb4..deaef841a2 100644 --- a/src/components/modal-design-library/editor.scss +++ b/src/components/modal-design-library/editor.scss @@ -408,17 +408,14 @@ div.ugb-modal-design-library__enable-background { position: absolute; right: 60px; } -.stk-design-library__plan-dropdown, .ugb-modal-design-library__stk-color-scheme-list { +.stk-design-library__plan-dropdown, .ugb-design-library__color-scheme-popover { --wp-admin-theme-color: #f00069; --wp-admin-theme-color-darker-10: #{ darken(#f00069, 10%) }; --wp-admin-theme-color-darker-20: #{ darken(#f00069, 20%) }; } -.ugb-modal-design-library__stk-color-scheme-list-wrapper { - max-height: 145px; -} .ugb-modal-design-library__stk-color-scheme-list { - padding: 0 0 8px; + padding: 8px; .stk-color-scheme__none { padding: 8px; min-height: 30px; @@ -458,3 +455,27 @@ div.ugb-modal-design-library__enable-background { .ugb-modal-design-library__style-options-tooltip .stk-tooltip__text a { display: inline; } + +.ugb-design-library__color-scheme-popover .components-popover__content { + padding: 0; +} + +.ugb-modal-design-library__stk-color-scheme-list-header { + display: flex; + justify-content: space-between; + padding: 0; + margin: 0 0 4px; + border-bottom: 1px solid #e0e0e0; + + p { + margin: 6px 6px 4px; + font-size: 11px; + font-weight: 500; + } + + .components-button.has-icon { + padding: 0; + height: unset; + min-width: unset; + } +} diff --git a/src/components/modal-design-library/modal.js b/src/components/modal-design-library/modal.js index 44acf843c7..b78799774c 100644 --- a/src/components/modal-design-library/modal.js +++ b/src/components/modal-design-library/modal.js @@ -26,9 +26,7 @@ import { Spinner, ToggleControl, } from '@wordpress/components' -import { - useEffect, useState, useRef, -} from '@wordpress/element' +import { useEffect, useState } from '@wordpress/element' import { sprintf, __ } from '@wordpress/i18n' import { useBlockColorSchemes } from '~stackable/hooks' import ColorSchemePreview from '../color-scheme-preview' @@ -37,6 +35,7 @@ import Tooltip from '../tooltip' const PLAN_OPTIONS = [ { key: '', label: __( 'All', i18n ) }, { key: 'free', label: __( 'Free', i18n ) }, { key: 'premium', label: __( 'Premium', i18n ) } ] const popoverProps = { + className: 'ugb-design-library__color-scheme-popover', placement: 'right-start', shift: true, } @@ -66,8 +65,6 @@ export const ModalDesignLibrary = props => { const [ selectedContainerScheme, setSelectedContainerScheme ] = useState( '' ) const [ selectedBackgroundScheme, setSelectedBackgroundScheme ] = useState( '' ) - const openDropdownRef = useRef( null ) - // For version 4, the default tab is now 'patterns' and for category, we use '' instead of 'All'. // So we need to update the local storage values here. useEffect( () => { @@ -162,17 +159,10 @@ export const ModalDesignLibrary = props => { onClick={ () => setDoReset( true ) } /> ( + focusOnMount="container" + renderToggle={ ( { onToggle } ) => ( ) } renderContent={ ( { onClose } ) => ( -
      +
      +
      +

      { __( 'Background Scheme', i18n ) }

      +
      ) } renderContent={ ( { onClose } ) => ( -
      +
      +
      +

      { __( 'Container Scheme', i18n ) }

      +
      ) } - /> + /> }
      ) } diff --git a/src/design-library/init.php b/src/design-library/init.php index 8657fd8ef5..b4385717ef 100644 --- a/src/design-library/init.php +++ b/src/design-library/init.php @@ -424,6 +424,12 @@ public function register_design_pattern() { $designs = $this->get_design_library_from_cloud(); $library = $designs[ self::API_VERSION ]; + + if ( ! $library ) { + return; + } + + $disabled_blocks = $this->get_disabled_blocks(); From f80bd6f47ba9cce075a1e2e2386e5f618a5e5204 Mon Sep 17 00:00:00 2001 From: Mikhaela Tapia Date: Fri, 20 Jun 2025 13:13:00 +0800 Subject: [PATCH 117/124] add fixes - grayed toggle for premium - fix pro note --- .../design-library-list-item.js | 7 +++- .../design-library-list/editor.scss | 39 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/components/design-library-list/design-library-list-item.js b/src/components/design-library-list/design-library-list-item.js index da45a4c1b2..3a3482dc8b 100644 --- a/src/components/design-library-list/design-library-list-item.js +++ b/src/components/design-library-list/design-library-list-item.js @@ -240,6 +240,11 @@ const DesignLibraryListItem = forwardRef( ( props, ref ) => { } }, [ selectedNum ] ) + const getDesignPreviewSize = () => { + return selectedNum && selectedData ? selectedData.selectedPreviewSize.preview + : ( enableBackground ? previewSize.heightBackground : previewSize.heightNoBackground ) + } + return (