From cb3ac16dc40e45ea6ec46d871af839df8d17f59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 18 Sep 2026 12:02:41 +0200 Subject: [PATCH 1/2] feat(PostSharing): implement post sharing component with copy link and native share functionality - Added a new `post-sharing.php` template for displaying sharing options. - Created `PostSharing.js` class to handle copy and share actions, including user feedback. - Introduced SCSS styles for the post sharing buttons and their states. - Updated language files to include new strings for sharing actions. - Added SVG icons for copy and share actions. - Integrated the new component into the main JavaScript and SCSS files. --- components/parts/common/post-sharing.php | 41 ++++++ languages/beapi-frontend-framework.pot | 54 ++++---- languages/fr_FR.mo | Bin 2536 -> 2247 bytes languages/fr_FR.po | 88 ++++++++----- src/img/icons/sprite/check-circle.svg | 1 + src/img/icons/sprite/link.svg | 1 + src/img/icons/sprite/logo-beapi.svg | 1 - src/img/icons/sprite/share.svg | 1 + src/js/classes/PostSharing.js | 148 ++++++++++++++++++++++ src/js/index.js | 1 + src/scss/05-components/_post-sharing.scss | 102 +++++++++++++++ src/scss/style.scss | 1 + 12 files changed, 380 insertions(+), 59 deletions(-) create mode 100644 components/parts/common/post-sharing.php create mode 100644 src/img/icons/sprite/check-circle.svg create mode 100644 src/img/icons/sprite/link.svg delete mode 100644 src/img/icons/sprite/logo-beapi.svg create mode 100644 src/img/icons/sprite/share.svg create mode 100644 src/js/classes/PostSharing.js create mode 100644 src/scss/05-components/_post-sharing.scss diff --git a/components/parts/common/post-sharing.php b/components/parts/common/post-sharing.php new file mode 100644 index 00000000..cf528621 --- /dev/null +++ b/components/parts/common/post-sharing.php @@ -0,0 +1,41 @@ + + +
+
+ + +
+ +
diff --git a/languages/beapi-frontend-framework.pot b/languages/beapi-frontend-framework.pot index 6d9bc44c..7de87f97 100644 --- a/languages/beapi-frontend-framework.pot +++ b/languages/beapi-frontend-framework.pot @@ -9,7 +9,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2026-05-07T14:30:39+00:00\n" +"POT-Creation-Date: 2026-09-18T09:57:30+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: beapi-frontend-framework\n" @@ -39,6 +39,26 @@ msgstr "" msgid "Breadcrumb" msgstr "" +#: components/parts/common/post-sharing.php:25 +msgid "Link copied!" +msgstr "" + +#: components/parts/common/post-sharing.php:26 +msgid "Unable to copy the link" +msgstr "" + +#: components/parts/common/post-sharing.php:27 +msgid "Unable to share" +msgstr "" + +#: components/parts/common/post-sharing.php:33 +msgid "Copy link" +msgstr "" + +#: components/parts/common/post-sharing.php:37 +msgid "Share" +msgstr "" + #: footer.php:2 msgid "Footer" msgstr "" @@ -92,6 +112,10 @@ msgstr "" msgid "Close menu" msgstr "" +#: inc/Helpers/Formatting/Datetime.php:53 +msgid "Published on" +msgstr "" + #: inc/Helpers/Formatting/Link.php:144 msgid "New window" msgstr "" @@ -120,46 +144,28 @@ msgstr "" msgid "Share by email" msgstr "" -#: inc/Services/Editor.php:144 +#: inc/Services/Editor.php:168 msgid "Small" msgstr "" -#: inc/Services/Editor.php:152 +#: inc/Services/Editor.php:176 msgid "Large" msgstr "" -#: inc/Services/Editor.php:160 +#: inc/Services/Editor.php:184 msgid "Huge" msgstr "" #. translators: %s: heading number -#: inc/Services/Editor.php:168 +#: inc/Services/Editor.php:192 #, php-format msgid "Style H%s" msgstr "" -#: inc/Services/Editor_Patterns.php:40 +#: inc/Services/Editor_Patterns.php:45 msgid "Common" msgstr "" -#. translators: %s: file name. -#: inc/Services/Editor_Patterns.php:125 -#, php-format -msgid "Could not register file \"%s\" as a block pattern (\"Slug\" field missing)" -msgstr "" - -#. translators: %1s: file name; %2s: slug value found. -#: inc/Services/Editor_Patterns.php:138 -#, php-format -msgid "Could not register file \"%1$s\" as a block pattern (invalid slug \"%2$s\")" -msgstr "" - -#. translators: %1s: file name; %2s: slug value found. -#: inc/Services/Editor_Patterns.php:155 -#, php-format -msgid "Could not register file \"%s\" as a block pattern (\"Title\" field missing)" -msgstr "" - #: inc/Services/Menu.php:37 msgid "Main menu" msgstr "" diff --git a/languages/fr_FR.mo b/languages/fr_FR.mo index c62c0ed6febb0d2f2fc9642e8a33d185010b4346..c22b928ad14aad957a055aef2c662069ccda976d 100644 GIT binary patch delta 1013 zcmY+?O=uHA7{>8wKFo)0HA&;QW@AwgYHdpeZ4V-zlpIn-=+V?H-Nl&1Bp%|S(TjNL zK^8$oK@fz1f=~oc77_JOJoF|WL_B#A6z`({XFDnmefKvr-I;mk-F(Tt&GbHIW48@$ zl-NZ~4H$C-OA#KlWYm~6rmQ14%(#FXa12v;6f=0j&d=gT#&bA`RqJKUGG4?T#`MfB zI@;(l#&8+q_#CzHg>@Bm(0km3pHYeZu=780h;f2VwqYK%ehjtW3@Y)HxEafs;e1n} zqm0_&gfTZz8{a`Ke281Ik2?4z-ls}ys0|xrFBh##s6_AC@gvmwXQ+hUp(^zO^PF$K z(oyLniLfN5fcqFvBY);R50%nGCDcLP^eSqjo2UftVjlZ;e#OqOSwEpJ@C|jrU)W31 zN%K$zayWwfQ5((R7M!(X55t?H5?Mke9-!WPiWDYnyRAY=>V35WF-j;A3aI<9-&(3{ zdkB3$C8Va3DO(+&-{elBNGMSysm8fs+s-pb==(avpQa?0xT>g6LBYccM#C8fdx=Ew zIx?APcAc7EzZk4WuML*yI2RgC->nvdGqGy6d}Y4ow>`J&H0tK8S82JylbCgB>aHjHcPI1zKbv-)`3=NA Bg1!I% literal 2536 zcmcJPO>7)B6vqus`PlMN0+jD!MN}jzlWYQ%uu%}r$EFe4WN8)@j@}t(*JQ>XY)?Xj zkT@W5*Mf~5)bT=uK1A@`c{`}t9?>)a~ zzl*~|=NKN2_fdLpV(cXGX8hr~09L@SOa1`f1^E(q3-~Lz1pW@z!P!HF{4RJ8MMjnk_l9p49}1z1%wbj`BtmH(8ttX_ z`&C+OtB&x>=)%sKV$qqbBiV|u_;2iAnG1Od^3h;5Ta`O}N9oAyu(ec{*78xmOwBUu zp0+vEkdYF+CExUHy(_Hb?OiSrW?YJhXfDpA+u9u{>LQeFW3~pGW$nD^Sdk1=E65RS z>w#v2ttTRmS<{-=W$1(DD0o#`hfUY`oaCpQ%e-#$+(a0NMD7^7#V5zB1SVQ)r0B5Jo2?EZLp|AgA8_R ze7dcCr5R^d#6jKI#MQV?bJ5LC&G2G4JF58$d~$52vJosvEo~8Q)1VBVKRrGT{enSl zk?$L3h1FHeqs#AnD6A4%vbwYhZZPg|e%`JVmwJ1;w}+_Ma*`U{xHcw6`1SU}7^evf z1?yrGeh zFO(rkdD%Y5RU-5onb7`!a#S{$gPvBD)UL>U81_DO+~V9uk~Ni#D5XstIMzVhYP+Cfk \ No newline at end of file diff --git a/src/img/icons/sprite/link.svg b/src/img/icons/sprite/link.svg new file mode 100644 index 00000000..1ba22c65 --- /dev/null +++ b/src/img/icons/sprite/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/icons/sprite/logo-beapi.svg b/src/img/icons/sprite/logo-beapi.svg deleted file mode 100644 index 5cf0fca4..00000000 --- a/src/img/icons/sprite/logo-beapi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/img/icons/sprite/share.svg b/src/img/icons/sprite/share.svg new file mode 100644 index 00000000..9adcdcc2 --- /dev/null +++ b/src/img/icons/sprite/share.svg @@ -0,0 +1 @@ + diff --git a/src/js/classes/PostSharing.js b/src/js/classes/PostSharing.js new file mode 100644 index 00000000..8b34553e --- /dev/null +++ b/src/js/classes/PostSharing.js @@ -0,0 +1,148 @@ +import AbstractDomElement from './AbstractDomElement' + +/** + * Post sharing — copy link (desktop) and native share (mobile). + * Button visibility is handled in CSS. + */ +class PostSharing extends AbstractDomElement { + constructor(element, options) { + const instance = super(element, options) + + // avoid double init : + if (!instance.isNewInstance()) { + return instance + } + + const el = this._element + const copyButton = el.querySelector('[data-action="copy"]') + const shareButton = el.querySelector('[data-action="share"]') + const status = el.querySelector('.post-sharing__status') + + if (!el.dataset.url || !copyButton || !status) { + return instance + } + + this._copyButton = copyButton + this._shareButton = shareButton + this._status = status + + copyButton.addEventListener('click', onClickCopy.bind(this)) + + // No Web Share API: CSS shows the copy button as mobile fallback. + if (!shareButton || typeof navigator.share !== 'function') { + el.classList.add('post-sharing--no-native-share') + return instance + } + + shareButton.addEventListener('click', onClickShare.bind(this)) + + return instance + } +} + +// ---- +// defaults +// ---- + +PostSharing.defaults = { + resetDelay: 3000, +} + +// ---- +// utils +// ---- + +/** + * Announce a message to screen readers, then clear it. + * + * @param {HTMLElement} el Status element. + * @param {string} message Message to announce. + * @param {number} delay Duration before clearing (ms). + */ +function showTemporaryMessage(el, message, delay) { + el.textContent = message + el.hidden = false + + window.setTimeout(() => { + el.textContent = '' + el.hidden = true + }, delay) +} + +/** + * Swap the copy button label and icon, then restore the default state. + * + * @param {HTMLButtonElement} copyButton + * @param {HTMLElement} statusEl + * @param {string} copiedLabel + * @param {number} delay + */ +function showCopySuccess(copyButton, statusEl, copiedLabel, delay) { + const labelEl = copyButton.querySelector('.post-sharing__label') + + if (!labelEl || copyButton.classList.contains('is-copied')) { + return + } + + const originalLabel = labelEl.textContent + + labelEl.textContent = copiedLabel + copyButton.classList.add('is-copied') + showTemporaryMessage(statusEl, copiedLabel, delay) + + window.setTimeout(() => { + labelEl.textContent = originalLabel + copyButton.classList.remove('is-copied') + }, delay) +} + +// ---- +// events +// ---- + +async function onClickCopy() { + const { url, copiedLabel, errorCopy } = this._element.dataset + const { resetDelay } = this._settings + + try { + await navigator.clipboard.writeText(url) + showCopySuccess(this._copyButton, this._status, copiedLabel, resetDelay) + } catch { + showTemporaryMessage(this._status, errorCopy, resetDelay) + } +} + +async function onClickShare() { + const { url, title, errorShare } = this._element.dataset + const { resetDelay } = this._settings + + try { + const shareData = { url } + + if (title) { + shareData.title = title + } + + if (navigator.canShare?.(shareData) === false) { + showTemporaryMessage(this._status, errorShare, resetDelay) + return + } + + await navigator.share(shareData) + } catch (error) { + // User dismissed the native share sheet — not an error. + if (error?.name !== 'AbortError') { + showTemporaryMessage(this._status, errorShare, resetDelay) + } + } +} + +// ---- +// init +// ---- +PostSharing.init('.post-sharing') + +// ---- +// export +// ---- +export default PostSharing diff --git a/src/js/index.js b/src/js/index.js index b2cfa361..dc67e40f 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -3,3 +3,4 @@ import './classes/ScrollDirection' import './classes/ButtonSeoClick' import './classes/Header' import './classes/Animation' +import './classes/PostSharing' diff --git a/src/scss/05-components/_post-sharing.scss b/src/scss/05-components/_post-sharing.scss new file mode 100644 index 00000000..422c3936 --- /dev/null +++ b/src/scss/05-components/_post-sharing.scss @@ -0,0 +1,102 @@ +@use "../01-abstract/theme-json" as *; +@use "../02-tools/m-breakpoint" as *; +@use "../02-tools/m-hover" as *; +@use "../02-tools/m-sr-only" as *; + +/** + * Post sharing — copy link (desktop) / native share (mobile). + * Without navigator.share, copy is also shown on small screens as fallback. + */ + +.post-sharing { + $el: &; + + &__actions { + display: flex; + flex-wrap: wrap; + align-items: center; + } + + &__button { + --post-sharing-color: #{$settings-color-black}; + --post-sharing-bg: #{$settings-color-grey-100}; + --post-sharing-hover-color: #{$settings-color-black}; + --post-sharing-hover-bg: #{$settings-color-grey-200}; + --post-sharing-icon-size: 20px; + + display: inline-flex; + gap: 8px; + align-items: center; + padding: 8px 12px; + color: var(--post-sharing-color); + cursor: pointer; + background-color: var(--post-sharing-bg); + border: 0; + border-radius: 50px; + transition: background-color .3s ease, color .3s ease; + appearance: none; + + @include hover { + color: var(--post-sharing-hover-color); + background-color: var(--post-sharing-hover-bg); + } + + &--copy { + &.is-copied { + #{$el}__icon--copy { + display: none; + } + + #{$el}__icon--check { + display: block; + } + } + + @include breakpoints(sm) { + display: inline-flex; + } + + @include breakpoints(sm, max) { + display: none; + } + } + + &--share { + @include breakpoints(sm) { + display: none; + } + + @include breakpoints(sm, max) { + display: inline-flex; + } + } + } + + &__icon { + width: var(--post-sharing-icon-size); + height: var(--post-sharing-icon-size); + + &--check { + display: none; + } + } + + &__status { + @include sr-only; + } + + // Fallback when Web Share API is unavailable: show copy on small screens. + &--no-native-share { + #{$el}__button--share { + @include breakpoints(sm, max) { + display: none; + } + } + + #{$el}__button--copy { + @include breakpoints(sm, max) { + display: inline-flex; + } + } + } +} diff --git a/src/scss/style.scss b/src/scss/style.scss index 486bce0a..cd29f267 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -49,6 +49,7 @@ variables.$entry-file-name: "style"; @use "05-components/btn"; @use "05-components/searchform"; @use "05-components/skip-links"; +@use "05-components/post-sharing"; /** * Gutenberg From 9923ea3f0f2b19d0750d5cd00e86579b8fa11c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Andrietti?= Date: Fri, 18 Sep 2026 12:17:54 +0200 Subject: [PATCH 2/2] fix(PostSharing): keep status live region in the accessibility tree Remove the hidden attribute so aria-live can announce feedback; rely on sr-only for visual hiding. --- components/parts/common/post-sharing.php | 2 +- src/js/classes/PostSharing.js | 4 ++-- src/scss/05-components/_post-sharing.scss | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/components/parts/common/post-sharing.php b/components/parts/common/post-sharing.php index cf528621..ba85b7d3 100644 --- a/components/parts/common/post-sharing.php +++ b/components/parts/common/post-sharing.php @@ -37,5 +37,5 @@ class="post-sharing" - +

