UNPKG

@wordpress/block-library

Version:
273 lines (252 loc) 6.69 kB
/** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { useCallback, useEffect, useState, useRef } from '@wordpress/element'; import { Button, ButtonGroup, PanelBody, TextControl, ToolbarButton, Popover, } from '@wordpress/components'; import { BlockControls, InspectorControls, RichText, useBlockProps, __experimentalUseBorderProps as useBorderProps, __experimentalUseColorProps as useColorProps, __experimentalGetSpacingClassesAndStyles as useSpacingProps, __experimentalLinkControl as LinkControl, __experimentalGetElementClassName, } from '@wordpress/block-editor'; import { displayShortcut, isKeyboardEvent } from '@wordpress/keycodes'; import { link, linkOff } from '@wordpress/icons'; import { createBlock } from '@wordpress/blocks'; import { useMergeRefs } from '@wordpress/compose'; const NEW_TAB_REL = 'noreferrer noopener'; function WidthPanel( { selectedWidth, setAttributes } ) { function handleChange( newWidth ) { // Check if we are toggling the width off const width = selectedWidth === newWidth ? undefined : newWidth; // Update attributes. setAttributes( { width } ); } return ( <PanelBody title={ __( 'Width settings' ) }> <ButtonGroup aria-label={ __( 'Button width' ) }> { [ 25, 50, 75, 100 ].map( ( widthValue ) => { return ( <Button key={ widthValue } isSmall variant={ widthValue === selectedWidth ? 'primary' : undefined } onClick={ () => handleChange( widthValue ) } > { widthValue }% </Button> ); } ) } </ButtonGroup> </PanelBody> ); } function ButtonEdit( props ) { const { attributes, setAttributes, className, isSelected, onReplace, mergeBlocks, } = props; const { linkTarget, placeholder, rel, style, text, url, width } = attributes; const onSetLinkRel = useCallback( ( value ) => { setAttributes( { rel: value } ); }, [ setAttributes ] ); function onToggleOpenInNewTab( value ) { const newLinkTarget = value ? '_blank' : undefined; let updatedRel = rel; if ( newLinkTarget && ! rel ) { updatedRel = NEW_TAB_REL; } else if ( ! newLinkTarget && rel === NEW_TAB_REL ) { updatedRel = undefined; } setAttributes( { linkTarget: newLinkTarget, rel: updatedRel, } ); } function setButtonText( newText ) { // Remove anchor tags from button text content. setAttributes( { text: newText.replace( /<\/?a[^>]*>/g, '' ) } ); } function onKeyDown( event ) { if ( isKeyboardEvent.primary( event, 'k' ) ) { startEditing( event ); } else if ( isKeyboardEvent.primaryShift( event, 'k' ) ) { unlink(); richTextRef.current?.focus(); } } // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. const [ popoverAnchor, setPopoverAnchor ] = useState( null ); const borderProps = useBorderProps( attributes ); const colorProps = useColorProps( attributes ); const spacingProps = useSpacingProps( attributes ); const ref = useRef(); const richTextRef = useRef(); const blockProps = useBlockProps( { ref: useMergeRefs( [ setPopoverAnchor, ref ] ), onKeyDown, } ); const [ isEditingURL, setIsEditingURL ] = useState( false ); const isURLSet = !! url; const opensInNewTab = linkTarget === '_blank'; function startEditing( event ) { event.preventDefault(); setIsEditingURL( true ); } function unlink() { setAttributes( { url: undefined, linkTarget: undefined, rel: undefined, } ); setIsEditingURL( false ); } useEffect( () => { if ( ! isSelected ) { setIsEditingURL( false ); } }, [ isSelected ] ); return ( <> <div { ...blockProps } className={ classnames( blockProps.className, { [ `has-custom-width wp-block-button__width-${ width }` ]: width, [ `has-custom-font-size` ]: blockProps.style.fontSize, } ) } > <RichText ref={ richTextRef } aria-label={ __( 'Button text' ) } placeholder={ placeholder || __( 'Add text…' ) } value={ text } onChange={ ( value ) => setButtonText( value ) } withoutInteractiveFormatting className={ classnames( className, 'wp-block-button__link', colorProps.className, borderProps.className, { // For backwards compatibility add style that isn't // provided via block support. 'no-border-radius': style?.border?.radius === 0, }, __experimentalGetElementClassName( 'button' ) ) } style={ { ...borderProps.style, ...colorProps.style, ...spacingProps.style, } } onSplit={ ( value ) => createBlock( 'core/button', { ...attributes, text: value, } ) } onReplace={ onReplace } onMerge={ mergeBlocks } identifier="text" /> </div> <BlockControls group="block"> { ! isURLSet && ( <ToolbarButton name="link" icon={ link } title={ __( 'Link' ) } shortcut={ displayShortcut.primary( 'k' ) } onClick={ startEditing } /> ) } { isURLSet && ( <ToolbarButton name="link" icon={ linkOff } title={ __( 'Unlink' ) } shortcut={ displayShortcut.primaryShift( 'k' ) } onClick={ unlink } isActive={ true } /> ) } </BlockControls> { isSelected && ( isEditingURL || isURLSet ) && ( <Popover position="bottom center" onClose={ () => { setIsEditingURL( false ); richTextRef.current?.focus(); } } anchor={ popoverAnchor } focusOnMount={ isEditingURL ? 'firstElement' : false } __unstableSlotName={ '__unstable-block-tools-after' } shift > <LinkControl className="wp-block-navigation-link__inline-link-input" value={ { url, opensInNewTab } } onChange={ ( { url: newURL = '', opensInNewTab: newOpensInNewTab, } ) => { setAttributes( { url: newURL } ); if ( opensInNewTab !== newOpensInNewTab ) { onToggleOpenInNewTab( newOpensInNewTab ); } } } onRemove={ () => { unlink(); richTextRef.current?.focus(); } } forceIsEditingLink={ isEditingURL } /> </Popover> ) } <InspectorControls> <WidthPanel selectedWidth={ width } setAttributes={ setAttributes } /> </InspectorControls> <InspectorControls __experimentalGroup="advanced"> <TextControl label={ __( 'Link rel' ) } value={ rel || '' } onChange={ onSetLinkRel } /> </InspectorControls> </> ); } export default ButtonEdit;