diff --git a/assets/css/admin-distributed-post.css b/assets/css/admin-distributed-post.css index a07aa4a7f..cb2d083a2 100644 --- a/assets/css/admin-distributed-post.css +++ b/assets/css/admin-distributed-post.css @@ -12,15 +12,3 @@ .open-distributor-help { cursor: pointer; } - -#distributed-to:before { - content: ' '; - display: inline-block; - top: 5px; - padding-right: 25px; - width: 20px; - height: 20px; - background: url(../../assets/img/icon.svg) no-repeat top left; - background-size: contain; - position: relative; -} diff --git a/assets/css/admin.css b/assets/css/admin.css index abe4d4ca6..981f8155f 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -11,3 +11,22 @@ font-weight: bold; font-size: 14px; } + +@media (min-width: 783px) { + body.is-showing-distributor.is-fullscreen-mode #wpadminbar { + display: block; + } + + body.is-showing-distributor.is-fullscreen-mode .interface-interface-skeleton { + top: 32px; + } +} + +.distributor-panel .components-panel__body-title svg { + height: 20px; + width: 20px; +} + +.distributor-toggle { + margin-bottom: 8px; +} diff --git a/assets/css/gutenberg-syndicated-post.css b/assets/css/gutenberg-syndicated-post.css index 645dd0d2b..121d1b338 100644 --- a/assets/css/gutenberg-syndicated-post.css +++ b/assets/css/gutenberg-syndicated-post.css @@ -59,15 +59,3 @@ body.dt-linked-post { #distributed-from a { vertical-align: top; } - -#distributed-from:before { - content: ' '; - display: inline-block; - top: -1px; - padding-right: 2px; - width: 20px; - height: 20px; - background: url(../../assets/img/icon.svg) no-repeat top left; - background-size: contain; - position: relative; -} diff --git a/assets/css/push.css b/assets/css/push.css index 5e182b819..a880e5ae7 100644 --- a/assets/css/push.css +++ b/assets/css/push.css @@ -9,6 +9,7 @@ @custom-media --small-screen (min-width: 480px); @custom-media --medium-screen (min-width: 768px); +@custom-media --gutenberg-small-screen (max-width: 782px); #distributor-push-wrapper, #wpadminbar #wp-admin-bar-distributor-placeholder > .ab-item { @@ -47,17 +48,20 @@ display: block; } -#wp-admin-bar-distributor.hover::before, -#wp-admin-bar-distributor.syncing::before { +#distributor-overlay { + display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; - background: rgba(0,0,0,0.6); - z-index: -1; - content: ''; - pointer-events: none; + background: rgba(0, 0, 0, 0.6); + z-index: 10000; + + &.show, + &.syncing { + display: block; + } } #wpadminbar #distributor-push-wrapper .inner { @@ -432,6 +436,12 @@ } } +.distributor-panel { + @media (--gutenberg-small-screen) { + display: none; + } +} + #distributor-push-wrapper .loader-item, #distributor-push-wrapper .loader-item *, #distributor-push-wrapper .loader-item ::after, diff --git a/assets/js/gutenberg-plugin.js b/assets/js/gutenberg-plugin.js new file mode 100644 index 000000000..0421c98aa --- /dev/null +++ b/assets/js/gutenberg-plugin.js @@ -0,0 +1,163 @@ +import { wp, dtGutenberg } from 'window'; +import PluginIcon from '../img/icon.svg'; // eslint-disable-line no-unused-vars + +const { Icon } = wp.components; // eslint-disable-line no-unused-vars +const { select, useSelect } = wp.data; +const { PluginDocumentSettingPanel } = wp.editPost; // eslint-disable-line no-unused-vars +const { __, sprintf } = wp.i18n; +const { registerPlugin } = wp.plugins; + +/** + * Add ability to show the admin bar, if needed + */ +const RenderShowAdminBar = () => { // eslint-disable-line no-unused-vars + const bodyClasses = document.body.classList; + const isFullScreenMode = select( 'core/edit-post' ).isFeatureActive( 'fullscreenMode' ); + const distributorTopMenu = document.querySelector( '#wp-admin-bar-distributor' ); + const distributorAdminItem = document.querySelector( '#wp-admin-bar-distributor > a' ); + + // Don't show anything if this is a distributed item + if ( 0 !== parseInt( dtGutenberg.syndicationTime ) ) { + return null; + } + + if ( ! distributorTopMenu || ! distributorAdminItem ) { + return ( +
+ { __( 'Refresh page to see distribution options', 'distributor' ) } +
+ ); + } + + return ( +
+ +
+ ); +}; + +/** + * Render the draft message + */ +const RenderDraftMessage = () => { // eslint-disable-line no-unused-vars + if ( 0 !== parseInt( dtGutenberg.syndicationTime ) ) { + return null; + } + + return ( +

+ { __( 'Distribution options available once published', 'distributor' ) } +

+ ); +}; + +/** + * Render the distribution information, if needed + */ +const RenderDistributionInfo = () => { // eslint-disable-line no-unused-vars + if ( 0 < parseInt( dtGutenberg.syndicationCount ) ) { + return ; + } else if ( 0 !== parseInt( dtGutenberg.syndicationTime ) ) { + return ; + } + + return null; +}; + +/** + * Render the distributed to component + */ +const RenderDistributedTo = () => { // eslint-disable-line no-unused-vars + return( + + { sprintf( __( 'Distributed to %1$s connection%2$s.', 'distributor' ), + dtGutenberg.syndicationCount, + '1' === dtGutenberg.syndicationCount ? '' : 's' ) } + + ); +}; + +/** + * Render the distributed from component + */ +const RenderDistributedFrom = () => { // eslint-disable-line no-unused-vars + return( + + { __( 'Distributed on: ', 'distributor' ) } + { dtGutenberg.syndicationTime } + + ); +}; + +/** + * Create the Distributor icon + */ +const DistributorIcon = () => ( + } + size={ 20 } + /> +); + +/** + * Add the Distributor panel to Gutenberg + */ +const DistributorPlugin = () => { + // Ensure the user has proper permissions + if ( dtGutenberg.noPermissions && 1 === parseInt( dtGutenberg.noPermissions ) ) { + return null; + } + + const postType = useSelect( select => select( 'core/editor' ).getCurrentPostType() ); + const postStatus = useSelect( select => select( 'core/editor' ).getCurrentPostAttribute( 'status' ) ); + + // Ensure we are on a supported post type + if ( dtGutenberg.supportedPostTypes && dtGutenberg.supportedPostTypes[ postType ] === undefined ) { + return null; + } + + // If we are on a non-supported post status, change what we show + if ( dtGutenberg.supportedPostStati && ! dtGutenberg.supportedPostStati.includes( postStatus ) ) { + return ( + + + + + ); + } + + return ( + + + + + ); +}; + +registerPlugin( 'distributor-plugin', { render: DistributorPlugin } ); diff --git a/assets/js/gutenberg-status-plugin.js b/assets/js/gutenberg-status-plugin.js deleted file mode 100644 index c13faf40e..000000000 --- a/assets/js/gutenberg-status-plugin.js +++ /dev/null @@ -1,42 +0,0 @@ -import { wp, dtGutenberg } from 'window'; - -if ( 'undefined' !== typeof wp.editPost.PluginPostStatusInfo ) { - const { registerPlugin } = wp.plugins; - const { __ } = wp.i18n; - const { PluginPostStatusInfo } = wp.editPost; // eslint-disable-line no-unused-vars - - /** - * Render the distributed from component. - */ - const renderDistributedFrom = () => { - return( - - - { __( 'Distributed on: ' ) } - { dtGutenberg.syndicationTime } - - - ); - }; - - /** - * Render the distributed to component. - */ - const renderDistributedTo = () => { - return( - - - { wp.i18n.sprintf( wp.i18n.__( 'Distributed to %1$s connection%2$s.', 'distributor' ), - dtGutenberg.syndicationCount, - '1' === dtGutenberg.syndicationCount ? '' : 's' ) } - - - ); - }; - - if ( 0 < parseInt( dtGutenberg.syndicationCount ) ) { - registerPlugin( 'distributor-status-panel', { render: renderDistributedTo } ); - } else if ( 0 !== parseInt( dtGutenberg.syndicationTime ) ) { - registerPlugin( 'distributor-status-panel', { render: renderDistributedFrom } ); - } -} diff --git a/assets/js/push.js b/assets/js/push.js index 93341c444..c16e5e8f8 100755 --- a/assets/js/push.js +++ b/assets/js/push.js @@ -51,6 +51,12 @@ jQuery( window ).on( 'load', () => { distributorMenuItem.appendChild( distributorPushWrapper ); + // Add our overlay div + const overlayDiv = document.createElement( 'div' ); + overlayDiv.id = 'distributor-overlay'; + const contentNode = document.getElementById( 'wpadminbar' ); + contentNode.parentNode.insertBefore( overlayDiv, contentNode ); + /** * Set variables after connections have been rendered */ @@ -273,12 +279,42 @@ jQuery( window ).on( 'load', () => { } } + /** + * If the menu isn't showing yet, wait to see if it does. + * + * This is an attempt to deal with the delay on the + * hoverintent function core uses on the adminbar. + */ + function waitForDistributorMenuToShow() { + if ( distributorTopMenu.classList.contains( 'hover' ) ) { + distributorMenuEntered(); + } else { + setTimeout( () => { + if ( distributorTopMenu.classList.contains( 'hover' ) ) { + distributorMenuEntered(); + } + }, 210 ); + } + } + /** * Handle distributor push dropdown menu. */ function distributorMenuEntered() { distributorMenuItem.focus(); + // Show or hide the overlay + if ( + overlayDiv.classList.contains( 'show' ) && + ! overlayDiv.classList.contains( 'syncing' ) && + ! distributorTopMenu.classList.contains( 'hover' ) + ) { + overlayDiv.classList.remove( 'show' ); + document.body.classList.remove( 'is-showing-distributor' ); + } else { + overlayDiv.classList.add( 'show' ); + } + if ( distributorPushWrapper.classList.contains( 'loaded' ) ) { return; } @@ -340,16 +376,82 @@ jQuery( window ).on( 'load', () => { } ); } - // Event listerners when to fetch distributor data. + /** + * Close distributor menu when a click occurs outside of it + */ + function maybeCloseDistributorMenu() { + // If a distribution is in progress, don't close things + if ( overlayDiv.classList.contains( 'syncing' ) ) { + return; + } + + // If the Distributor menu is showing, hide everything + if ( distributorTopMenu.classList.contains( 'hover' ) ) { + overlayDiv.classList.remove( 'show' ); + distributorTopMenu.classList.remove( 'hover' ); + document.body.classList.remove( 'is-showing-distributor' ); + } + + // If the Distributor menu isn't showing but the overlay is, remove the overlay + if ( + ! distributorTopMenu.classList.contains( 'hover' ) && + overlayDiv.classList.contains( 'show' ) + ) { + overlayDiv.classList.remove( 'show' ); + document.body.classList.remove( 'is-showing-distributor' ); + } + } + + // Event listeners when to fetch distributor data. distributorAdminItem.addEventListener( 'keydown', function( e ) { // Pressing Enter. if ( ( 13 === e.keyCode ) ) { distributorMenuEntered(); } + + // Pressing Escape. + if ( 27 === e.keyCode ) { + overlayDiv.classList.remove( 'show' ); + } }, false ); - distributorAdminItem.addEventListener( 'touchstart', distributorMenuEntered, false ); - distributorAdminItem.addEventListener( 'mouseenter', distributorMenuEntered, false ); + // Listen for hover events to remove overlay div + window.hoverintent( + distributorTopMenu, + hoverIn, + hoverOut + ).options( { + timeout: 190 + } ); + + /** + * Distributor menu hovered on + * + * Not currently using as this is handled in the + * distributorMenuEntered function. + */ + function hoverIn() { + return null; + } + + /** + * Distributor menu hovered out + * + * Used to remove the overlay. + */ + function hoverOut() { + if ( + ! distributorTopMenu.classList.contains( 'hover' ) && + ! overlayDiv.classList.contains( 'syncing' ) + ) { + overlayDiv.classList.remove( 'show' ); + document.body.classList.remove( 'is-showing-distributor' ); + } + } + + distributorAdminItem.addEventListener( 'touchstart', waitForDistributorMenuToShow, false ); + distributorAdminItem.addEventListener( 'mouseenter', waitForDistributorMenuToShow, false ); + overlayDiv.addEventListener( 'click', maybeCloseDistributorMenu, true ); /** * Do syndication ajax @@ -360,6 +462,7 @@ jQuery( window ).on( 'load', () => { } distributorTopMenu.classList.add( 'syncing' ); + overlayDiv.classList.add( 'syncing' ); const data = { action: 'dt_push', @@ -380,6 +483,10 @@ jQuery( window ).on( 'load', () => { } ).done( ( response ) => { setTimeout( () => { distributorTopMenu.classList.remove( 'syncing' ); + overlayDiv.classList.remove( 'syncing' ); + + // Hide the overlay if a user moved out of the Distributor menu + hoverOut(); if ( ! response.success ) { doError( response.data ); @@ -396,6 +503,7 @@ jQuery( window ).on( 'load', () => { } ).error( ( xhr, textStatus, errorThrown ) => { setTimeout( () => { distributorTopMenu.classList.remove( 'syncing' ); + overlayDiv.classList.remove( 'syncing' ); doError( `${dt.messages.ajax_error} ${errorThrown}` ); }, 500 ); diff --git a/includes/syndicated-post-ui.php b/includes/syndicated-post-ui.php index 0083e607f..26e06aafa 100644 --- a/includes/syndicated-post-ui.php +++ b/includes/syndicated-post-ui.php @@ -564,7 +564,7 @@ function enqueue_gutenberg_edit_scripts() { } wp_enqueue_script( 'dt-gutenberg-syndicated-post', plugins_url( '/dist/js/gutenberg-syndicated-post.min.js', __DIR__ ), [ 'wp-blocks' ], DT_VERSION, true ); - wp_enqueue_script( 'dt-gutenberg-syndicated-status-plugin', plugins_url( '/dist/js/gutenberg-status-plugin.min.js', __DIR__ ), [ 'wp-blocks', 'wp-edit-post' ], DT_VERSION, true ); + wp_enqueue_script( 'dt-gutenberg-plugin', plugins_url( '/dist/js/gutenberg-plugin.min.js', __DIR__ ), [ 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-edit-post', 'wp-i18n', 'wp-plugins' ], DT_VERSION, true ); wp_localize_script( 'dt-gutenberg-syndicated-post', @@ -584,6 +584,9 @@ function enqueue_gutenberg_edit_scripts() { 'originalLocationName' => sanitize_text_field( $original_location_name ), 'unlinkNonceUrl' => wp_nonce_url( add_query_arg( 'action', 'unlink', admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) ), "unlink-post_{$post->ID}" ), 'linkNonceUrl' => wp_nonce_url( add_query_arg( 'action', 'link', admin_url( sprintf( $post_type_object->_edit_link, $post->ID ) ) ), "link-post_{$post->ID}" ), + 'supportedPostTypes' => \Distributor\Utils\distributable_post_types(), + 'supportedPostStati' => \Distributor\Utils\distributable_post_statuses(), + 'noPermissions' => ! is_user_logged_in() || ! current_user_can( apply_filters( 'dt_syndicatable_capabilities', 'edit_posts' ) ), ] ); } diff --git a/package-lock.json b/package-lock.json index 6ba485949..3bad17e9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9941,6 +9941,12 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "ramda": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz", + "integrity": "sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10554,6 +10560,12 @@ } } }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", + "dev": true + }, "rxjs": { "version": "6.5.5", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", @@ -11221,6 +11233,45 @@ "es6-symbol": "^3.1.1" } }, + "svg-react-loader": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/svg-react-loader/-/svg-react-loader-0.4.6.tgz", + "integrity": "sha512-HVEypjWQsQuJdBIPzXGxpmQsQts7QwfQuYgK1rah6BVCMoLNSCh/ESKVNd7/tHq8DkWYHHTyaUMDA1FjqZYrgA==", + "dev": true, + "requires": { + "css": "2.2.4", + "loader-utils": "1.1.0", + "ramda": "0.21.0", + "rx": "4.1.0", + "traverse": "0.6.6", + "xml2js": "0.4.17" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + } + } + }, "svgo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", @@ -11571,6 +11622,12 @@ "through2": "^2.0.3" } }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", + "dev": true + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", @@ -12384,6 +12441,25 @@ "mkdirp": "^0.5.1" } }, + "xml2js": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", + "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "^4.1.0" + } + }, + "xmlbuilder": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", + "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", + "dev": true, + "requires": { + "lodash": "^4.0.0" + } + }, "xmlcreate": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", diff --git a/package.json b/package.json index 89867e5ac..6e03424ff 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "pump": "^3.0.0", "require-dir": "^1.2.0", "run-sequence": "^2.2.1", + "svg-react-loader": "^0.4.6", "webpack": "^4.43.0", "webpack-stream": "^5.2.1", "wp-hookdoc": "^0.2.0" diff --git a/webpack.config.babel.js b/webpack.config.babel.js index 8bd1c99b3..c4c68c2ed 100644 --- a/webpack.config.babel.js +++ b/webpack.config.babel.js @@ -1,6 +1,6 @@ import path from 'path'; -const DIST_PATH = path.resolve('./dist/js'); +const DIST_PATH = path.resolve( './dist/js' ); module.exports = { cache: true, @@ -11,7 +11,7 @@ module.exports = { 'admin-distributed-post': './assets/js/admin-distributed-post.js', push: './assets/js/push.js', 'gutenberg-syndicated-post': './assets/js/gutenberg-syndicated-post.js', - 'gutenberg-status-plugin': './assets/js/gutenberg-status-plugin.js', + 'gutenberg-plugin': './assets/js/gutenberg-plugin.js', }, output: { path: DIST_PATH, @@ -34,16 +34,22 @@ module.exports = { query: { configFile: './.eslintrc.json' } - } + }, + { + test: /\.svg$/, + use: [{ + loader: 'svg-react-loader' + }] + }, ] }, mode: 'production', - externals: { jquery: 'jQuery', + react: 'React', underscores: '_', window: 'window', wp: 'wp' }, stats: { colors: true }, -} +};