From 81eab53be6b95bb368cf35fe37482ba927847836 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 6 Dec 2024 14:29:07 -0500 Subject: [PATCH 01/11] Add a Modal block --- mu-plugins/blocks/modal/index.php | 22 ++++++ mu-plugins/blocks/modal/render.php | 68 +++++++++++++++++++ mu-plugins/blocks/modal/src/block.json | 27 ++++++++ mu-plugins/blocks/modal/src/index.js | 33 +++++++++ mu-plugins/blocks/modal/src/style.scss | 63 +++++++++++++++++ mu-plugins/blocks/modal/src/view.js | 93 ++++++++++++++++++++++++++ mu-plugins/loader.php | 1 + 7 files changed, 307 insertions(+) create mode 100644 mu-plugins/blocks/modal/index.php create mode 100644 mu-plugins/blocks/modal/render.php create mode 100644 mu-plugins/blocks/modal/src/block.json create mode 100644 mu-plugins/blocks/modal/src/index.js create mode 100644 mu-plugins/blocks/modal/src/style.scss create mode 100644 mu-plugins/blocks/modal/src/view.js diff --git a/mu-plugins/blocks/modal/index.php b/mu-plugins/blocks/modal/index.php new file mode 100644 index 00000000..ac303a73 --- /dev/null +++ b/mu-plugins/blocks/modal/index.php @@ -0,0 +1,22 @@ + false, +]; + +// Set up a unique ID for this modal. +$html_id = wp_unique_id( 'modal-' ); + +?> +
+ data-wp-interactive="wporg/modal" + data-wp-watch="callbacks.init" + data-wp-on--keydown="actions.handleKeydown" + data-wp-class--is-modal-open="context.isOpen" + +> +
+
+ + + + + +
+
+ +
+
+ + +
+ +
+
+
+
diff --git a/mu-plugins/blocks/modal/src/block.json b/mu-plugins/blocks/modal/src/block.json new file mode 100644 index 00000000..058ff837 --- /dev/null +++ b/mu-plugins/blocks/modal/src/block.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "wporg/modal", + "title": "Modal", + "icon": "location", + "category": "layout", + "description": "A modal dialog.", + "textdomain": "wporg", + "attributes": { + "href": { + "type": "string" + }, + "label": { + "type": "string", + "default": "Open modal" + } + }, + "supports": { + "align": false, + "interactivity": true + }, + "editorScript": "file:./index.js", + "style": "file:./style-index.css", + "viewScriptModule": "file:./view.js", + "render": "file:../render.php" +} diff --git a/mu-plugins/blocks/modal/src/index.js b/mu-plugins/blocks/modal/src/index.js new file mode 100644 index 00000000..3f2463a1 --- /dev/null +++ b/mu-plugins/blocks/modal/src/index.js @@ -0,0 +1,33 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { registerBlockType } from '@wordpress/blocks'; +import { InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import './style.scss'; + +function Edit( { attributes, setAttributes } ) { + return ( +
+ setAttributes( { label } ) } + placeholder={ __( 'Open modal', 'wporg' ) } + /> + +
+ ); +} + +registerBlockType( metadata.name, { + edit: Edit, + save: () => { + return ; + } +} ); diff --git a/mu-plugins/blocks/modal/src/style.scss b/mu-plugins/blocks/modal/src/style.scss new file mode 100644 index 00000000..660a06b5 --- /dev/null +++ b/mu-plugins/blocks/modal/src/style.scss @@ -0,0 +1,63 @@ +.wp-block-wporg-modal { + position: relative; + + &.is-modal-open { + z-index: 100000; /* admin bar + 1. */ + + & .wporg-modal__modal { + display: block; + } + } +} + +.wporg-modal__modal-backdrop { + position: fixed; + inset: 0; + height: 100vh; + width: 100vw; + padding: 20px; + + &:not([hidden]) { + display: flex; + align-items: center; + justify-content: center; + background: #1e1e1ecc; + } +} + +.wporg-modal__modal { + display: none; + position: relative; + width: 35rem; + background: var(--wp--preset--color--white); +} + +.wporg-modal__modal-close { + position: absolute; + top: var(--wp--preset--spacing--20); + right: var(--wp--preset--spacing--20); + z-index: 1; + width: 24px; + height: 24px; + border: none; + background: transparent; + + &::before { + content: ""; + display: inline-block; + position: absolute; + inset: 0; + /* stylelint-disable-next-line function-url-quotes */ + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24' aria-hidden='true' focusable='false'%3E%3Cpath d='m13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z'%3E%3C/path%3E%3C/svg%3E"); + mask-repeat: no-repeat; + mask-position: center; + background-color: var(--wp--preset--color--white); + } +} + +.wporg-modal__modal-content { + margin: 0; + max-height: 80dvh; + overflow-y: auto; + border: none; +} diff --git a/mu-plugins/blocks/modal/src/view.js b/mu-plugins/blocks/modal/src/view.js new file mode 100644 index 00000000..5d242653 --- /dev/null +++ b/mu-plugins/blocks/modal/src/view.js @@ -0,0 +1,93 @@ +/** + * WordPress dependencies + */ +import { getContext, getElement, store } from '@wordpress/interactivity'; + +// See https://github.com/WordPress/gutenberg/blob/37f52ae884a40f7cb77ac2484648b4e4ad973b59/packages/block-library/src/navigation/view-interactivity.js +const focusableSelectors = [ + 'a[href]', + 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', + 'select:not([disabled]):not([aria-hidden])', + 'textarea:not([disabled]):not([aria-hidden])', + 'button:not([disabled]):not([aria-hidden])', + '[contenteditable]', + '[tabindex]:not([tabindex^="-"])', +]; + +const { actions } = store( 'wporg/modal', { + actions: { + toggle: () => { + const context = getContext(); + if ( context.isOpen ) { + actions.close(); + } else { + actions.open(); + } + }, + + /** + * Close the modal only if the backdrop is clicked. + * Ignores clicks inside the modal itself. + * + * @param {Event} event + */ + clickBackdrop: ( event ) => { + if ( event.target.classList.contains( 'wporg-modal__modal-backdrop' ) ) { + actions.close(); + } + }, + + open: () => { + const context = getContext(); + context.isOpen = true; + context.modal.focus(); + }, + + close: () => { + const context = getContext(); + context.isOpen = false; + context.toggleButton.focus(); + }, + + handleKeydown: ( event ) => { + const context = getContext(); + // Only handle key events if the dropdown is open. + if ( ! context.isOpen ) { + return; + } + + // If Escape close the dropdown. + if ( event.key === 'Escape' ) { + actions.close(); + return; + } + + // Trap focus. + if ( event.key === 'Tab' ) { + // If shift + tab it change the direction. + if ( event.shiftKey && window.document.activeElement === context.firstFocusableElement ) { + event.preventDefault(); + context.lastFocusableElement.focus(); + } else if ( ! event.shiftKey && window.document.activeElement === context.lastFocusableElement ) { + event.preventDefault(); + context.firstFocusableElement.focus(); + } + } + }, + }, + + callbacks: { + init: () => { + const context = getContext(); + const { ref } = getElement(); + context.toggleButton = ref.querySelector( '.wporg-modal__toggle' ); + context.modal = ref.querySelector( '.wporg-modal__modal' ); + + if ( context.isOpen ) { + const focusableElements = context.modal.querySelectorAll( focusableSelectors ); + context.firstFocusableElement = focusableElements[ 0 ]; + context.lastFocusableElement = focusableElements[ focusableElements.length - 1 ]; + } + }, + }, +} ); diff --git a/mu-plugins/loader.php b/mu-plugins/loader.php index 3d08e7ba..8aacabbf 100644 --- a/mu-plugins/loader.php +++ b/mu-plugins/loader.php @@ -37,6 +37,7 @@ require_once __DIR__ . '/blocks/local-navigation-bar/index.php'; require_once __DIR__ . '/blocks/latest-news/latest-news.php'; require_once __DIR__ . '/blocks/link-wrapper/index.php'; +require_once __DIR__ . '/blocks/modal/index.php'; require_once __DIR__ . '/blocks/navigation/index.php'; require_once __DIR__ . '/blocks/notice/index.php'; require_once __DIR__ . '/blocks/query-filter/index.php'; From 30a5582eb659d379a905e0f35731cf5a4cdae1d1 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Fri, 6 Dec 2024 18:14:38 -0500 Subject: [PATCH 02/11] Add editor UI --- mu-plugins/blocks/modal/render.php | 2 +- mu-plugins/blocks/modal/src/block.json | 1 + mu-plugins/blocks/modal/src/editor.scss | 28 ++++++++ mu-plugins/blocks/modal/src/index.js | 85 +++++++++++++++++++++---- 4 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 mu-plugins/blocks/modal/src/editor.scss diff --git a/mu-plugins/blocks/modal/render.php b/mu-plugins/blocks/modal/render.php index dddf3111..f8f8eae0 100644 --- a/mu-plugins/blocks/modal/render.php +++ b/mu-plugins/blocks/modal/render.php @@ -24,7 +24,7 @@ >
+ <> + + + { + setIsModalPreview( newValue ); + } } + /> + setAttributes( { href } ) } + /> + + +
+
+
+ setAttributes( { label } ) } + placeholder={ __( 'Open modal', 'wporg' ) } + /> +
+
+ +
+ ); } @@ -29,5 +92,5 @@ registerBlockType( metadata.name, { edit: Edit, save: () => { return ; - } + }, } ); From 6720344734bd55018a15e6e5c352ac387291046a Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Mon, 9 Dec 2024 15:31:06 -0500 Subject: [PATCH 03/11] Add color controls --- mu-plugins/blocks/modal/render.php | 21 ++++- mu-plugins/blocks/modal/src/block.json | 29 ++++++ mu-plugins/blocks/modal/src/editor.scss | 5 +- mu-plugins/blocks/modal/src/index.js | 117 ++++++++++++++++++++++-- mu-plugins/blocks/modal/src/style.scss | 8 +- 5 files changed, 167 insertions(+), 13 deletions(-) diff --git a/mu-plugins/blocks/modal/render.php b/mu-plugins/blocks/modal/render.php index f8f8eae0..4925fb72 100644 --- a/mu-plugins/blocks/modal/render.php +++ b/mu-plugins/blocks/modal/render.php @@ -5,6 +5,25 @@ $attributes['label'] = $attributes['label'] ?: __( 'Open modal', 'wporg' ); +$background_color = ! empty( $attributes['customBackgroundColor'] ) ? $attributes['customBackgroundColor'] : "var(--wp--preset--color--{$attributes['backgroundColor']})"; +$text_color = ! empty( $attributes['customTextColor'] ) ? $attributes['customTextColor'] : "var(--wp--preset--color--{$attributes['textColor']})"; +$overlay_color = ! empty( $attributes['customOverlayColor'] ) ? $attributes['customOverlayColor'] : "var(--wp--preset--color--{$attributes['overlayColor']})"; +$close_button_color = ! empty( $attributes['customCloseButtonColor'] ) ? $attributes['customCloseButtonColor'] : "var(--wp--preset--color--{$attributes['closeButtonColor']})"; + +$style = ''; +if ( $background_color ) { + $style .= "--wp--custom--wporg-modal--color--background: {$background_color};"; +} +if ( $text_color ) { + $style .= "--wp--custom--wporg-modal--color--text: {$text_color};"; +} +if ( $overlay_color ) { + $style .= "--wp--custom--wporg-modal--color--overlay: {$overlay_color};"; +} +if ( $close_button_color ) { + $style .= "--wp--custom--wporg-modal--color--close-button: {$close_button_color};"; +} + // Initial state to pass to Interactivity API. $init_state = [ 'isOpen' => false, @@ -15,7 +34,7 @@ ?>
+ $style ]); // phpcs:ignore ?> data-wp-interactive="wporg/modal" data-wp-watch="callbacks.init" data-wp-on--keydown="actions.handleKeydown" diff --git a/mu-plugins/blocks/modal/src/block.json b/mu-plugins/blocks/modal/src/block.json index 6407b8ae..16431f1a 100644 --- a/mu-plugins/blocks/modal/src/block.json +++ b/mu-plugins/blocks/modal/src/block.json @@ -8,6 +8,34 @@ "description": "A modal dialog.", "textdomain": "wporg", "attributes": { + "backgroundColor": { + "type": "string", + "default": "white" + }, + "customBackgroundColor": { + "type": "string" + }, + "textColor": { + "type": "string", + "default": "charcoal-1" + }, + "customTextColor": { + "type": "string" + }, + "closeButtonColor": { + "type": "string", + "default": "charcoal-1" + }, + "customCloseButtonColor": { + "type": "string" + }, + "overlayColor": { + "type": "string" + }, + "customOverlayColor": { + "type": "string", + "default": "#1e1e1ecc" + }, "href": { "type": "string" }, @@ -18,6 +46,7 @@ }, "supports": { "align": false, + "layout": false, "interactivity": true }, "editorScript": "file:./index.js", diff --git a/mu-plugins/blocks/modal/src/editor.scss b/mu-plugins/blocks/modal/src/editor.scss index 0d45d6d8..5de7b3f0 100644 --- a/mu-plugins/blocks/modal/src/editor.scss +++ b/mu-plugins/blocks/modal/src/editor.scss @@ -17,12 +17,13 @@ display: flex; align-items: center; justify-content: center; - background: #1e1e1ecc; + background: var(--wp--custom--wporg-modal--color--overlay); } } .wporg-modal__modal { position: relative; width: 35rem; - background: var(--wp--preset--color--white); + background: var(--wp--custom--wporg-modal--color--background); + color: var(--wp--custom--wporg-modal--color--text); } diff --git a/mu-plugins/blocks/modal/src/index.js b/mu-plugins/blocks/modal/src/index.js index 209b825f..facc45aa 100644 --- a/mu-plugins/blocks/modal/src/index.js +++ b/mu-plugins/blocks/modal/src/index.js @@ -2,7 +2,15 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { InspectorControls, InnerBlocks, RichText, useBlockProps } from '@wordpress/block-editor'; +import { + __experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown, + __experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients, + InspectorControls, + InnerBlocks, + RichText, + useBlockProps, + withColors, +} from '@wordpress/block-editor'; import { PanelBody, TextControl, ToggleControl } from '@wordpress/components'; import { registerBlockType } from '@wordpress/blocks'; import { useState } from 'react'; @@ -11,18 +19,104 @@ import { useState } from 'react'; * Internal dependencies */ import metadata from './block.json'; -import './editor.scss'; import './style.scss'; -function Edit( { attributes, setAttributes } ) { +function Edit( { + attributes, + setAttributes, + backgroundColor, + setBackgroundColor, + textColor, + setTextColor, + closeButtonColor, + setCloseButtonColor, + overlayColor, + setOverlayColor, + clientId, +} ) { const [ isModalPreview, setIsModalPreview ] = useState( false ); + const { customBackgroundColor, customCloseButtonColor, customTextColor, customOverlayColor } = attributes; + const colorGradientSettings = useMultipleOriginColorsAndGradients(); const classes = []; if ( isModalPreview ) { classes.push( 'is-modal-open' ); } + + const style = { + '--wp--custom--wporg-modal--color--background': backgroundColor.slug + ? `var( --wp--preset--color--${ backgroundColor.slug } )` + : customBackgroundColor, + '--wp--custom--wporg-modal--color--text': textColor.slug + ? `var( --wp--preset--color--${ textColor.slug } )` + : customTextColor, + '--wp--custom--wporg-modal--color--close-button': closeButtonColor.slug + ? `var( --wp--preset--color--${ closeButtonColor.slug } )` + : customCloseButtonColor, + '--wp--custom--wporg-modal--color--overlay': overlayColor.slug + ? `var( --wp--preset--color--${ overlayColor.slug } )` + : customOverlayColor, + }; + console.log( style ); + const blockProps = useBlockProps( { + className: classes, + style, + } ); + return ( <> - + + { + setBackgroundColor( value ); + setAttributes( { + customBackgroundColor: value, + } ); + }, + }, + { + label: __( 'Modal text', 'wporg' ), + colorValue: textColor.color || customTextColor, + onColorChange: ( value ) => { + setTextColor( value ); + setAttributes( { + customTextColor: value, + } ); + }, + }, + { + label: __( 'Close button', 'wporg' ), + colorValue: closeButtonColor.color || customCloseButtonColor, + onColorChange: ( value ) => { + setCloseButtonColor( value ); + setAttributes( { + customCloseButtonColor: value, + } ); + }, + }, + { + label: __( 'Overlay', 'wporg' ), + colorValue: overlayColor.color || customOverlayColor, + onColorChange: ( value ) => { + setOverlayColor( value ); + setAttributes( { + customOverlayColor: value, + } ); + }, + enableAlpha: true, + }, + ] } + panelId={ clientId } + hasColorsOrGradients={ false } + disableCustomColors={ false } + __experimentalIsRenderedInSidebar + { ...colorGradientSettings } + /> + + -
+