From 7cae2eb70d39393006bbabc3433d38308eef1151 Mon Sep 17 00:00:00 2001 From: Alquen Sarmiento Date: Tue, 20 Jan 2026 11:08:07 +0800 Subject: [PATCH] feat: add block defaults for heading theme margin and icon for icon list block --- src/block/heading/edit.js | 23 +++++++- src/block/icon-list/edit.js | 15 +++++- src/block/icon-list/schema.js | 4 +- src/components/admin-icon-setting/editor.scss | 3 ++ src/components/admin-icon-setting/index.js | 32 +++++++++++ src/components/icon-control/index.js | 3 +- src/editor-settings.php | 53 +++++++++++++++++++ src/welcome/admin.js | 43 +++++++++++++++ 8 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 src/components/admin-icon-setting/editor.scss create mode 100644 src/components/admin-icon-setting/index.js diff --git a/src/block/heading/edit.js b/src/block/heading/edit.js index a1de0367ac..db9b60d218 100644 --- a/src/block/heading/edit.js +++ b/src/block/heading/edit.js @@ -22,7 +22,11 @@ import { Transform, useUniqueId, } from '~stackable/block-components' -import { version as VERSION, i18n } from 'stackable' +import { + version as VERSION, + i18n, + settings, +} from 'stackable' import classnames from 'classnames' import { kebabCase } from 'lodash' import { @@ -75,13 +79,28 @@ const Edit = props => { attributes, } = props - const { parentBlock } = useSelect( select => { + const { parentBlock, postType } = useSelect( select => { const { getBlockRootClientId, getBlock } = select( 'core/block-editor' ) const parentClientId = getBlockRootClientId( props.clientId ) return { parentBlock: getBlock( parentClientId ), + postType: select( 'core/editor' ).getCurrentPostType(), } }, [ props.clientId ] ) + + // Set useThemeTextMargins default value from setting on first load + useEffect( () => { + if ( attributes.useThemeTextMargins === undefined || attributes.useThemeTextMargins === '' ) { + const isPost = postType === 'post' + + const defaultThemeMargins = isPost + ? !! settings.stackable_enable_heading_default_theme_margins_posts + : !! settings.stackable_enable_heading_default_theme_margins_non_posts + + setAttributes( { useThemeTextMargins: defaultThemeMargins } ) + } + }, [] ) + const textClasses = getTypographyClasses( props.attributes ) const blockAlignmentClass = getAlignmentClasses( props.attributes ) const blockClassNames = classnames( [ diff --git a/src/block/icon-list/edit.js b/src/block/icon-list/edit.js index 661f14a272..e45d2de220 100644 --- a/src/block/icon-list/edit.js +++ b/src/block/icon-list/edit.js @@ -7,7 +7,11 @@ import blockStyles from './style' * External dependencies */ import classnames from 'classnames' -import { i18n, version as VERSION } from 'stackable' +import { + i18n, + version as VERSION, + settings, +} from 'stackable' import { InspectorTabs, InspectorStyleControls, @@ -50,7 +54,7 @@ import { compose } from '@wordpress/compose' import { useInnerBlocksProps } from '@wordpress/block-editor' import { dispatch, useSelect } from '@wordpress/data' import { addFilter } from '@wordpress/hooks' -import { memo } from '@wordpress/element' +import { memo, useEffect } from '@wordpress/element' import { useBlockLayoutDefaults } from '~stackable/hooks' import { ToggleControl } from '@wordpress/components' @@ -201,6 +205,13 @@ const Edit = props => { version: VERSION, } ) + // Set icon default value from setting on first load + useEffect( () => { + if ( attributes.icon === undefined || attributes.icon === '' ) { + setAttributes( { icon: settings.stackable_icon_list_block_default_icon || DEFAULT_SVG } ) + } + }, [] ) + return ( <> { + return ( + + { + props.onChange( icon ) + } } + allowReset={ false } + hasPanelModifiedIndicator={ true } + /> + { props.children } + + ) +} + +AdminIconSetting.defaultProps = { + value: '', + onChange: () => {}, +} + +export default AdminIconSetting diff --git a/src/components/icon-control/index.js b/src/components/icon-control/index.js index 617f5ee98f..94b6cffe2b 100644 --- a/src/components/icon-control/index.js +++ b/src/components/icon-control/index.js @@ -29,7 +29,7 @@ const IconControl = props => { {}, defaultValue: '', + allowReset: true, hasPanelModifiedIndicator: true, } diff --git a/src/editor-settings.php b/src/editor-settings.php index d37c8d3807..f1c7ec97f0 100644 --- a/src/editor-settings.php +++ b/src/editor-settings.php @@ -241,12 +241,62 @@ public function register_settings() { 'default' => false, ) ); + + register_setting( + 'stackable_editor_settings', + 'stackable_enable_heading_default_theme_margins_posts', + array( + 'type' => 'boolean', + 'description' => __( "When enabled, newly added Stackable Heading blocks in Posts will use the theme's default margins automatically.", STACKABLE_I18N ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => false, + ) + ); + + register_setting( + 'stackable_editor_settings', + 'stackable_enable_heading_default_theme_margins_non_posts', + array( + 'type' => 'boolean', + 'description' => __( "When enabled, newly added Stackable Heading blocks in non-Post content (Pages and custom post types) will use the theme's default margins automatically.", STACKABLE_I18N ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => false, + ) + ); + + register_setting( + 'stackable_editor_settings', + 'stackable_icon_list_block_default_icon', + array( + 'type' => 'string', + 'description' => __( 'Choose the default icon that will be used when adding a new Icon List block.', STACKABLE_I18N ), + 'sanitize_callback' => array( $this, 'sanitize_svg_setting' ), + 'show_in_rest' => true, + 'default' => '', + ) + ); } public function sanitize_array_setting( $input ) { return ! is_array( $input ) ? array( array() ) : $input; } + public function sanitize_svg_setting( $input ) { + if ( empty( $input ) ) { + return ''; + } + + // Remove scripts, event handlers, foreignObject, iframe, embeds + $input = preg_replace( '/<\s*(script|iframe|embed|object|foreignObject)[^>]*>.*?<\s*\/\s*\1\s*>/is', '', $input ); + $input = preg_replace( '/on\w+\s*=\s*"[^"]*"/i', '', $input ); + $input = preg_replace( "/on\w+\s*=\s*'[^']*'/i", '', $input ); + $input = preg_replace( '/javascript:/i', '', $input ); + + return $input; + } + /** * Make our settings available in the editor. * @@ -267,6 +317,9 @@ public function add_settings( $settings ) { $settings['stackable_enable_reset_layout'] = get_option( 'stackable_enable_reset_layout' ); $settings['stackable_enable_save_as_default_block'] = get_option( 'stackable_enable_save_as_default_block' ); $settings['stackable_enable_text_default_block'] = get_option( 'stackable_enable_text_default_block' ); + $settings['stackable_enable_heading_default_theme_margins_posts'] = get_option( 'stackable_enable_heading_default_theme_margins_posts' ); + $settings['stackable_enable_heading_default_theme_margins_non_posts'] = get_option( 'stackable_enable_heading_default_theme_margins_non_posts' ); + $settings['stackable_icon_list_block_default_icon'] = get_option( 'stackable_icon_list_block_default_icon' ); return $settings; } diff --git a/src/welcome/admin.js b/src/welcome/admin.js index 2091a76a84..bfcb034c59 100644 --- a/src/welcome/admin.js +++ b/src/welcome/admin.js @@ -38,6 +38,7 @@ import AdminSelectSetting from '~stackable/components/admin-select-setting' import AdminToggleSetting from '~stackable/components/admin-toggle-setting' import AdminTextSetting from '~stackable/components/admin-text-setting' import AdminToolbarSetting from '~stackable/components/admin-toolbar-setting' +import AdminIconSetting from '~stackable/components/admin-icon-setting' import { GettingStarted } from './getting-started' import { BLOCK_STATE } from '~stackable/util/blocks' import { BlockToggler, OptimizationSettings } from '~stackable/deprecated/v2/welcome/admin' @@ -81,6 +82,14 @@ const SEARCH_TREE = [ __( 'Nested Wide Block Width', i18n ), ], }, + { + id: 'block-defaults', + children: [ + __( 'Default to Theme Margins for Headings (Posts)', i18n ), + __( 'Default to Theme Margins for Headings (Non-Posts)', i18n ), + __( 'Default Icon for Icon List Block', i18n ), + ], + }, { id: 'editor', children: [ @@ -631,6 +640,7 @@ const EditorSettings = props => { const groups = filteredSearchTree.find( tab => tab.id === 'editor-settings' ).groups const blocks = groups.find( group => group.id === 'blocks' ) + const blockDefaults = groups.find( group => group.id === 'block-defaults' ) const editor = groups.find( group => group.id === 'editor' ) const toolbar = groups.find( group => group.id === 'toolbar' ) const inspector = groups.find( group => group.id === 'inspector' ) @@ -668,6 +678,39 @@ const EditorSettings = props => { /> } + { ( blockDefaults.children === null || blockDefaults.children.length > 0 ) && +
+

{ __( 'Block Defaults', i18n ) }

+

{ __( 'Adjust the default behavior of some Stackable blocks.', i18n ) }

+ { + handleSettingsChange( { stackable_enable_heading_default_theme_margins_posts: value } ) // eslint-disable-line camelcase + } } + help={ __( "When enabled, newly added Stackable Heading blocks in Posts will use the theme's default margins automatically. Existing blocks are not affected.", i18n ) } + /> + { + handleSettingsChange( { stackable_enable_heading_default_theme_margins_non_posts: value } ) // eslint-disable-line camelcase + } } + help={ __( "When enabled, newly added Stackable Heading blocks in non-Post content (Pages and custom post types) will use the theme's default margins automatically. Existing blocks are not affected.", i18n ) } + /> + { + handleSettingsChange( { stackable_icon_list_block_default_icon: value } ) // eslint-disable-line camelcase + } } + help={ __( 'Choose the default icon that will be used when adding a new Icon List block.', i18n ) } + /> +
+ } { ( editor.children === null || editor.children.length > 0 ) &&

{ __( 'Editor', i18n ) }