diff --git a/packages/block-editor/src/components/block-alignment-toolbar/index.js b/packages/block-editor/src/components/block-alignment-toolbar/index.js index e766504f0d9f4..53f003a2c34cc 100644 --- a/packages/block-editor/src/components/block-alignment-toolbar/index.js +++ b/packages/block-editor/src/components/block-alignment-toolbar/index.js @@ -52,6 +52,7 @@ export function BlockAlignmentToolbar( { value, onChange, controls = DEFAULT_CON return ( { + const { + getBlockIndex, + getBlockOrder, + getBlockRootClientId, + } = select( 'core/block-editor' ); + const rootClientId = getBlockRootClientId( clientId ); + const numberOfSiblings = getBlockOrder( rootClientId ).length; + const buttonIndex = getBlockIndex( clientId, rootClientId ); + return { + parentID: rootClientId, + isFirst: buttonIndex === 0, + isLast: buttonIndex === ( numberOfSiblings - 1 ), + }; + }, + clientId + ); + const { moveBlocksUp, moveBlocksDown } = useDispatch( 'core/block-editor' ); + const moveUp = useCallback( + partial( moveBlocksUp, [ clientId ], parentID ), + [ moveBlocksUp, clientId, parentID ] + ); + const moveDown = useCallback( + partial( moveBlocksDown, [ clientId ], parentID ), + [ moveBlocksDown, clientId, parentID ] + ); + const toolbarControls = useMemo( + () => ( compact( [ + isFirst ? null : { + icon: 'arrow-left-alt2', + title: __( 'Move left' ), + onClick: moveUp, + }, + isLast ? null : { + icon: 'arrow-right-alt2', + title: __( 'Move right' ), + onClick: moveDown, + }, + ] ) ), + [ moveUp, moveDown, isFirst, isLast ] + ); + + return ( + + + + ); +} + +const InlineURLPicker = withInstanceId( + function( { instanceId, isSelected, url, onChange } ) { + const linkId = `wp-block-button__inline-link-${ instanceId }`; + return ( + + + + ); + } +); + +function PopoverURLPicker( { url, onChange } ) { + const [ urlInput, setUrlInput ] = useState( url || '' ); + const [ isPopoverEnable, setIsPopoverEnable ] = useState( true ); + const onSubmit = useCallback( + () => { + onChange( urlInput ); + setIsPopoverEnable( false ); + }, + [ urlInput, onChange ] + ); + if ( ! isPopoverEnable ) { + return null; + } + return ( + + + + ); +} + +function URLPicker( { isSelected, url, setAttributes } ) { + const onChange = useCallback( + ( value ) => setAttributes( { url: value } ), + [ setAttributes ] + ); + const { urlInPopover } = useContext( ButtonEditSettings ); + if ( urlInPopover ) { + return isSelected ? ( + + ) : null; + } + return ( + + ); +} + function ButtonEdit( { attributes, backgroundColor, + clientId, textColor, setBackgroundColor, setTextColor, @@ -84,7 +227,6 @@ function ButtonEdit( { fallbackTextColor, setAttributes, className, - instanceId, isSelected, } ) { const { @@ -127,7 +269,6 @@ function ButtonEdit( { setGradient, } = __experimentalUseGradient(); - const linkId = `wp-block-button__inline-link-${ instanceId }`; return (
- - setAttributes( { url: value } ) } - disableSuggestions={ ! isSelected } - id={ linkId } - isFullWidth - hasBorder - /> - + +
); } export default compose( [ - withInstanceId, withColors( 'backgroundColor', { textColor: 'color' } ), applyFallbackStyles, ] )( ButtonEdit ); diff --git a/packages/block-library/src/button/index.js b/packages/block-library/src/button/index.js index 83a01deff0554..28c2e483ed114 100644 --- a/packages/block-library/src/button/index.js +++ b/packages/block-library/src/button/index.js @@ -32,6 +32,7 @@ export const settings = { align: true, alignWide: false, }, + parent: [ 'core/buttons' ], styles: [ { name: 'fill', label: __( 'Fill' ), isDefault: true }, { name: 'outline', label: __( 'Outline' ) }, diff --git a/packages/block-library/src/buttons/block.json b/packages/block-library/src/buttons/block.json new file mode 100644 index 0000000000000..c6fd753dba932 --- /dev/null +++ b/packages/block-library/src/buttons/block.json @@ -0,0 +1,5 @@ +{ + "name": "core/buttons", + "category": "layout", + "attributes": {} +} diff --git a/packages/block-library/src/buttons/edit.js b/packages/block-library/src/buttons/edit.js new file mode 100644 index 0000000000000..c27ed31f73ded --- /dev/null +++ b/packages/block-library/src/buttons/edit.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { IconButton } from '@wordpress/components'; +import { InnerBlocks } from '@wordpress/block-editor'; +import { createBlock } from '@wordpress/blocks'; +import { useCallback } from '@wordpress/element'; +import { useDispatch } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { name as buttonBlockName } from '../button/'; +import { ButtonEditSettings } from '../button/edit-settings'; + +const ALLOWED_BLOCKS = [ buttonBlockName ]; +const BUTTON_BLOCK_SETTINGS = { urlInPopover: true }; +const BUTTONS_TEMPLATE = [ [ 'core/button' ] ]; +const UI_PARTS = { + hasSelectedUI: false, + hasFocusedUI: false, + hasHoveredUI: false, + hasBreadcrumbs: false, + hasMovers: false, + hasSpacing: false, + hasSideInserter: false, +}; + +function useInsertButton( clientId ) { + const { insertBlock } = useDispatch( 'core/block-editor' ); + const insertButton = useCallback( + () => { + const buttonBlock = createBlock( buttonBlockName ); + insertBlock( buttonBlock, undefined, clientId ); + }, + [ insertBlock, clientId ] + ); + return useCallback( + () => { + return ( + + ); + }, + [ insertButton ] + ); +} + +function ButtonsEdit( { clientId, className } ) { + const renderAppender = useInsertButton( clientId ); + return ( +
+ + + +
+ ); +} + +export default ButtonsEdit; diff --git a/packages/block-library/src/buttons/editor.scss b/packages/block-library/src/buttons/editor.scss new file mode 100644 index 0000000000000..1caef42223520 --- /dev/null +++ b/packages/block-library/src/buttons/editor.scss @@ -0,0 +1,37 @@ +.wp-block-buttons .wp-block.block-editor-block-list__block[data-type="core/button"] { + display: inline-block; + width: auto; +} + +.wp-block-buttons { + .block-editor-block-list__block[data-type="core/button"] .block-editor-block-alignment-toolbar { + display: none; + } + + .wp-block-button .wp-block-button__link { + margin-top: 0; + } +} + +.block-editor-block-list__block[data-type="core/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; + } +} diff --git a/packages/block-library/src/buttons/icon.js b/packages/block-library/src/buttons/icon.js new file mode 100644 index 0000000000000..6e18a60a648fe --- /dev/null +++ b/packages/block-library/src/buttons/icon.js @@ -0,0 +1,8 @@ +/** + * WordPress dependencies + */ +import { G, Path, SVG } from '@wordpress/components'; + +export default ( + +); diff --git a/packages/block-library/src/buttons/index.js b/packages/block-library/src/buttons/index.js new file mode 100644 index 0000000000000..6a3ae48b99e56 --- /dev/null +++ b/packages/block-library/src/buttons/index.js @@ -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, +}; diff --git a/packages/block-library/src/buttons/save.js b/packages/block-library/src/buttons/save.js new file mode 100644 index 0000000000000..91cb804231d05 --- /dev/null +++ b/packages/block-library/src/buttons/save.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { InnerBlocks } from '@wordpress/block-editor'; + +export default function save() { + return ( +
+ +
+ ); +} diff --git a/packages/block-library/src/buttons/style.scss b/packages/block-library/src/buttons/style.scss new file mode 100644 index 0000000000000..47fb245ec0024 --- /dev/null +++ b/packages/block-library/src/buttons/style.scss @@ -0,0 +1,7 @@ +.wp-block-buttons .wp-block-button { + display: inline-block; + margin: $grid-size-small; +} +.wp-block-buttons.aligncenter { + text-align: center; +} diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index 7e0224fc67888..457befcf4e64a 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -2,6 +2,7 @@ @import "./audio/editor.scss"; @import "./block/editor.scss"; @import "./button/editor.scss"; +@import "./buttons/editor.scss"; @import "./categories/editor.scss"; @import "./code/editor.scss"; @import "./columns/editor.scss"; diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 3147ebcc0e9cd..257061d10c6d9 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -23,6 +23,7 @@ import * as quote from './quote'; import * as gallery from './gallery'; import * as archives from './archives'; import * as audio from './audio'; +import * as buttons from './buttons'; import * as button from './button'; import * as calendar from './calendar'; import * as categories from './categories'; @@ -108,6 +109,7 @@ export const registerCoreBlocks = () => { archives, audio, button, + buttons, calendar, categories, code, diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index 3fd7e3c2b8e04..b31dc240e34ad 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -1,5 +1,6 @@ @import "./audio/style.scss"; @import "./button/style.scss"; +@import "./buttons/style.scss"; @import "./calendar/style.scss"; @import "./categories/style.scss"; @import "./columns/style.scss"; diff --git a/packages/e2e-tests/fixtures/block-transforms.js b/packages/e2e-tests/fixtures/block-transforms.js index 454bf2aeb2bf2..8c1c25f380492 100644 --- a/packages/e2e-tests/fixtures/block-transforms.js +++ b/packages/e2e-tests/fixtures/block-transforms.js @@ -30,6 +30,12 @@ export const EXPECTED_TRANSFORMS = { 'Group', ], }, + core__buttons: { + originalBlock: 'Buttons', + availableTransforms: [ + 'Group', + ], + }, core__calendar: { originalBlock: 'Calendar', availableTransforms: [ diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.html b/packages/e2e-tests/fixtures/blocks/core__buttons.html new file mode 100644 index 0000000000000..e70af7acc72ad --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.html @@ -0,0 +1,11 @@ + +
+ + + + + + + +
+ diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.json b/packages/e2e-tests/fixtures/blocks/core__buttons.json new file mode 100644 index 0000000000000..044daeb82101a --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.json @@ -0,0 +1,31 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/buttons", + "isValid": true, + "attributes": {}, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/button", + "isValid": true, + "attributes": { + "text": "My button 1" + }, + "innerBlocks": [], + "originalContent": "" + }, + { + "clientId": "_clientId_1", + "name": "core/button", + "isValid": true, + "attributes": { + "text": "My button 2" + }, + "innerBlocks": [], + "originalContent": "" + } + ], + "originalContent": "
\n\t\n\n\t\n
" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json b/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json new file mode 100644 index 0000000000000..b96b1f50db1fc --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.parsed.json @@ -0,0 +1,43 @@ +[ + { + "blockName": "core/buttons", + "attrs": {}, + "innerBlocks": [ + { + "blockName": "core/button", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\t\n\t", + "innerContent": [ + "\n\t\n\t" + ] + }, + { + "blockName": "core/button", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n\t\n\t", + "innerContent": [ + "\n\t\n\t" + ] + } + ], + "innerHTML": "\n
\n\t\n\n\t\n
\n", + "innerContent": [ + "\n
\n\t", + null, + "\n\n\t", + null, + "\n
\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html b/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html new file mode 100644 index 0000000000000..baf0a0226c066 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.serialized.html @@ -0,0 +1,9 @@ + + + diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/buttons.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/buttons.test.js.snap new file mode 100644 index 0000000000000..8aae5ee57c83c --- /dev/null +++ b/packages/e2e-tests/specs/blocks/__snapshots__/buttons.test.js.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Buttons can jump focus back & forth 1`] = ` +" +
+ + + + + +
+" +`; + +exports[`Buttons has focus on button content 1`] = ` +" + +" +`;