@lesjoursfr/edith 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core/dom.js DELETED
@@ -1,480 +0,0 @@
1
- const dataNamespace = "edithData";
2
- /**
3
- * Convert a dashed string to camelCase
4
- */
5
- function dashedToCamel(string) {
6
- return string.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
7
- }
8
- /**
9
- * Check if an node is a tag element
10
- */
11
- function isTagElement(node, tag) {
12
- return isHTMLElement(node) && node.tagName === tag.toUpperCase();
13
- }
14
- /**
15
- * Check if a node is an HTML Element.
16
- * @param {Node} node the node to test
17
- * @returns {boolean} true if the node is an HTMLElement
18
- */
19
- export function isCommentNode(node) {
20
- return node.nodeType === Node.COMMENT_NODE;
21
- }
22
- /**
23
- * Check if a node is an HTML Element.
24
- * @param {Node} node the node to test
25
- * @returns {boolean} true if the node is an HTMLElement
26
- */
27
- export function isTextNode(node) {
28
- return node.nodeType === Node.TEXT_NODE;
29
- }
30
- /**
31
- * Check if a node is an HTML Element.
32
- * @param {Node} node the node to test
33
- * @returns {boolean} true if the node is an HTMLElement
34
- */
35
- export function isHTMLElement(node) {
36
- return node.nodeType === Node.ELEMENT_NODE;
37
- }
38
- /**
39
- * Create an HTMLElement from the HTML template.
40
- * @param {string} template the HTML template
41
- * @returns {HTMLElement} the created HTMLElement
42
- */
43
- export function createFromTemplate(template) {
44
- const range = document.createRange();
45
- range.selectNode(document.body);
46
- return range.createContextualFragment(template).children[0];
47
- }
48
- /**
49
- * Update the given CSS property.
50
- * If the value is `null` the property will be removed.
51
- * @param {HTMLElement} node the node to update
52
- * @param {string|{ [key: string]: string|null }} property multi-word property names are hyphenated (kebab-case) and not camel-cased.
53
- * @param {string|null} value (default to `null`)
54
- * @returns {HTMLElement} the element
55
- */
56
- export function updateCSS(node, property, value = null) {
57
- if (typeof property !== "string") {
58
- for (const [key, val] of Object.entries(property)) {
59
- val !== null ? node.style.setProperty(key, val) : node.style.removeProperty(key);
60
- }
61
- }
62
- else {
63
- value !== null ? node.style.setProperty(property, value) : node.style.removeProperty(property);
64
- }
65
- return node;
66
- }
67
- /**
68
- * Check if the node has the given attribute.
69
- * @param {HTMLElement} node
70
- * @param {string} attribute
71
- * @returns {boolean} true or false
72
- */
73
- export function hasAttribute(node, attribute) {
74
- return node.hasAttribute(attribute);
75
- }
76
- /**
77
- * Get the given attribute.
78
- * @param {HTMLElement} node
79
- * @param {string} attribute
80
- * @returns {string|null} the value
81
- */
82
- export function getAttribute(node, attribute) {
83
- return node.getAttribute(attribute);
84
- }
85
- /**
86
- * Set the given attribute.
87
- * If the value is `null` the attribute will be removed.
88
- * @param {HTMLElement} node
89
- * @param {string} attribute
90
- * @param {string|null} value
91
- * @returns {HTMLElement} the element
92
- */
93
- export function setAttribute(node, attribute, value) {
94
- if (value === null) {
95
- node.removeAttribute(attribute);
96
- }
97
- else {
98
- node.setAttribute(attribute, value);
99
- }
100
- return node;
101
- }
102
- /**
103
- * Get the given data.
104
- * This function does not change the DOM.
105
- * If there is no key this function return all data
106
- * @param {HTMLElement} node
107
- * @param {string|undefined} key
108
- * @returns {EdithData|string|null} the value
109
- */
110
- export function getData(node, key) {
111
- if (node[dataNamespace] === undefined) {
112
- node[dataNamespace] = {};
113
- for (const [k, v] of Object.entries(node.dataset)) {
114
- if (v === undefined) {
115
- continue;
116
- }
117
- node[dataNamespace][dashedToCamel(k)] = v;
118
- }
119
- }
120
- return key === undefined ? node[dataNamespace] : node[dataNamespace][dashedToCamel(key)] ?? null;
121
- }
122
- /**
123
- * Set the given data.
124
- * If the value is `null` the data will be removed.
125
- * This function does not change the DOM.
126
- * @param {HTMLElement} node
127
- * @param {string} key
128
- * @param {string|null} value
129
- * @returns {HTMLElement} the element
130
- */
131
- export function setData(node, key, value) {
132
- if (node[dataNamespace] === undefined) {
133
- node[dataNamespace] = {};
134
- }
135
- if (value === null) {
136
- delete node[dataNamespace][dashedToCamel(key)];
137
- }
138
- else {
139
- node[dataNamespace][dashedToCamel(key)] = value;
140
- }
141
- return node;
142
- }
143
- /**
144
- * Check if the node has the given tag name, or if its tag name is in the given list.
145
- * @param {HTMLElement} node the element to check
146
- * @param {string|Array<string>} tags a tag name or a list of tag name
147
- * @returns {boolean} true if the node has the given tag name
148
- */
149
- export function hasTagName(node, tags) {
150
- if (typeof tags === "string") {
151
- return node.tagName === tags.toUpperCase();
152
- }
153
- return tags.some((tag) => node.tagName === tag.toUpperCase());
154
- }
155
- /**
156
- * Check if the node has the given class name.
157
- * @param {HTMLElement} node the element to check
158
- * @param {string} className a class name
159
- * @returns {boolean} true if the node has the given class name
160
- */
161
- export function hasClass(node, className) {
162
- return node.classList.contains(className);
163
- }
164
- /**
165
- * Add the class to the node's class attribute.
166
- * @param {HTMLElement} node
167
- * @param {string|Array<string>} className
168
- * @returns {HTMLElement} the element
169
- */
170
- export function addClass(node, className) {
171
- if (typeof className === "string") {
172
- node.classList.add(className);
173
- }
174
- else {
175
- node.classList.add(...className);
176
- }
177
- return node;
178
- }
179
- /**
180
- * Remove the class from the node's class attribute.
181
- * @param {HTMLElement} node
182
- * @param {string|Array<string>} className
183
- * @returns {HTMLElement} the element
184
- */
185
- export function removeClass(node, className) {
186
- if (typeof className === "string") {
187
- node.classList.remove(className);
188
- }
189
- else {
190
- node.classList.remove(...className);
191
- }
192
- return node;
193
- }
194
- /**
195
- * Test if the node match the given selector.
196
- * @param {HTMLElement} node
197
- * @param {string} selector
198
- * @returns {boolean} true or false
199
- */
200
- export function is(node, selector) {
201
- return node.matches(selector);
202
- }
203
- /**
204
- * Get the node's offset.
205
- * @param {HTMLElement} node
206
- * @returns {{ top: number, left: number }} The node's offset
207
- */
208
- export function offset(node) {
209
- const rect = node.getBoundingClientRect();
210
- const win = node.ownerDocument.defaultView;
211
- return {
212
- top: rect.top + win.scrollY,
213
- left: rect.left + win.scrollX,
214
- };
215
- }
216
- /**
217
- * Create a new node.
218
- * @param {string} tag the tag name of the node
219
- * @param {object} options optional parameters
220
- * @param {string} options.innerHTML the HTML code of the node
221
- * @param {string} options.textContent the text content of the node
222
- * @param {object} options.attributes attributes of the node
223
- * @returns {HTMLElement} the created node
224
- */
225
- export function createNodeWith(tag, { innerHTML, textContent, attributes, } = {}) {
226
- const node = document.createElement(tag);
227
- if (attributes) {
228
- for (const key in attributes) {
229
- if (Object.hasOwnProperty.call(attributes, key)) {
230
- node.setAttribute(key, attributes[key]);
231
- }
232
- }
233
- }
234
- if (typeof innerHTML === "string") {
235
- node.innerHTML = innerHTML;
236
- }
237
- else if (typeof textContent === "string") {
238
- node.textContent = textContent;
239
- }
240
- return node;
241
- }
242
- /**
243
- * Replace a node.
244
- * @param {HTMLElement} node the node to replace
245
- * @param {HTMLElement} replacement the new node
246
- * @returns {HTMLElement} the new node
247
- */
248
- export function replaceNodeWith(node, replacement) {
249
- node.replaceWith(replacement);
250
- return replacement;
251
- }
252
- /**
253
- * Replace the node by its child nodes.
254
- * @param {HTMLElement} node the node to replace
255
- * @returns {Array<ChildNode>} its child nodes
256
- */
257
- export function unwrapNode(node) {
258
- const newNodes = [...node.childNodes];
259
- node.replaceWith(...newNodes);
260
- return newNodes;
261
- }
262
- /**
263
- * Replace the node by its text content.
264
- * @param {HTMLElement} node the node to replace
265
- * @returns {Text} the created Text node
266
- */
267
- export function textifyNode(node) {
268
- const newNode = document.createTextNode(node.textContent ?? "");
269
- node.replaceWith(newNode);
270
- return newNode;
271
- }
272
- /**
273
- * Know if a tag si a self-closing tag
274
- * @param {string} tagName
275
- * @returns {boolean}
276
- */
277
- export function isSelfClosing(tagName) {
278
- return [
279
- "AREA",
280
- "BASE",
281
- "BR",
282
- "COL",
283
- "EMBED",
284
- "HR",
285
- "IMG",
286
- "INPUT",
287
- "KEYGEN",
288
- "LINK",
289
- "META",
290
- "PARAM",
291
- "SOURCE",
292
- "TRACK",
293
- "WBR",
294
- ].includes(tagName);
295
- }
296
- /**
297
- * Remove all node's child nodes that pass the test implemented by the provided function.
298
- * @param {ChildNode} node the node to process
299
- * @param {Function} callbackFn the predicate
300
- */
301
- export function removeNodes(node, callbackFn) {
302
- for (const el of [...node.childNodes]) {
303
- if (callbackFn(el)) {
304
- el.remove();
305
- }
306
- }
307
- }
308
- /**
309
- * Remove recursively all node's child nodes that pass the test implemented by the provided function.
310
- * @param {ChildNode} node the node to process
311
- * @param {Function} callbackFn the predicate
312
- */
313
- export function removeNodesRecursively(node, callbackFn) {
314
- // Remove the node if it meets the condition
315
- if (callbackFn(node)) {
316
- node.remove();
317
- return;
318
- }
319
- // Loop through the node’s children
320
- for (const el of [...node.childNodes]) {
321
- // Execute the same function if it’s an element node
322
- removeNodesRecursively(el, callbackFn);
323
- }
324
- }
325
- /**
326
- * Remove all node's child nodes that are empty text nodes.
327
- * @param {ChildNode} node the node to process
328
- */
329
- export function removeEmptyTextNodes(node) {
330
- removeNodes(node, (el) => isTextNode(el) && (el.textContent === null || el.textContent.trim().length === 0));
331
- }
332
- /**
333
- * Remove all node's child nodes that are comment nodes.
334
- * @param {ChildNode} node the node to process
335
- */
336
- export function removeCommentNodes(node) {
337
- removeNodes(node, (el) => isCommentNode(el));
338
- }
339
- /**
340
- * Reset all node's attributes to the given list.
341
- * @param {HTMLElement} node the node
342
- * @param {object} targetAttributes the requested node's attributes
343
- */
344
- export function resetAttributesTo(node, targetAttributes) {
345
- for (const name of node.getAttributeNames()) {
346
- if (targetAttributes[name] === undefined) {
347
- node.removeAttribute(name);
348
- }
349
- }
350
- for (const name of Object.keys(targetAttributes)) {
351
- node.setAttribute(name, targetAttributes[name]);
352
- }
353
- }
354
- /**
355
- * Replace the node's style attribute by some regular nodes (<b>, <i>, <u> or <s>).
356
- * @param {HTMLElement} node the node to process
357
- * @returns {HTMLElement} the new node
358
- */
359
- export function replaceNodeStyleByTag(node) {
360
- // Get the style
361
- const styleAttr = node.getAttribute("style") || "";
362
- // Check if a tag is override by the style attribute
363
- if ((hasTagName(node, "b") && styleAttr.match(/font-weight\s*:\s*(normal|400);/)) ||
364
- (hasTagName(node, "i") && styleAttr.match(/font-style\s*:\s*normal;/)) ||
365
- (hasTagName(node, ["u", "s"]) && styleAttr.match(/text-decoration\s*:\s*none;/))) {
366
- node = replaceNodeWith(node, createNodeWith("span", { attributes: { style: styleAttr }, innerHTML: node.innerHTML }));
367
- }
368
- // Infer the tag from the style
369
- if (styleAttr.match(/font-weight\s*:\s*(bold|700|800|900);/)) {
370
- node = replaceNodeWith(node, createNodeWith("b", {
371
- innerHTML: `<span style="${styleAttr.replace(/font-weight\s*:\s*(bold|700|800|900);/, "")}">${node.innerHTML}</span>`,
372
- }));
373
- }
374
- else if (styleAttr.match(/font-style\s*:\s*italic;/)) {
375
- node = replaceNodeWith(node, createNodeWith("i", {
376
- innerHTML: `<span style="${styleAttr.replace(/font-style\s*:\s*italic;/, "")}">${node.innerHTML}</span>`,
377
- }));
378
- }
379
- else if (styleAttr.match(/text-decoration\s*:\s*underline;/)) {
380
- node = replaceNodeWith(node, createNodeWith("u", {
381
- innerHTML: `<span style="${styleAttr.replace(/text-decoration\s*:\s*underline;/, "")}">${node.innerHTML}</span>`,
382
- }));
383
- }
384
- else if (styleAttr.match(/text-decoration\s*:\s*line-through;/)) {
385
- node = replaceNodeWith(node, createNodeWith("s", {
386
- innerHTML: `<span style="${styleAttr.replace(/text-decoration\s*:\s*line-through;/, "")}">${node.innerHTML}</span>`,
387
- }));
388
- }
389
- // Return the node
390
- return node;
391
- }
392
- /**
393
- * Remove all leading & trailing node's child nodes that match the given tag.
394
- * @param {HTMLElement} node the node to process
395
- * @param {string} tag the tag
396
- */
397
- export function trimTag(node, tag) {
398
- // Children
399
- const children = node.childNodes;
400
- // Remove Leading
401
- while (children.length > 0 && isTagElement(children[0], tag)) {
402
- children[0].remove();
403
- }
404
- // Remove Trailing
405
- while (children.length > 0 && isTagElement(children[children.length - 1], tag)) {
406
- children[children.length - 1].remove();
407
- }
408
- }
409
- /**
410
- * Clean the DOM content of the node
411
- * @param {HTMLElement} root the node to process
412
- * @param {object} style active styles for the root
413
- */
414
- export function cleanDomContent(root, style) {
415
- // Iterate through children
416
- for (let el of [...root.children]) {
417
- // Check if the span is an edith-nbsp
418
- if (hasTagName(el, "span") && hasClass(el, "edith-nbsp")) {
419
- // Ensure that we have a clean element
420
- resetAttributesTo(el, { class: "edith-nbsp", contenteditable: "false" });
421
- el.innerHTML = "¶";
422
- continue;
423
- }
424
- // Check if there is a style attribute on the current node
425
- if (el.hasAttribute("style")) {
426
- // Replace the style attribute by tags
427
- el = replaceNodeStyleByTag(el);
428
- }
429
- // Check if the Tag Match a Parent Tag
430
- if (style[el.tagName]) {
431
- el = replaceNodeWith(el, createNodeWith("span", { attributes: { style: el.getAttribute("style") || "" }, innerHTML: el.innerHTML }));
432
- }
433
- // Save the Current Style Tag
434
- const newTags = { ...style };
435
- if (hasTagName(el, ["b", "i", "q", "u", "s"])) {
436
- newTags[el.tagName] = true;
437
- }
438
- // Clean Children
439
- cleanDomContent(el, newTags);
440
- // Keep only href & target attributes for <a> tags
441
- if (hasTagName(el, "a")) {
442
- const linkAttributes = {};
443
- if (el.hasAttribute("href")) {
444
- linkAttributes.href = el.getAttribute("href");
445
- }
446
- if (el.hasAttribute("target")) {
447
- linkAttributes.target = el.getAttribute("target");
448
- }
449
- resetAttributesTo(el, linkAttributes);
450
- continue;
451
- }
452
- // Remove all tag attributes for tags in the allowed list
453
- if (hasTagName(el, ["b", "i", "q", "u", "s", "br", "sup"])) {
454
- resetAttributesTo(el, {});
455
- continue;
456
- }
457
- // Remove useless tags
458
- if (hasTagName(el, ["style", "meta", "link"])) {
459
- el.remove();
460
- continue;
461
- }
462
- // Check if it's a <p> tag
463
- if (hasTagName(el, "p")) {
464
- // Check if the element contains text
465
- if (el.textContent === null || el.textContent.trim().length === 0) {
466
- // Remove the node
467
- el.remove();
468
- continue;
469
- }
470
- // Remove all tag attributes
471
- resetAttributesTo(el, {});
472
- // Remove leading & trailing <br>
473
- trimTag(el, "br");
474
- // Return
475
- continue;
476
- }
477
- // Unwrap the node
478
- unwrapNode(el);
479
- }
480
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Based on lodash version of throttle : https://github.com/lodash/lodash/blob/master/throttle.js
3
- */
4
- /**
5
- * Creates a debounced function that delays invoking `func` until after `wait`
6
- * milliseconds have elapsed since the last time the debounced function was
7
- * invoked, or until the next browser frame is drawn. Provide `options` to indicate
8
- * whether `func` should be invoked on the leading and/or trailing edge of the
9
- * `wait` timeout. The `func` is invoked with the last arguments provided to the
10
- * debounced function. Subsequent calls to the debounced function return the
11
- * result of the last `func` invocation.
12
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
13
- * invoked on the trailing edge of the timeout only if the debounced function
14
- * is invoked more than once during the `wait` timeout.
15
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
16
- * until the next tick, similar to `setTimeout` with a timeout of `0`.
17
- * @param {Function} func The function to debounce
18
- * @param {number} wait The number of milliseconds to delay
19
- * @param {Object} [options={}] The options object
20
- * @param {boolean} [options.leading=false] Specify invoking on the leading edge of the timeout
21
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing edge of the timeout
22
- * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's invoked
23
- * @returns {Function} Returns the new debounced function
24
- */
25
- declare function debounce<F extends (...args: any) => any>(func: F, wait: number, options?: {
26
- leading?: boolean;
27
- trailing?: boolean;
28
- maxWait?: number;
29
- }): (...args: Parameters<F>) => ReturnType<F>;
30
- /**
31
- * Creates a throttled function that only invokes `func` at most once per
32
- * every `wait` milliseconds (or once per browser frame). Provide `options` to indicate
33
- * whether `func` should be invoked on the leading and/or trailing edge of the
34
- * `wait` timeout. The `func` is invoked with the last arguments provided to the
35
- * throttled function. Subsequent calls to the throttled function return the
36
- * result of the last `func` invocation.
37
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
38
- * invoked on the trailing edge of the timeout only if the throttled function
39
- * is invoked more than once during the `wait` timeout.
40
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
41
- * until the next tick, similar to `setTimeout` with a timeout of `0`.
42
- * @param {Function} func The function to throttle
43
- * @param {number} wait The number of milliseconds to throttle invocations to
44
- * @param {Object} [options={}] The options object
45
- * @param {boolean} [options.leading=true] Specify invoking on the leading edge of the timeout
46
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing edge of the timeout
47
- * @returns {Function} Returns the new throttled function
48
- */
49
- declare function throttle<F extends (...args: any) => any>(func: F, wait: number, options?: {
50
- leading?: boolean;
51
- trailing?: boolean;
52
- }): (...args: Parameters<F>) => ReturnType<F>;
53
- export { debounce, throttle };
@@ -1,139 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /**
3
- * Based on lodash version of throttle : https://github.com/lodash/lodash/blob/master/throttle.js
4
- */
5
- /**
6
- * Creates a debounced function that delays invoking `func` until after `wait`
7
- * milliseconds have elapsed since the last time the debounced function was
8
- * invoked, or until the next browser frame is drawn. Provide `options` to indicate
9
- * whether `func` should be invoked on the leading and/or trailing edge of the
10
- * `wait` timeout. The `func` is invoked with the last arguments provided to the
11
- * debounced function. Subsequent calls to the debounced function return the
12
- * result of the last `func` invocation.
13
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
14
- * invoked on the trailing edge of the timeout only if the debounced function
15
- * is invoked more than once during the `wait` timeout.
16
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
17
- * until the next tick, similar to `setTimeout` with a timeout of `0`.
18
- * @param {Function} func The function to debounce
19
- * @param {number} wait The number of milliseconds to delay
20
- * @param {Object} [options={}] The options object
21
- * @param {boolean} [options.leading=false] Specify invoking on the leading edge of the timeout
22
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing edge of the timeout
23
- * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's invoked
24
- * @returns {Function} Returns the new debounced function
25
- */
26
- function debounce(func, wait, options = {}) {
27
- let lastArgs, lastThis, result, timerId, lastCallTime;
28
- let lastInvokeTime = 0;
29
- const leading = !!options.leading;
30
- const maxing = "maxWait" in options;
31
- const maxWait = maxing ? Math.max(options.maxWait || 0, wait) : undefined;
32
- const trailing = "trailing" in options ? !!options.trailing : true;
33
- function invokeFunc(time) {
34
- const args = lastArgs;
35
- const thisArg = lastThis;
36
- lastArgs = lastThis = undefined;
37
- lastInvokeTime = time;
38
- result = func.apply(thisArg, args);
39
- return result;
40
- }
41
- function startTimer(pendingFunc, wait) {
42
- return setTimeout(pendingFunc, wait);
43
- }
44
- function leadingEdge(time) {
45
- // Reset any `maxWait` timer.
46
- lastInvokeTime = time;
47
- // Start the timer for the trailing edge.
48
- timerId = startTimer(timerExpired, wait);
49
- // Invoke the leading edge.
50
- return leading ? invokeFunc(time) : result;
51
- }
52
- function remainingWait(time) {
53
- const timeSinceLastCall = time - lastCallTime;
54
- const timeSinceLastInvoke = time - lastInvokeTime;
55
- const timeWaiting = wait - timeSinceLastCall;
56
- return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting;
57
- }
58
- function shouldInvoke(time) {
59
- const timeSinceLastCall = time - lastCallTime;
60
- const timeSinceLastInvoke = time - lastInvokeTime;
61
- // Either this is the first call, activity has stopped and we're at the
62
- // trailing edge, the system time has gone backwards and we're treating
63
- // it as the trailing edge, or we've hit the `maxWait` limit.
64
- return (lastCallTime === undefined ||
65
- timeSinceLastCall >= wait ||
66
- timeSinceLastCall < 0 ||
67
- (maxing && timeSinceLastInvoke >= maxWait));
68
- }
69
- function timerExpired() {
70
- const time = Date.now();
71
- if (shouldInvoke(time)) {
72
- return trailingEdge(time);
73
- }
74
- // Restart the timer.
75
- timerId = startTimer(timerExpired, remainingWait(time));
76
- }
77
- function trailingEdge(time) {
78
- timerId = undefined;
79
- // Only invoke if we have `lastArgs` which means `func` has been
80
- // debounced at least once.
81
- if (trailing && lastArgs) {
82
- return invokeFunc(time);
83
- }
84
- lastArgs = lastThis = undefined;
85
- return result;
86
- }
87
- function debounced(...args) {
88
- const time = Date.now();
89
- const isInvoking = shouldInvoke(time);
90
- lastArgs = args;
91
- // eslint-disable-next-line @typescript-eslint/no-this-alias
92
- lastThis = this;
93
- lastCallTime = time;
94
- if (isInvoking) {
95
- if (timerId === undefined) {
96
- return leadingEdge(lastCallTime);
97
- }
98
- if (maxing) {
99
- // Handle invocations in a tight loop.
100
- timerId = startTimer(timerExpired, wait);
101
- return invokeFunc(lastCallTime);
102
- }
103
- }
104
- if (timerId === undefined) {
105
- timerId = startTimer(timerExpired, wait);
106
- }
107
- return result;
108
- }
109
- return debounced;
110
- }
111
- /**
112
- * Creates a throttled function that only invokes `func` at most once per
113
- * every `wait` milliseconds (or once per browser frame). Provide `options` to indicate
114
- * whether `func` should be invoked on the leading and/or trailing edge of the
115
- * `wait` timeout. The `func` is invoked with the last arguments provided to the
116
- * throttled function. Subsequent calls to the throttled function return the
117
- * result of the last `func` invocation.
118
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
119
- * invoked on the trailing edge of the timeout only if the throttled function
120
- * is invoked more than once during the `wait` timeout.
121
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
122
- * until the next tick, similar to `setTimeout` with a timeout of `0`.
123
- * @param {Function} func The function to throttle
124
- * @param {number} wait The number of milliseconds to throttle invocations to
125
- * @param {Object} [options={}] The options object
126
- * @param {boolean} [options.leading=true] Specify invoking on the leading edge of the timeout
127
- * @param {boolean} [options.trailing=true] Specify invoking on the trailing edge of the timeout
128
- * @returns {Function} Returns the new throttled function
129
- */
130
- function throttle(func, wait, options = {}) {
131
- const leading = "leading" in options ? !!options.leading : true;
132
- const trailing = "trailing" in options ? !!options.trailing : true;
133
- return debounce(func, wait, {
134
- leading,
135
- trailing,
136
- maxWait: wait,
137
- });
138
- }
139
- export { debounce, throttle };