PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / trunk
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts vtrunk
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
content-blocks-builder / src / utils / dom / utils.js

utils.js in Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts trunk, at src/utils/dom/utils.js

243 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Utilities for collape, modal, off-canvas
3 * Adapted from bootstrap
4 */
5
6 /**
7 * Constants
8 */
9 const MAX_UID = 1_000_000;
10 const TRANSITION_END = "transitionend";
11 const MILLISECONDS_MULTIPLIER = 1000;
12
13 /**
14 * Properly escape IDs selectors to handle weird IDs
15 * @param {string} selector
16 * @returns {string}
17 */
18 const parseSelector = (selector) => {
19 if (selector && window.CSS && window.CSS.escape) {
20 // document.querySelector needs escaping to handle IDs (html5+) containing for instance /
21 selector = selector.replace(
22 /#([^\s"#']+)/g,
23 (match, id) => `#${CSS.escape(id)}`,
24 );
25 }
26
27 return selector;
28 };
29
30 // Shout-out Angus Croll (https://goo.gl/pxwQGp)
31 const toType = (object) => {
32 if (object === null || object === undefined) {
33 return `${object}`;
34 }
35
36 return Object.prototype.toString
37 .call(object)
38 .match(/\s([a-z]+)/i)[1]
39 .toLowerCase();
40 };
41
42 /**
43 * Public Util API
44 */
45
46 const getUID = (prefix) => {
47 do {
48 prefix += Math.floor(Math.random() * MAX_UID);
49 } while (document.getElementById(prefix));
50
51 return prefix;
52 };
53
54 const noop = () => {};
55
56 const isElement = (object) => {
57 if (!object || typeof object !== "object") {
58 return false;
59 }
60
61 if (typeof object.jquery !== "undefined") {
62 object = object[0];
63 }
64
65 return typeof object.nodeType !== "undefined";
66 };
67
68 const getElement = (object) => {
69 // it's a jQuery object or a node element
70 if (isElement(object)) {
71 return object?.jquery ? object[0] : object;
72 }
73
74 if (typeof object === "string" && object.length > 0) {
75 return document.querySelector(parseSelector(object));
76 }
77
78 return null;
79 };
80
81 const triggerTransitionEnd = (element) => {
82 element.dispatchEvent(new Event(TRANSITION_END));
83 };
84
85 const getTransitionDurationFromElement = (element) => {
86 if (!element) {
87 return 0;
88 }
89
90 // Get transition-duration of the element
91 let { transitionDuration, transitionDelay } =
92 window.getComputedStyle(element);
93 const floatTransitionDuration = Number.parseFloat(transitionDuration);
94 const floatTransitionDelay = Number.parseFloat(transitionDelay);
95
96 // Return 0 if element or transition duration is not found
97 if (!floatTransitionDuration && !floatTransitionDelay) {
98 return 0;
99 }
100
101 // If multiple durations are defined, take the first
102 transitionDuration = transitionDuration.split(",")[0];
103 transitionDelay = transitionDelay.split(",")[0];
104 return (
105 (Number.parseFloat(transitionDuration) +
106 Number.parseFloat(transitionDelay)) *
107 MILLISECONDS_MULTIPLIER
108 );
109 };
110
111 /**
112 * Call a callback on transitionend event
113 *
114 * @param {Function} callback
115 * @param {Node} elem
116 * @param {Boolean} immediate
117 */
118 const executeAfterTransition = (callback, elem, immediate = false) => {
119 if (typeof callback === "function") {
120 if (immediate) {
121 callback();
122 return;
123 }
124
125 const durationPadding = 5;
126 const emulatedDuration =
127 getTransitionDurationFromElement(elem) + durationPadding;
128 let called = false;
129
130 const handler = ({ target }) => {
131 if (target !== elem) {
132 return;
133 }
134 called = true;
135 elem.removeEventListener(TRANSITION_END, handler);
136 execute(callback);
137 };
138
139 elem.addEventListener(TRANSITION_END, handler);
140
141 setTimeout(() => {
142 if (!called) {
143 triggerTransitionEnd(elem);
144 }
145 }, emulatedDuration);
146 }
147 };
148
149 /**
150 * Run a function
151 *
152 * @param {Function} callback
153 */
154 const execute = (callback) => {
155 if (typeof callback === "function") {
156 callback();
157 }
158 };
159
160 /**
161 * Trick to restart an element's animation
162 *
163 * @param {HTMLElement} element
164 * @return void
165 *
166 * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
167 */
168 const reflow = (element) => {
169 element.offsetHeight; // eslint-disable-line no-unused-expressions
170 };
171
172 const isRTL = () => document.documentElement.dir === "rtl";
173
174 const exposeComponent = (name, component) => {
175 window.CBB = window?.CBB ?? {};
176 window.CBB = { ...window.CBB, [name]: component };
177 };
178
179 const parseJSON = (str) => {
180 try {
181 return JSON.parse(
182 Object.assign(document.createElement("textarea"), {
183 innerHTML: str || "{}",
184 }).value,
185 );
186 } catch {
187 return {};
188 }
189 };
190
191 /**
192 * Make sure the logic run event the script is defered or delayed.
193 *
194 * @param {Function} fn
195 */
196 const domReady = (fn) =>
197 document.readyState !== "loading"
198 ? fn()
199 : document.addEventListener("DOMContentLoaded", fn);
200
201 const exposeHelper = (helper) => {
202 if (toType(helper) !== "object") {
203 console.error("The parameter must be an object");
204 }
205
206 let storedValue = window?.CBB?.Util ?? false;
207 storedValue =
208 toType(storedValue) === "object" ? { ...storedValue, ...helper } : helper;
209
210 exposeComponent("Util", storedValue);
211 };
212
213 // Expose helper methods
214 exposeHelper({
215 toType,
216 getUID,
217 noop,
218 isElement,
219 executeAfterTransition,
220 execute,
221 reflow,
222 isRTL,
223 parseJSON,
224 domReady,
225 exposeComponent,
226 });
227
228 export {
229 toType,
230 getUID,
231 noop,
232 isElement,
233 getElement,
234 executeAfterTransition,
235 execute,
236 reflow,
237 isRTL,
238 parseJSON,
239 domReady,
240 exposeComponent,
241 exposeHelper,
242 };
243