diff --git a/src/js/classes/PostSharing.js b/src/js/classes/PostSharing.js index 8b34553e..3f32cec9 100644 --- a/src/js/classes/PostSharing.js +++ b/src/js/classes/PostSharing.js @@ -60,12 +60,12 @@ PostSharing.defaults = { * @param {number} delay Duration before clearing (ms). */ function showTemporaryMessage(el, message, delay) { + // Keep the live region in the accessibility tree (no `hidden`): + // `sr-only` already hides it visually; toggling `hidden` would block announcements. el.textContent = message - el.hidden = false window.setTimeout(() => { el.textContent = '' - el.hidden = true }, delay) } diff --git a/src/scss/05-components/_post-sharing.scss b/src/scss/05-components/_post-sharing.scss index 422c3936..89c6020e 100644 --- a/src/scss/05-components/_post-sharing.scss +++ b/src/scss/05-components/_post-sharing.scss @@ -1,7 +1,6 @@ @use "../01-abstract/theme-json" as *; @use "../02-tools/m-breakpoint" as *; @use "../02-tools/m-hover" as *; -@use "../02-tools/m-sr-only" as *; /** * Post sharing — copy link (desktop) / native share (mobile). @@ -81,10 +80,6 @@ } } - &__status { - @include sr-only; - } - // Fallback when Web Share API is unavailable: show copy on small screens. &--no-native-share { #{$el}__button--share {