-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add: Buttons block #17352
Add: Buttons block #17352
Changes from all commits
5543da9
483c7c1
d03c51c
6d0cfbc
9b44f75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,34 +2,50 @@ | |
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
import { escape } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { | ||
useCallback, | ||
useEffect, | ||
useState, | ||
} from '@wordpress/element'; | ||
import { | ||
compose, | ||
withInstanceId, | ||
} from '@wordpress/compose'; | ||
import { | ||
KeyboardShortcuts, | ||
PanelBody, | ||
RangeControl, | ||
TextControl, | ||
ToggleControl, | ||
withFallbackStyles, | ||
ToolbarButton, | ||
ToolbarGroup, | ||
} from '@wordpress/components'; | ||
import { | ||
BlockControls, | ||
__experimentalUseGradient, | ||
ContrastChecker, | ||
InspectorControls, | ||
__experimentalPanelColorGradientSettings as PanelColorGradientSettings, | ||
RichText, | ||
URLInput, | ||
withColors, | ||
__experimentalLinkControl as LinkControl, | ||
} from '@wordpress/block-editor'; | ||
import { | ||
LEFT, | ||
RIGHT, | ||
UP, | ||
DOWN, | ||
BACKSPACE, | ||
ENTER, | ||
rawShortcut, | ||
displayShortcut, | ||
} from '@wordpress/keycodes'; | ||
|
||
const { getComputedStyle } = window; | ||
|
||
|
@@ -72,6 +88,85 @@ function BorderPanel( { borderRadius = '', setAttributes } ) { | |
); | ||
} | ||
|
||
const handleLinkControlOnKeyDown = ( event ) => { | ||
const { keyCode } = event; | ||
|
||
if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( keyCode ) > -1 ) { | ||
// Stop the key event from propagating up to ObserveTyping.startTypingInTextField. | ||
event.stopPropagation(); | ||
} | ||
}; | ||
|
||
const handleLinkControlOnKeyPress = ( event ) => { | ||
event.stopPropagation(); | ||
}; | ||
|
||
function URLPicker( { isSelected, url, title, setAttributes, opensInNewTab, onToggleOpenInNewTab } ) { | ||
const [ isURLPickerOpen, setIsURLPickerOpen ] = useState( false ); | ||
useEffect( | ||
() => { | ||
if ( ! isSelected ) { | ||
setIsURLPickerOpen( false ); | ||
} | ||
}, | ||
[ isSelected ] | ||
); | ||
const openLinkControl = () => { | ||
setIsURLPickerOpen( true ); | ||
}; | ||
const linkControl = isURLPickerOpen && ( | ||
<LinkControl | ||
className="wp-block-navigation-link__inline-link-input" | ||
onKeyDown={ handleLinkControlOnKeyDown } | ||
onKeyPress={ handleLinkControlOnKeyPress } | ||
currentLink={ ! url && ! title ? null : { url, title } } | ||
onLinkChange={ ( { title: newTitle = '', url: newURL = '' } ) => { | ||
setAttributes( { | ||
title: escape( newTitle ), | ||
url: newURL, | ||
} ); | ||
} } | ||
currentSettings={ [ | ||
{ | ||
id: 'opensInNewTab', | ||
title: __( 'Open in new tab' ), | ||
checked: opensInNewTab, | ||
}, | ||
] } | ||
onSettingsChange={ ( setting, value ) => { | ||
if ( setting === 'opensInNewTab' ) { | ||
onToggleOpenInNewTab( value ); | ||
} | ||
} } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The API is a bit weird to me. That's feedback that is not meant directly to this PR but more about the LinkControl component. cc @getdave There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took a stab at this here #19396 |
||
onClose={ () => { | ||
setIsURLPickerOpen( false ); | ||
} } | ||
/> | ||
); | ||
return ( | ||
<> | ||
<BlockControls> | ||
<ToolbarGroup> | ||
<ToolbarButton | ||
name="link" | ||
icon="admin-links" | ||
title={ __( 'Link' ) } | ||
shortcut={ displayShortcut.primary( 'k' ) } | ||
onClick={ openLinkControl } | ||
/> | ||
</ToolbarGroup> | ||
</BlockControls> | ||
<KeyboardShortcuts | ||
bindGlobal | ||
shortcuts={ { | ||
[ rawShortcut.primary( 'k' ) ]: openLinkControl, | ||
} } | ||
/> | ||
{ linkControl } | ||
</> | ||
); | ||
} | ||
|
||
function ButtonEdit( { | ||
attributes, | ||
backgroundColor, | ||
|
@@ -150,18 +245,13 @@ function ButtonEdit( { | |
borderRadius: borderRadius ? borderRadius + 'px' : undefined, | ||
} } | ||
/> | ||
<URLInput | ||
label={ __( 'Link' ) } | ||
className="wp-block-button__inline-link" | ||
value={ url } | ||
/* eslint-disable jsx-a11y/no-autofocus */ | ||
// Disable Reason: The rule is meant to prevent enabling auto-focus, not disabling it. | ||
autoFocus={ false } | ||
/* eslint-enable jsx-a11y/no-autofocus */ | ||
onChange={ ( value ) => setAttributes( { url: value } ) } | ||
disableSuggestions={ ! isSelected } | ||
isFullWidth | ||
hasBorder | ||
<URLPicker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also could use the new LinkControl introduced by the Navigation block. This could be a separate PR though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I updated the PR to use LinkControl, so we have more similar UI's. |
||
title={ title } | ||
url={ url } | ||
setAttributes={ setAttributes } | ||
isSelected={ isSelected } | ||
opensInNewTab={ linkTarget === '_blank' } | ||
onToggleOpenInNewTab={ onToggleOpenInNewTab } | ||
/> | ||
<InspectorControls> | ||
<PanelColorGradientSettings | ||
|
@@ -215,7 +305,6 @@ function ButtonEdit( { | |
} | ||
|
||
export default compose( [ | ||
withInstanceId, | ||
withColors( 'backgroundColor', { textColor: 'color' } ), | ||
applyFallbackStyles, | ||
] )( ButtonEdit ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"name": "core/buttons", | ||
"category": "layout", | ||
"attributes": {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { InnerBlocks } from '@wordpress/block-editor'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { name as buttonBlockName } from '../button/'; | ||
|
||
const ALLOWED_BLOCKS = [ buttonBlockName ]; | ||
const BUTTONS_TEMPLATE = [ [ 'core/button' ] ]; | ||
const UI_PARTS = { | ||
hasSelectedUI: false, | ||
}; | ||
|
||
function ButtonsEdit( { className } ) { | ||
return ( | ||
<div className={ className }> | ||
<InnerBlocks | ||
allowedBlocks={ ALLOWED_BLOCKS } | ||
template={ BUTTONS_TEMPLATE } | ||
__experimentalUIParts={ UI_PARTS } | ||
__experimentalMoverDirection="horizontal" | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default ButtonsEdit; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
.wp-block-buttons .wp-block.block-editor-block-list__block[data-type="core/button"] { | ||
display: inline-block; | ||
width: auto; | ||
} | ||
|
||
.wp-block-buttons { | ||
div[data-type="core/button"] div[data-block] { | ||
display: block; | ||
} | ||
|
||
&[data-align="center"] .block-editor-block-list__layout { | ||
display: flex; | ||
align-items: center; | ||
flex-wrap: wrap; | ||
justify-content: center; | ||
} | ||
|
||
&[data-align="right"] .block-editor-block-list__layout { | ||
display: flex; | ||
justify-content: flex-end; | ||
} | ||
|
||
.block-list-appender { | ||
display: inline-block !important; | ||
margin: 0; | ||
} | ||
|
||
.block-editor-block-list__layout > div:last-child { | ||
display: inline-block; | ||
} | ||
|
||
.block-editor-button-block-appender { | ||
background: none; | ||
outline: none; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { G, Path, SVG } from '@wordpress/components'; | ||
|
||
export default ( | ||
<SVG viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><Path fill="none" d="M0 0h24v24H0V0z" /><G><Path d="M19 6H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H5V8h14v8z" /></G></SVG> | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import edit from './edit'; | ||
import icon from './icon'; | ||
import metadata from './block.json'; | ||
import save from './save'; | ||
|
||
const { name } = metadata; | ||
|
||
export { metadata, name }; | ||
|
||
export const settings = { | ||
title: __( 'Buttons' ), | ||
description: __( 'Prompt visitors to take action with a group of button-style links.' ), | ||
icon, | ||
keywords: [ __( 'link' ) ], | ||
supports: { | ||
align: true, | ||
alignWide: false, | ||
}, | ||
edit, | ||
save, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { InnerBlocks } from '@wordpress/block-editor'; | ||
|
||
export default function save() { | ||
return ( | ||
<div> | ||
<InnerBlocks.Content /> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.wp-block-buttons .wp-block-button { | ||
display: inline-block; | ||
margin: $grid-size-small; | ||
} | ||
.wp-block-buttons.aligncenter { | ||
text-align: center; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!-- wp:buttons --> | ||
<div class="wp-block-buttons"> | ||
<!-- wp:button --> | ||
<div class="wp-block-button"><a class="wp-block-button__link">My button 1</a></div> | ||
<!-- /wp:button --> | ||
|
||
<!-- wp:button --> | ||
<div class="wp-block-button"><a class="wp-block-button__link">My button 2</a></div> | ||
<!-- /wp:button --> | ||
</div> | ||
<!-- /wp:buttons --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What purpose does this serve? How would a future maintainer be expected to interpret this requirement to avoid a regression?
A few suggestions:
Event#stopPropagation
is a code smell, and we should avoid it as much as possible