diff --git a/.wp-env.json b/.wp-env.json index dafab87..b535397 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,5 +1,6 @@ { "plugins": [ "." - ] + ], + "testsEnvironment": false } diff --git a/README.md b/README.md index 23ff1d0..8781b9f 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,44 @@ add_filter( 'blockparty_post_sharing_breakpoint', function () { - **Parameters:** `int` — Breakpoint width in pixels. - **Default:** `600` -### Icon customization +### Share networks (fallback menu) -Default icons are exposed as CSS custom properties on `.wp-block-blockparty-post-sharing-button`: +When the Web Share API is unavailable, the Share button opens a menu of social networks. Themes and plugins can customize the list with: + +```php +add_filter( 'blockparty_post_sharing_networks', function ( $networks, $url, $title, $post_id ) { + // Remove WhatsApp. + $networks = array_values( + array_filter( + $networks, + static function ( $network ) { + return ( $network['id'] ?? '' ) !== 'whatsapp'; + } + ) + ); + + // Add Mastodon. + $networks[] = [ + 'id' => 'mastodon', + 'label' => 'Mastodon', + 'url' => 'https://mastodon.social/share?text=' . rawurlencode( $title . ' ' . $url ), + ]; + + return $networks; +}, 10, 4 ); +``` + +- **Filter name:** `blockparty_post_sharing_networks` +- **Parameters:** + - `array $networks` — List of networks (`id`, `label`, `url`) + - `string $url` — Post permalink + - `string $title` — Post title + - `int $post_id` — Post ID +- **Default:** Facebook, X, Bluesky, LinkedIn, WhatsApp + +### Icon and menu customization + +Default icons and the share fallback menu are exposed as CSS custom properties on `.wp-block-blockparty-post-sharing-button`: ```css .wp-block-blockparty-post-sharing-button { @@ -101,6 +136,30 @@ Default icons are exposed as CSS custom properties on `.wp-block-blockparty-post --wp-block-blockparty-post-sharing-button-check-icon: url( '/path/to/check.svg' ); --wp-block-blockparty-post-sharing-button-icon-size: 1.25rem; --wp-block-blockparty-post-sharing-button-icon-color: currentColor; + + /* Share fallback menu */ + --wp-block-blockparty-post-sharing-button-menu-offset: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-min-width: 12rem; + --wp-block-blockparty-post-sharing-button-menu-padding: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-gap: 0.25rem; + --wp-block-blockparty-post-sharing-button-menu-border-width: 1px; + --wp-block-blockparty-post-sharing-button-menu-border-style: solid; + --wp-block-blockparty-post-sharing-button-menu-border-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-border-radius: 0.25rem; + --wp-block-blockparty-post-sharing-button-menu-bg: #fff; + --wp-block-blockparty-post-sharing-button-menu-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12); + --wp-block-blockparty-post-sharing-button-menu-link-padding: 0.5rem 0.75rem; + --wp-block-blockparty-post-sharing-button-menu-link-border-radius: 0.125rem; + --wp-block-blockparty-post-sharing-button-menu-link-hover-bg: rgba(0, 0, 0, 0.06); + --wp-block-blockparty-post-sharing-button-menu-link-gap: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-icon-size: 1.25rem; + --wp-block-blockparty-post-sharing-button-menu-icon-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-facebook-icon: url( '/path/to/facebook.svg' ); + --wp-block-blockparty-post-sharing-button-menu-x-icon: url( '/path/to/x.svg' ); + --wp-block-blockparty-post-sharing-button-menu-bluesky-icon: url( '/path/to/bluesky.svg' ); + --wp-block-blockparty-post-sharing-button-menu-linkedin-icon: url( '/path/to/linkedin.svg' ); + --wp-block-blockparty-post-sharing-button-menu-whatsapp-icon: url( '/path/to/whatsapp.svg' ); } ``` @@ -121,7 +180,8 @@ blockparty-post-sharing/ │ └── style.scss # Frontend and editor styles ├── includes/ # PHP classes │ ├── BlockRenderer.php # Dynamic block rendering -│ └── ResponsiveDisplay.php # Responsive visibility rules +│ ├── ResponsiveDisplay.php # Responsive visibility rules +│ └── ShareNetworks.php # Share fallback networks (filterable) ├── build/ # Compiled assets (blocks-manifest.php, etc.) ├── languages/ # Translation files ├── .wordpress-org/blueprints/ # WordPress Playground blueprint diff --git a/blockparty-post-sharing.php b/blockparty-post-sharing.php index 140b904..285eb0e 100644 --- a/blockparty-post-sharing.php +++ b/blockparty-post-sharing.php @@ -22,7 +22,11 @@ } if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) { - include_once __DIR__ . '/vendor/autoload.php'; + require_once __DIR__ . '/vendor/autoload.php'; +} else { + require_once __DIR__ . '/includes/BlockRenderer.php'; + require_once __DIR__ . '/includes/ResponsiveDisplay.php'; + require_once __DIR__ . '/includes/ShareNetworks.php'; } define( 'BLOCKPARTY_POST_SHARING_VERSION', '1.0.0' ); diff --git a/includes/BlockRenderer.php b/includes/BlockRenderer.php index 9b40ef2..4768cc0 100644 --- a/includes/BlockRenderer.php +++ b/includes/BlockRenderer.php @@ -63,6 +63,9 @@ public static function render( $attributes ) { 'data-url' => esc_url( $url ), 'data-title' => esc_attr( $title ), 'data-copied-label' => esc_attr( $copied_label ), + 'data-networks' => wp_json_encode( + ShareNetworks::get_networks( $url, $title, (int) $post_id ) + ), ]; if ( ! empty( $wrapper_classes ) ) { diff --git a/includes/ShareNetworks.php b/includes/ShareNetworks.php new file mode 100644 index 0000000..0790bd6 --- /dev/null +++ b/includes/ShareNetworks.php @@ -0,0 +1,107 @@ + + */ + public static function get_networks( string $url, string $title = '', int $post_id = 0 ): array { + $encoded_url = rawurlencode( $url ); + $encoded_title = rawurlencode( $title ); + $encoded_text = rawurlencode( '' !== $title ? $title . ' ' . $url : $url ); + + $networks = [ + [ + 'id' => 'facebook', + 'label' => 'Facebook', + 'url' => 'https://www.facebook.com/sharer/sharer.php?u=' . $encoded_url, + ], + [ + 'id' => 'x', + 'label' => 'X', + 'url' => 'https://x.com/intent/post?url=' . $encoded_url . '&text=' . $encoded_title, + ], + [ + 'id' => 'bluesky', + 'label' => 'Bluesky', + 'url' => 'https://bsky.app/intent/compose?text=' . $encoded_text, + ], + [ + 'id' => 'linkedin', + 'label' => 'LinkedIn', + 'url' => 'https://www.linkedin.com/sharing/share-offsite/?url=' . $encoded_url, + ], + [ + 'id' => 'whatsapp', + 'label' => 'WhatsApp', + 'url' => 'https://api.whatsapp.com/send?text=' . $encoded_text, + ], + ]; + + /** + * Filter social networks used in the share fallback menu. + * + * Each network should provide: + * - id (string): Network slug used as a CSS modifier. + * - label (string): Visible network name. + * - url (string): Absolute share URL for the current post. + * + * @param array $networks Social network definitions. + * @param string $url Post permalink. + * @param string $title Post title. + * @param int $post_id Post ID. + */ + $networks = apply_filters( + 'blockparty_post_sharing_networks', + $networks, + $url, + $title, + $post_id + ); + + if ( ! is_array( $networks ) ) { + return []; + } + + return array_values( + array_filter( + array_map( [ self::class, 'sanitize_network' ], $networks ) + ) + ); + } + + /** + * Sanitize a single network definition. + * + * @param mixed $network Network definition. + * + * @return array{id: string, label: string, url: string}|null + */ + private static function sanitize_network( $network ): ?array { + if ( ! is_array( $network ) ) { + return null; + } + + $id = isset( $network['id'] ) ? sanitize_key( (string) $network['id'] ) : ''; + $label = isset( $network['label'] ) ? sanitize_text_field( (string) $network['label'] ) : ''; + $url = isset( $network['url'] ) ? esc_url_raw( (string) $network['url'] ) : ''; + + if ( '' === $id || '' === $label || '' === $url ) { + return null; + } + + return [ + 'id' => $id, + 'label' => $label, + 'url' => $url, + ]; + } +} diff --git a/package.json b/package.json index 1f7a698..43e5706 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "make-pot": "wp i18n make-pot . languages/blockparty-post-sharing.pot --exclude=\"src\" --domain=blockparty-post-sharing", "make-json": "wp i18n make-json languages/blockparty-post-sharing-fr_FR.po languages/ --no-purge", "start": "wp-scripts start --blocks-manifest", - "start:env": "wp-env start", - "stop:env": "wp-env stop" + "env:start": "wp-env start", + "env:stop": "wp-env stop" }, "dependencies": { "@wordpress/block-editor": "latest", diff --git a/src/blockparty-post-sharing/editor.scss b/src/blockparty-post-sharing/editor.scss index f4233ff..d60fa15 100644 --- a/src/blockparty-post-sharing/editor.scss +++ b/src/blockparty-post-sharing/editor.scss @@ -9,12 +9,9 @@ } &.is-display-preview-desktop.hide-copy-on-desktop &__copy, - &.is-display-preview-desktop.hide-share-on-desktop &__share { - display: none; - } - + &.is-display-preview-desktop.hide-share-on-desktop &__share, &.is-display-preview-mobile.hide-copy-on-mobile &__copy, &.is-display-preview-mobile.hide-share-on-mobile &__share { - display: none; + opacity: .5; } } diff --git a/src/blockparty-post-sharing/img/bluesky.svg b/src/blockparty-post-sharing/img/bluesky.svg new file mode 100644 index 0000000..0af4aa4 --- /dev/null +++ b/src/blockparty-post-sharing/img/bluesky.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/blockparty-post-sharing/img/facebook.svg b/src/blockparty-post-sharing/img/facebook.svg new file mode 100644 index 0000000..7e58df3 --- /dev/null +++ b/src/blockparty-post-sharing/img/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/blockparty-post-sharing/img/linkedin.svg b/src/blockparty-post-sharing/img/linkedin.svg new file mode 100644 index 0000000..8285a2d --- /dev/null +++ b/src/blockparty-post-sharing/img/linkedin.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/blockparty-post-sharing/img/whatsapp.svg b/src/blockparty-post-sharing/img/whatsapp.svg new file mode 100644 index 0000000..337fc34 --- /dev/null +++ b/src/blockparty-post-sharing/img/whatsapp.svg @@ -0,0 +1 @@ + diff --git a/src/blockparty-post-sharing/img/x.svg b/src/blockparty-post-sharing/img/x.svg new file mode 100644 index 0000000..c4e3179 --- /dev/null +++ b/src/blockparty-post-sharing/img/x.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/blockparty-post-sharing/style.scss b/src/blockparty-post-sharing/style.scss index d31a033..40cb4bf 100644 --- a/src/blockparty-post-sharing/style.scss +++ b/src/blockparty-post-sharing/style.scss @@ -13,6 +13,28 @@ --wp-block-blockparty-post-sharing-button-copy-icon: url("./img/link.svg"); --wp-block-blockparty-post-sharing-button-share-icon: url("./img/share.svg"); --wp-block-blockparty-post-sharing-button-check-icon: url("./img/check.svg"); + --wp-block-blockparty-post-sharing-button-menu-offset: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-min-width: 12rem; + --wp-block-blockparty-post-sharing-button-menu-padding: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-gap: 0.25rem; + --wp-block-blockparty-post-sharing-button-menu-border-width: 1px; + --wp-block-blockparty-post-sharing-button-menu-border-style: solid; + --wp-block-blockparty-post-sharing-button-menu-border-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-border-radius: 0.25rem; + --wp-block-blockparty-post-sharing-button-menu-bg: #fff; + --wp-block-blockparty-post-sharing-button-menu-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12); + --wp-block-blockparty-post-sharing-button-menu-link-padding: 0.5rem 0.75rem; + --wp-block-blockparty-post-sharing-button-menu-link-border-radius: 0.125rem; + --wp-block-blockparty-post-sharing-button-menu-link-hover-bg: rgba(0, 0, 0, 0.06); + --wp-block-blockparty-post-sharing-button-menu-link-gap: 0.5rem; + --wp-block-blockparty-post-sharing-button-menu-icon-size: 1.25rem; + --wp-block-blockparty-post-sharing-button-menu-icon-color: currentColor; + --wp-block-blockparty-post-sharing-button-menu-facebook-icon: url("./img/facebook.svg"); + --wp-block-blockparty-post-sharing-button-menu-x-icon: url("./img/x.svg"); + --wp-block-blockparty-post-sharing-button-menu-bluesky-icon: url("./img/bluesky.svg"); + --wp-block-blockparty-post-sharing-button-menu-linkedin-icon: url("./img/linkedin.svg"); + --wp-block-blockparty-post-sharing-button-menu-whatsapp-icon: url("./img/whatsapp.svg"); &__actions { display: flex; @@ -65,6 +87,108 @@ mask-image: var(--wp-block-blockparty-post-sharing-button-share-icon); } + &__share-wrap { + position: relative; + display: inline-flex; + } + + &__share-menu { + position: absolute; + z-index: 10; + top: calc(100% + var(--wp-block-blockparty-post-sharing-button-menu-offset, 0.5rem)); + left: 0; + min-width: var(--wp-block-blockparty-post-sharing-button-menu-min-width, 12rem); + padding: var(--wp-block-blockparty-post-sharing-button-menu-padding, 0.5rem); + border-width: var(--wp-block-blockparty-post-sharing-button-menu-border-width, 1px); + border-style: var(--wp-block-blockparty-post-sharing-button-menu-border-style, solid); + border-color: var(--wp-block-blockparty-post-sharing-button-menu-border-color, currentColor); + border-radius: var(--wp-block-blockparty-post-sharing-button-menu-border-radius, 0.25rem); + background-color: var(--wp-block-blockparty-post-sharing-button-menu-bg, #fff); + color: var(--wp-block-blockparty-post-sharing-button-menu-color, currentColor); + box-shadow: var( + --wp-block-blockparty-post-sharing-button-menu-shadow, + 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12) + ); + } + + &__share-menu[hidden] { + display: none; + } + + &__share-menu-list { + display: flex; + flex-direction: column; + gap: var(--wp-block-blockparty-post-sharing-button-menu-gap, 0.25rem); + margin: 0; + padding: 0; + list-style: none; + } + + &__share-menu-item { + margin: 0; + } + + &__share-menu-link { + display: flex; + align-items: center; + gap: var(--wp-block-blockparty-post-sharing-button-menu-link-gap, 0.5rem); + padding: var(--wp-block-blockparty-post-sharing-button-menu-link-padding, 0.5rem 0.75rem); + border-radius: var( + --wp-block-blockparty-post-sharing-button-menu-link-border-radius, + 0.125rem + ); + color: inherit; + text-decoration: none; + line-height: 1.25; + + &:hover, + &:focus-visible { + background-color: var( + --wp-block-blockparty-post-sharing-button-menu-link-hover-bg, + rgba(0, 0, 0, 0.06) + ); + outline: none; + } + } + + &__share-menu-icon { + display: block; + flex-shrink: 0; + width: var(--wp-block-blockparty-post-sharing-button-menu-icon-size, 1.25rem); + height: var(--wp-block-blockparty-post-sharing-button-menu-icon-size, 1.25rem); + background-color: var( + --wp-block-blockparty-post-sharing-button-menu-icon-color, + currentColor + ); + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + } + + &__share-menu-link.is-facebook &__share-menu-icon { + mask-image: var(--wp-block-blockparty-post-sharing-button-menu-facebook-icon); + } + + &__share-menu-link.is-x &__share-menu-icon { + mask-image: var(--wp-block-blockparty-post-sharing-button-menu-x-icon); + } + + &__share-menu-link.is-bluesky &__share-menu-icon { + mask-image: var(--wp-block-blockparty-post-sharing-button-menu-bluesky-icon); + } + + &__share-menu-link.is-linkedin &__share-menu-icon { + mask-image: var(--wp-block-blockparty-post-sharing-button-menu-linkedin-icon); + } + + &__share-menu-link.is-whatsapp &__share-menu-icon { + mask-image: var(--wp-block-blockparty-post-sharing-button-menu-whatsapp-icon); + } + + &__share-menu-label { + flex: 1 1 auto; + } + &__status { margin: 0.5rem 0 0; } diff --git a/src/blockparty-post-sharing/view.js b/src/blockparty-post-sharing/view.js index c869765..7da7280 100644 --- a/src/blockparty-post-sharing/view.js +++ b/src/blockparty-post-sharing/view.js @@ -1,7 +1,7 @@ /** * Front-end behavior for the post sharing block. */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; /** * Copy text to the clipboard with a fallback for older browsers. @@ -76,6 +76,236 @@ function showStatus( statusEl, message ) { }, 3000 ); } +/** + * Whether the Web Share API is available. + * + * @return {boolean} True when navigator.share is a function. + */ +function canUseNativeShare() { + return typeof navigator.share === 'function'; +} + +/** + * Parse share networks exposed by PHP on the block wrapper. + * + * @param {HTMLElement} block Block wrapper element. + * @return {Array<{id: string, label: string, url: string}>} Network definitions. + */ +function getShareNetworks( block ) { + const raw = block.dataset.networks; + + if ( ! raw ) { + return []; + } + + try { + const networks = JSON.parse( raw ); + + if ( ! Array.isArray( networks ) ) { + return []; + } + + return networks.filter( + ( network ) => + network && + typeof network.id === 'string' && + typeof network.label === 'string' && + typeof network.url === 'string' && + network.id && + network.label && + network.url + ); + } catch { + return []; + } +} + +/** + * Ensure the share button is wrapped for absolute tooltip positioning. + * + * @param {HTMLElement} shareButton Share button element. + * @return {HTMLElement} Wrapper element. + */ +function ensureShareWrap( shareButton ) { + const existing = shareButton.closest( + '.wp-block-blockparty-post-sharing-button__share-wrap' + ); + + if ( existing ) { + return existing; + } + + const wrap = document.createElement( 'div' ); + wrap.className = 'wp-block-blockparty-post-sharing-button__share-wrap'; + shareButton.parentNode.insertBefore( wrap, shareButton ); + wrap.appendChild( shareButton ); + + return wrap; +} + +/** + * Create the fallback share tooltip menu. + * + * @param {Array<{id: string, label: string, url: string}>} networks Network definitions. + * @param {Function} onClose Callback invoked when the menu should close. + * @return {HTMLElement} Menu element. + */ +function createShareFallbackMenu( networks, onClose ) { + const menu = document.createElement( 'div' ); + menu.className = 'wp-block-blockparty-post-sharing-button__share-menu'; + menu.setAttribute( 'role', 'menu' ); + menu.setAttribute( + 'aria-label', + __( 'Share on social networks', 'blockparty-post-sharing' ) + ); + menu.hidden = true; + + const list = document.createElement( 'ul' ); + list.className = 'wp-block-blockparty-post-sharing-button__share-menu-list'; + + networks.forEach( ( network ) => { + const item = document.createElement( 'li' ); + item.className = + 'wp-block-blockparty-post-sharing-button__share-menu-item'; + item.setAttribute( 'role', 'none' ); + + const link = document.createElement( 'a' ); + link.className = `wp-block-blockparty-post-sharing-button__share-menu-link is-${ network.id }`; + link.setAttribute( 'role', 'menuitem' ); + link.href = network.url; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + link.setAttribute( + 'aria-label', + sprintf( + /* translators: %s: social network name */ + __( 'Share on %s', 'blockparty-post-sharing' ), + network.label + ) + ); + link.addEventListener( 'click', () => { + onClose(); + } ); + + const icon = document.createElement( 'span' ); + icon.className = + 'wp-block-blockparty-post-sharing-button__share-menu-icon'; + icon.setAttribute( 'aria-hidden', 'true' ); + + const label = document.createElement( 'span' ); + label.className = + 'wp-block-blockparty-post-sharing-button__share-menu-label'; + label.textContent = network.label; + + link.appendChild( icon ); + link.appendChild( label ); + + item.appendChild( link ); + list.appendChild( item ); + } ); + + menu.appendChild( list ); + + return menu; +} + +/** + * Bind fallback share tooltip behavior when Web Share API is unavailable. + * + * @param {HTMLElement} shareButton Share button element. + * @param {HTMLElement} menu Fallback menu element. + * @return {Function} Close menu callback. + */ +function bindShareFallback( shareButton, menu ) { + const closeMenu = () => { + menu.hidden = true; + shareButton.setAttribute( 'aria-expanded', 'false' ); + }; + + const openMenu = () => { + menu.hidden = false; + shareButton.setAttribute( 'aria-expanded', 'true' ); + const firstItem = menu.querySelector( '[role="menuitem"]' ); + firstItem?.focus(); + }; + + const toggleMenu = () => { + if ( menu.hidden ) { + openMenu(); + } else { + closeMenu(); + } + }; + + shareButton.setAttribute( 'aria-haspopup', 'menu' ); + shareButton.setAttribute( 'aria-expanded', 'false' ); + shareButton.setAttribute( 'aria-controls', menu.id ); + + shareButton.addEventListener( 'click', ( event ) => { + event.preventDefault(); + event.stopPropagation(); + toggleMenu(); + } ); + + document.addEventListener( 'click', ( event ) => { + if ( menu.hidden ) { + return; + } + + if ( + ! menu.contains( event.target ) && + ! shareButton.contains( event.target ) + ) { + closeMenu(); + } + } ); + + document.addEventListener( 'keydown', ( event ) => { + if ( event.key === 'Escape' && ! menu.hidden ) { + closeMenu(); + shareButton.focus(); + } + } ); + + return closeMenu; +} + +/** + * Share via the native Web Share API. + * + * @param {string} url Post URL. + * @param {string} title Post title. + * @param {HTMLElement} statusEl Status element. + */ +async function shareNatively( url, title, statusEl ) { + try { + const shareData = { url }; + + if ( title ) { + shareData.title = title; + } + + if ( navigator.canShare && ! navigator.canShare( shareData ) ) { + showStatus( + statusEl, + __( 'Unable to share', 'blockparty-post-sharing' ) + ); + return; + } + + await navigator.share( shareData ); + } catch ( error ) { + if ( error?.name === 'AbortError' ) { + return; + } + + showStatus( + statusEl, + __( 'Unable to share', 'blockparty-post-sharing' ) + ); + } +} + /** * Initialize a single post sharing block instance. * @@ -96,10 +326,6 @@ function initBlock( block ) { block.dataset.copiedLabel || __( 'Link copied', 'blockparty-post-sharing' ); - if ( ! navigator.share && shareButton ) { - shareButton.hidden = true; - } - copyButton.addEventListener( 'click', async () => { try { await copyToClipboard( url ); @@ -112,36 +338,33 @@ function initBlock( block ) { } } ); - if ( shareButton && navigator.share ) { - shareButton.addEventListener( 'click', async () => { - try { - const shareData = { url }; - - if ( title ) { - shareData.title = title; - } - - if ( navigator.canShare && ! navigator.canShare( shareData ) ) { - showStatus( - status, - __( 'Unable to share', 'blockparty-post-sharing' ) - ); - return; - } - - await navigator.share( shareData ); - } catch ( error ) { - if ( error?.name === 'AbortError' ) { - return; - } - - showStatus( - status, - __( 'Unable to share', 'blockparty-post-sharing' ) - ); - } + if ( ! shareButton ) { + return; + } + + if ( canUseNativeShare() ) { + shareButton.addEventListener( 'click', () => { + shareNatively( url, title, status ); } ); + return; } + + const networks = getShareNetworks( block ); + + if ( ! networks.length ) { + return; + } + + const wrap = ensureShareWrap( shareButton ); + const menuId = `blockparty-post-sharing-menu-${ Math.random() + .toString( 36 ) + .slice( 2, 10 ) }`; + + let closeMenu = () => {}; + const menu = createShareFallbackMenu( networks, () => closeMenu() ); + menu.id = menuId; + wrap.appendChild( menu ); + closeMenu = bindShareFallback( shareButton, menu ); } document