diff options
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r-- | app/assets/javascripts/lib/dompurify.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 11 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/constants.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/dom_utils.js | 14 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/intersection_observer.js | 28 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/navigation_utility.js | 39 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/url_utility.js | 2 |
7 files changed, 95 insertions, 2 deletions
diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js index 47ede8cb1bb..47568f0ecff 100644 --- a/app/assets/javascripts/lib/dompurify.js +++ b/app/assets/javascripts/lib/dompurify.js @@ -3,7 +3,7 @@ import { getNormalizedURL, getBaseURL, relativePathToAbsolute } from '~/lib/util const defaultConfig = { // Safely allow SVG <use> tags - ADD_TAGS: ['use', 'gl-emoji'], + ADD_TAGS: ['use', 'gl-emoji', 'copy-code'], // Prevent possible XSS attacks with data-* attributes used by @rails/ujs // See https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1421 FORBID_ATTR: ['data-remote', 'data-url', 'data-type', 'data-method'], diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index a82dad7e2c9..7235b38848c 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -735,3 +735,14 @@ export const isFeatureFlagEnabled = (flag) => window.gon.features?.[flag]; export const convertArrayToCamelCase = (array) => array.map((i) => convertToCamelCase(i)); export const isLoggedIn = () => Boolean(window.gon?.current_user_id); + +/** + * This method takes in array of objects with snake_case + * property names and returns a new array of objects with + * camelCase property names + * + * @param {Array[Object]} array - Array to be converted + * @returns {Array[Object]} Converted array + */ +export const convertArrayOfObjectsToCamelCase = (array) => + array.map((o) => convertObjectPropsToCamelCase(o)); diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index 36c6545164e..a108b02bcbf 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -17,6 +17,7 @@ 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/dom_utils.js b/app/assets/javascripts/lib/utils/dom_utils.js index f7687a929de..b52a736f153 100644 --- a/app/assets/javascripts/lib/utils/dom_utils.js +++ b/app/assets/javascripts/lib/utils/dom_utils.js @@ -89,3 +89,17 @@ export const getParents = (element) => { return parents; }; + +/** + * This method takes a HTML element and an object of attributes + * to save repeated calls to `setAttribute` when multiple + * attributes need to be set. + * + * @param {HTMLElement} el + * @param {Object} attributes + */ +export const setAttributes = (el, attributes) => { + Object.keys(attributes).forEach((key) => { + el.setAttribute(key, attributes[key]); + }); +}; diff --git a/app/assets/javascripts/lib/utils/intersection_observer.js b/app/assets/javascripts/lib/utils/intersection_observer.js new file mode 100644 index 00000000000..0959df9a186 --- /dev/null +++ b/app/assets/javascripts/lib/utils/intersection_observer.js @@ -0,0 +1,28 @@ +import { memoize } from 'lodash'; + +import { uuids } from './uuids'; + +export const create = memoize((options = {}) => { + const id = uuids()[0]; + + return { + id, + observer: new IntersectionObserver((entries) => { + entries.forEach((entry) => { + entry.target.dispatchEvent( + new CustomEvent(`IntersectionUpdate`, { detail: { entry, observer: id } }), + ); + + if (entry.isIntersecting) { + entry.target.dispatchEvent( + new CustomEvent(`IntersectionAppear`, { detail: { observer: id } }), + ); + } else { + entry.target.dispatchEvent( + new CustomEvent(`IntersectionDisappear`, { detail: { observer: id } }), + ); + } + }); + }, options), + }; +}); diff --git a/app/assets/javascripts/lib/utils/navigation_utility.js b/app/assets/javascripts/lib/utils/navigation_utility.js index 1579b225e44..029e9f5fd9f 100644 --- a/app/assets/javascripts/lib/utils/navigation_utility.js +++ b/app/assets/javascripts/lib/utils/navigation_utility.js @@ -13,3 +13,42 @@ export default function findAndFollowLink(selector) { visitUrl(link); } } + +export function prefetchDocument(url) { + const newPrefetchLink = document.createElement('link'); + newPrefetchLink.rel = 'prefetch'; + newPrefetchLink.href = url; + newPrefetchLink.setAttribute('as', 'document'); + document.head.appendChild(newPrefetchLink); +} + +export function initPrefetchLinks(selector) { + document.querySelectorAll(selector).forEach((el) => { + let mouseOverTimer; + + const mouseOutHandler = () => { + if (mouseOverTimer) { + clearTimeout(mouseOverTimer); + mouseOverTimer = undefined; + } + }; + + const mouseOverHandler = () => { + el.addEventListener('mouseout', mouseOutHandler, { once: true, passive: true }); + + mouseOverTimer = setTimeout(() => { + if (el.href) prefetchDocument(el.href); + + // Only execute once + el.removeEventListener('mouseover', mouseOverHandler, true); + + mouseOverTimer = undefined; + }, 100); + }; + + el.addEventListener('mouseover', mouseOverHandler, { + capture: true, + passive: true, + }); + }); +} diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index e53a39cde06..12462a2575e 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -1,6 +1,6 @@ export const DASH_SCOPE = '-'; -const PATH_SEPARATOR = '/'; +export const PATH_SEPARATOR = '/'; const PATH_SEPARATOR_LEADING_REGEX = new RegExp(`^${PATH_SEPARATOR}+`); const PATH_SEPARATOR_ENDING_REGEX = new RegExp(`${PATH_SEPARATOR}+$`); const SHA_REGEX = /[\da-f]{40}/gi; |