From 95080b1a6e3cfa0b43cf8808b2e6d75f787ff49f Mon Sep 17 00:00:00 2001 From: Lukasz Ostafin Date: Mon, 24 Nov 2025 12:55:56 +0100 Subject: [PATCH] IBX-10081: copy link on share dialog doesnt work on safari --- .../js/scripts/helpers/browser.helper.js | 21 +++++++++++- .../js/scripts/helpers/tooltips.helper.js | 34 ++++++++++++++----- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/bundle/Resources/public/js/scripts/helpers/browser.helper.js b/src/bundle/Resources/public/js/scripts/helpers/browser.helper.js index 52585424a6..bb64f56b1e 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/browser.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/browser.helper.js @@ -3,5 +3,24 @@ const isEdge = () => userAgent.includes('Edg'); // Edge previously had Edge but const isChrome = () => userAgent.includes('Chrome') && !isEdge(); const isFirefox = () => userAgent.includes('Firefox'); const isSafari = () => userAgent.includes('Safari') && !isChrome() && !isEdge(); +const checkIsClipboardWriteSupported = async () => { + if (!navigator.clipboard?.writeText) { + return false; + } -export { isChrome, isFirefox, isSafari, isEdge }; + const isClipboardWriteSupported = await checkGrantedPermissions('clipboard-write'); + + return isClipboardWriteSupported; +}; +const checkGrantedPermissions = async (permissionName) => { + try { + const result = await navigator.permissions.query({ name: permissionName }); + + return result.state === 'granted'; + } catch (error) { + console.warn(`Permission check failed for "${permissionName}":`, error.message); + return false; + } +}; + +export { isChrome, isFirefox, isSafari, isEdge, checkIsClipboardWriteSupported }; diff --git a/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js b/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js index ac71f512e4..c65e62b9ef 100644 --- a/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js +++ b/src/bundle/Resources/public/js/scripts/helpers/tooltips.helper.js @@ -4,6 +4,19 @@ import { getBootstrap } from './context.helper'; const { document: doc } = window; const TOOLTIPS_SELECTOR = '[title], [data-tooltip-title]'; +const TOOLTIPS_DEFAULTS_PARAMS = { + delay: { + show: 150, + hide: 75, + }, + placement: 'bottom', + trigger: 'hover', + useHtml: false, + template: (extraClass = '') => `
+
+
+
`, +}; const observerConfig = { childList: true, subtree: true, @@ -130,15 +143,21 @@ const getContainer = (tooltipNode) => { return container ?? doc.body; }; const initializeTooltip = (tooltipNode, hasEllipsisStyle) => { + const { + delay: defaultDelay, + placement: defaultPlacement, + trigger: defaultTrigger, + template: defaultTemplate, + } = TOOLTIPS_DEFAULTS_PARAMS; const { delayShow, delayHide } = tooltipNode.dataset; const delay = { - show: delayShow ? parseInt(delayShow, 10) : 150, - hide: delayHide ? parseInt(delayHide, 10) : 75, + show: delayShow ? parseInt(delayShow, 10) : defaultDelay.show, + hide: delayHide ? parseInt(delayHide, 10) : defaultDelay.hide, }; const { title } = tooltipNode; const extraClass = tooltipNode.dataset.tooltipExtraClass ?? ''; - const placement = tooltipNode.dataset.tooltipPlacement ?? 'bottom'; - const trigger = tooltipNode.dataset.tooltipTrigger ?? 'hover'; + const placement = tooltipNode.dataset.tooltipPlacement ?? defaultPlacement; + const trigger = tooltipNode.dataset.tooltipTrigger ?? defaultTrigger; const useHtml = tooltipNode.dataset.tooltipUseHtml !== undefined; const container = getContainer(tooltipNode); const iframe = document.querySelector(tooltipNode.dataset.tooltipIframeSelector); @@ -150,10 +169,7 @@ const initializeTooltip = (tooltipNode, hasEllipsisStyle) => { container, popperConfig: modifyPopperConfig.bind(null, iframe), html: useHtml, - template: `
-
-
-
`, + template: defaultTemplate(extraClass), }); if (isSafari()) { @@ -241,4 +257,4 @@ const observe = (baseElement = doc) => { observer.observe(baseElement, observerConfig); }; -export { parse, hideAll, observe }; +export { parse, hideAll, observe, TOOLTIPS_DEFAULTS_PARAMS };