diff options
Diffstat (limited to 'app/assets/javascripts/user_popovers.js')
-rw-r--r-- | app/assets/javascripts/user_popovers.js | 121 |
1 files changed, 72 insertions, 49 deletions
diff --git a/app/assets/javascripts/user_popovers.js b/app/assets/javascripts/user_popovers.js index 4544373d8aa..597b96ae18c 100644 --- a/app/assets/javascripts/user_popovers.js +++ b/app/assets/javascripts/user_popovers.js @@ -57,41 +57,35 @@ const populateUserInfo = (user) => { ); }; -const initializedPopovers = new Map(); -let domObservedForChanges = false; +function initPopover(el, user, mountPopover) { + const preloadedUserInfo = getPreloadedUserInfo(el.dataset); -const addPopoversToModifiedTree = new MutationObserver(() => { - const userLinks = document?.querySelectorAll('.js-user-link, .gfm-project_member'); + Object.assign(user, preloadedUserInfo); - if (userLinks) { - addPopovers(userLinks); /* eslint-disable-line no-use-before-define */ + if (preloadedUserInfo.userId) { + populateUserInfo(user); } -}); - -function observeBody() { - if (!domObservedForChanges) { - addPopoversToModifiedTree.observe(document.body, { - subtree: true, - childList: true, - }); - - domObservedForChanges = true; - } -} - -export default function addPopovers(elements = document.querySelectorAll('.js-user-link')) { - const userLinks = Array.from(elements); const UserPopoverComponent = Vue.extend(UserPopover); + const popoverInstance = new UserPopoverComponent({ + propsData: { + target: el, + user, + }, + }); + mountPopover(popoverInstance); + // wait for component to actually mount + setTimeout(() => { + // trigger an event to force tooltip to show + const event = new MouseEvent('mouseenter'); + event.isSelfTriggered = true; + el.dispatchEvent(event); + }); +} - observeBody(); - - return userLinks - .filter(({ dataset }) => dataset.user || dataset.userId) - .map((el) => { - if (initializedPopovers.has(el)) { - return initializedPopovers.get(el); - } - +function initPopovers(userLinks, mountPopover) { + userLinks + .filter(({ dataset, user }) => !user && (dataset.user || dataset.userId)) + .forEach((el) => { const user = { location: null, bio: null, @@ -99,31 +93,60 @@ export default function addPopovers(elements = document.querySelectorAll('.js-us status: null, loaded: false, }; - const renderedPopover = new UserPopoverComponent({ - propsData: { - target: el, - user, - }, + el.user = user; + const init = initPopover.bind(null, el, user, mountPopover); + el.addEventListener('mouseenter', init, { once: true }); + el.addEventListener('mouseenter', ({ target, isSelfTriggered }) => { + if (!isSelfTriggered) return; + removeTitle(target); }); + el.addEventListener('mouseleave', ({ target }) => { + target.removeAttribute('aria-describedby'); + }); + }); +} - initializedPopovers.set(el, renderedPopover); +const userLinkSelector = 'a.js-user-link, a.gfm-project_member'; - renderedPopover.$mount(); +const getUserLinkNodes = (node) => { + if (!('matches' in node)) return null; + if (node.matches(userLinkSelector)) return [node]; + return Array.from(node.querySelectorAll(userLinkSelector)); +}; - el.addEventListener('mouseenter', ({ target }) => { - removeTitle(target); - const preloadedUserInfo = getPreloadedUserInfo(target.dataset); +let observer; - Object.assign(user, preloadedUserInfo); +export default function addPopovers( + elements = document.querySelectorAll('.js-user-link'), + mountPopover = (popoverInstance) => popoverInstance.$mount(), +) { + const userLinks = Array.from(elements); - if (preloadedUserInfo.userId) { - populateUserInfo(user); - } - }); - el.addEventListener('mouseleave', ({ target }) => { - target.removeAttribute('aria-describedby'); - }); + initPopovers(userLinks, mountPopover); + + if (!observer) { + observer = new MutationObserver((mutationsList) => { + const newUserLinks = mutationsList + .filter((mutation) => mutation.type === 'childList' && mutation.addedNodes) + .reduce((acc, mutation) => { + const userLinkNodes = Array.from(mutation.addedNodes) + .flatMap(getUserLinkNodes) + .filter(Boolean); + acc.push(...userLinkNodes); + return acc; + }, []); + + if (newUserLinks.length !== 0) { + initPopovers(newUserLinks, mountPopover); + } + }); + observer.observe(document.body, { + subtree: true, + childList: true, + }); - return renderedPopover; + document.addEventListener('beforeunload', () => { + observer.disconnect(); }); + } } |