/
githubmirror
/
gutenberg
Обзор
Документация
Войти
/
githubmirror
/
gutenberg
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
trunk
packages/block-library/src/icon/edit.js
262 строки
7 KB
Jorge Costa
Icon: Fix margin being applied twice in the editor (#81292)
12 авг 2026, 12:18
Не верифицирован
12 авг 2026, 12:18
3c78efc
Код
Авторство
О чём код?
import clsx from 'clsx'; import { __ } from '@wordpress/i18n'; import { DropdownMenu, TextControl, ToolbarButton, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { flipHorizontal as flipHorizontalIcon, flipVertical as flipVerticalIcon, rotateRight, } from '@wordpress/icons'; import { BlockControls, InspectorControls, useBlockProps, useBlockEditingMode, __experimentalUseColorProps as useColorProps, __experimentalUseBorderProps as useBorderProps, __experimentalGetSpacingClassesAndStyles as useSpacingProps, getDimensionsClassesAndStyles as useDimensionsProps, } from '@wordpress/block-editor'; import { useState } from '@wordpress/element'; import { SVG, Rect, Path } from '@wordpress/primitives'; import { useSelect } from '@wordpress/data'; import { store as coreDataStore } from '@wordpress/core-data'; import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; import HtmlRenderer from '../utils/html-renderer'; import { CustomInserterModal } from './components'; const IconPlaceholder = ( { className, style } ) => ( <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" preserveAspectRatio="none" fill="none" aria-hidden="true" className={ clsx( 'wp-block-icon__placeholder', className ) } style={ style } > <Rect width="60" height="60" fill="currentColor" fillOpacity={ 0.1 } /> <Path vectorEffect="non-scaling-stroke" stroke="currentColor" strokeOpacity={ 0.25 } d="M60 60 0 0" /> </SVG> ); export function Edit( { attributes, setAttributes } ) { const { icon, ariaLabel, flipHorizontal, flipVertical, rotation } = attributes; const [ isInserterOpen, setInserterOpen ] = useState( false ); const isContentOnlyMode = useBlockEditingMode() === 'contentOnly'; const colorProps = useColorProps( attributes ); // Only padding is applied to the inner SVG element, matching the front // end. The margin support is serialized to the block wrapper instead. const spacingProps = useSpacingProps( { style: { spacing: { padding: attributes.style?.spacing?.padding, }, }, } ); const borderProps = useBorderProps( attributes ); const dimensionsProps = useDimensionsProps( attributes ); const selectedIcon = useSelect( ( select ) => { const { getEntityRecord } = select( coreDataStore ); return icon ? getEntityRecord( 'root', 'icon', icon ) : null; }, [ icon ] ); const iconToDisplay = selectedIcon?.content || ''; const flipClasses = { 'is-flip-horizontal': flipHorizontal, 'is-flip-vertical': flipVertical, }; const rotationStyle = rotation ? { rotate: `${ rotation }deg` } : {}; const blockControls = ( <> { icon && ( <BlockControls group="block"> <ToolbarButton icon={ flipHorizontalIcon } label={ __( 'Flip horizontal' ) } isPressed={ flipHorizontal } onClick={ () => setAttributes( { flipHorizontal: ! flipHorizontal, } ) } /> <ToolbarButton icon={ flipVerticalIcon } label={ __( 'Flip vertical' ) } isPressed={ flipVertical } onClick={ () => setAttributes( { flipVertical: ! flipVertical, } ) } /> <ToolbarButton icon={ rotateRight } label={ __( 'Rotate' ) } onClick={ () => setAttributes( { rotation: ( ( rotation || 0 ) + 90 ) % 360, } ) } /> </BlockControls> ) } <BlockControls group="other"> <ToolbarButton onClick={ () => { setInserterOpen( true ); } } > { icon ? __( 'Replace' ) : __( 'Choose icon' ) } </ToolbarButton> { isContentOnlyMode && icon && ( // Add some extra controls for content attributes when content only mode is active. // With content only mode active, the inspector is hidden, so users need another way // to edit these attributes. <DropdownMenu icon="" toggleProps={ { as: ToolbarButton, } } popoverProps={ { className: 'is-alternate', } } text={ __( 'Label' ) } > { () => ( <TextControl className="wp-block-icon__toolbar-content" label={ __( 'Label' ) } value={ ariaLabel || '' } onChange={ ( value ) => setAttributes( { ariaLabel: value, } ) } help={ __( 'Briefly describe the icon to help screen reader users. Leave blank for decorative icons.' ) } /> ) } </DropdownMenu> ) } </BlockControls> </> ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const inspectorControls = icon && ( <> <InspectorControls group="settings"> <ToolsPanel label={ __( 'Settings' ) } resetAll={ () => setAttributes( { ariaLabel: undefined, } ) } dropdownMenuProps={ dropdownMenuProps } > <ToolsPanelItem label={ __( 'Label' ) } isShownByDefault hasValue={ () => !! ariaLabel } onDeselect={ () => setAttributes( { ariaLabel: undefined } ) } > <TextControl label={ __( 'Label' ) } help={ __( 'Briefly describe the icon to help screen reader users. Leave blank for decorative icons.' ) } value={ ariaLabel || '' } onChange={ ( value ) => setAttributes( { ariaLabel: value } ) } /> </ToolsPanelItem> </ToolsPanel> </InspectorControls> </> ); return ( <> { blockControls } { inspectorControls } <div { ...useBlockProps() }> { iconToDisplay ? ( <HtmlRenderer html={ iconToDisplay } wrapperProps={ { className: clsx( colorProps.className, borderProps.className, spacingProps.className, dimensionsProps.className, flipClasses ), style: { ...colorProps.style, ...borderProps.style, ...spacingProps.style, ...dimensionsProps.style, ...rotationStyle, }, } } /> ) : ( <IconPlaceholder className={ clsx( borderProps.className, spacingProps.className, dimensionsProps.className, flipClasses ) } style={ { ...borderProps.style, ...spacingProps.style, ...dimensionsProps.style, ...rotationStyle, height: 'auto', } } /> ) } </div> { isInserterOpen && ( <CustomInserterModal onClose={ () => setInserterOpen( false ) } value={ attributes.icon } onChange={ ( name ) => { setAttributes( { icon: name } ); setInserterOpen( false ); } } /> ) } </> ); } export default Edit;