diff options
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r-- | app/assets/javascripts/lib/mermaid.js | 61 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 10 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/constants.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/resize_observer.js | 58 |
4 files changed, 129 insertions, 1 deletions
diff --git a/app/assets/javascripts/lib/mermaid.js b/app/assets/javascripts/lib/mermaid.js new file mode 100644 index 00000000000..d621c9ddf9e --- /dev/null +++ b/app/assets/javascripts/lib/mermaid.js @@ -0,0 +1,61 @@ +import mermaid from 'mermaid'; +import { getParameterByName } from '~/lib/utils/url_utility'; + +const setIframeRenderedSize = (h, w) => { + const { origin } = window.location; + window.parent.postMessage({ h, w }, origin); +}; + +const drawDiagram = (source) => { + const element = document.getElementById('app'); + const insertSvg = (svgCode) => { + element.innerHTML = svgCode; + + const height = parseInt(element.firstElementChild.getAttribute('height'), 10); + const width = parseInt(element.firstElementChild.style.maxWidth, 10); + setIframeRenderedSize(height, width); + }; + mermaid.mermaidAPI.render('mermaid', source, insertSvg); +}; + +const darkModeEnabled = () => getParameterByName('darkMode') === 'true'; + +const initMermaid = () => { + let theme = 'neutral'; + + if (darkModeEnabled()) { + theme = 'dark'; + } + + mermaid.initialize({ + // mermaid core options + mermaid: { + startOnLoad: false, + }, + // mermaidAPI options + theme, + flowchart: { + useMaxWidth: true, + htmlLabels: true, + }, + secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'htmlLabels'], + securityLevel: 'strict', + }); +}; + +const addListener = () => { + window.addEventListener( + 'message', + (event) => { + if (event.origin !== window.location.origin) { + return; + } + drawDiagram(event.data); + }, + false, + ); +}; + +addListener(); +initMermaid(); +export default {}; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 7235b38848c..eff00dff7a7 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -181,6 +181,7 @@ export const contentTop = () => { }, () => getOuterHeight('.merge-request-tabs'), () => getOuterHeight('.js-diff-files-changed'), + () => getOuterHeight('.issue-sticky-header.gl-fixed'), ({ desktop }) => { const diffsTabIsActive = window.mrTabs?.currentAction === 'diffs'; let size; @@ -746,3 +747,12 @@ export const isLoggedIn = () => Boolean(window.gon?.current_user_id); */ export const convertArrayOfObjectsToCamelCase = (array) => array.map((o) => convertObjectPropsToCamelCase(o)); + +export const getFirstPropertyValue = (data) => { + if (!data) return null; + + const [key] = Object.keys(data); + if (!key) return null; + + return data[key]; +}; diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index a108b02bcbf..36c6545164e 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -17,7 +17,6 @@ export const BV_HIDE_MODAL = 'bv::hide::modal'; export const BV_HIDE_TOOLTIP = 'bv::hide::tooltip'; export const BV_DROPDOWN_SHOW = 'bv::dropdown::show'; export const BV_DROPDOWN_HIDE = 'bv::dropdown::hide'; -export const BV_COLLAPSE_STATE = 'bv::collapse::state'; export const DEFAULT_TH_CLASSES = 'gl-bg-transparent! gl-border-b-solid! gl-border-b-gray-100! gl-p-5! gl-border-b-1!'; diff --git a/app/assets/javascripts/lib/utils/resize_observer.js b/app/assets/javascripts/lib/utils/resize_observer.js new file mode 100644 index 00000000000..e72c6fe1679 --- /dev/null +++ b/app/assets/javascripts/lib/utils/resize_observer.js @@ -0,0 +1,58 @@ +import { contentTop } from './common_utils'; + +const interactionEvents = ['mousedown', 'touchstart', 'keydown', 'wheel']; + +export function createResizeObserver() { + return new ResizeObserver((entries) => { + entries.forEach((entry) => { + entry.target.dispatchEvent(new CustomEvent(`ResizeUpdate`, { detail: { entry } })); + }); + }); +} + +// watches for change in size of a container element (e.g. for lazy-loaded images) +// and scroll the target element to the top of the content area +// stop watching after any user input. So if user opens sidebar or manually +// scrolls the page we don't hijack their scroll position +export function scrollToTargetOnResize({ + target = window.location.hash, + container = '#content-body', +} = {}) { + if (!target) return null; + + const ro = createResizeObserver(); + const containerEl = document.querySelector(container); + let interactionListenersAdded = false; + + function keepTargetAtTop() { + const anchorEl = document.querySelector(target); + + if (!anchorEl) return; + + const anchorTop = anchorEl.getBoundingClientRect().top + window.scrollY; + const top = anchorTop - contentTop(); + document.documentElement.scrollTo({ + top, + }); + + if (!interactionListenersAdded) { + interactionEvents.forEach((event) => + // eslint-disable-next-line no-use-before-define + document.addEventListener(event, removeListeners), + ); + interactionListenersAdded = true; + } + } + + function removeListeners() { + interactionEvents.forEach((event) => document.removeEventListener(event, removeListeners)); + + ro.unobserve(containerEl); + containerEl.removeEventListener('ResizeUpdate', keepTargetAtTop); + } + + containerEl.addEventListener('ResizeUpdate', keepTargetAtTop); + + ro.observe(containerEl); + return ro; +} |