@andrew_l/dom 0.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2024 Andrew L. <andrew.io.dev@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # DOM Toolkit <!-- omit in toc -->
2
+
3
+ ![license](https://img.shields.io/npm/l/%40andrew_l%2Fdom) <!-- omit in toc -->
4
+ ![npm version](https://img.shields.io/npm/v/%40andrew_l%2Fdom) <!-- omit in toc -->
5
+ ![npm bundle size](https://img.shields.io/bundlephobia/minzip/%40andrew_l%2Fdom) <!-- omit in toc -->
6
+
7
+ This package provides a set of utility functions for common DOM tasks, including handling animations, clipboard operations, and smooth scrolling. These utilities simplify JavaScript development, making it easier to implement dynamic, interactive features while improving performance and user experience.
8
+
9
+ [Documentation](https://men232.github.io/toolkit/reference/@andrew_l/dom/)
10
+
11
+ <!-- install placeholder -->
12
+
13
+ ## ✨ Features
14
+
15
+ - **Animation Handling**: Easily create and manage animations for dynamic UI effects.
16
+ - **Clipboard Management**: Simplify clipboard interactions, like copying and pasting.
17
+ - **Smooth Scrolling**: Implement smooth scrolling effects for better navigation and user experience.
18
+
19
+ ## 🤔 Why Use This Package?
20
+
21
+ - **Streamlined UI Development:** Makes it faster and easier to implement common UI features like animations, clipboard handling, and smooth scrolling.
22
+ - **Improved User Experience:** Smooth transitions and animations enhance the usability and interactivity of your web applications.
23
+ - **Performance-Focused:** Built with performance in mind to ensure your web app runs smoothly even with complex DOM operations.
package/dist/index.cjs ADDED
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ const toolkit = require('@andrew_l/toolkit');
4
+
5
+ let currentInstance;
6
+ function animateSingle(tick, instance) {
7
+ if (!instance) {
8
+ if (currentInstance && !currentInstance.isCancelled) {
9
+ currentInstance.isCancelled = true;
10
+ }
11
+ instance = { isCancelled: false };
12
+ currentInstance = instance;
13
+ }
14
+ if (!instance.isCancelled && tick()) {
15
+ toolkit.fastRaf(() => {
16
+ animateSingle(tick, instance);
17
+ });
18
+ }
19
+ }
20
+ function animate(tick) {
21
+ toolkit.fastRaf(() => {
22
+ if (tick()) {
23
+ animate(tick);
24
+ }
25
+ });
26
+ }
27
+ function animateInstantly(tick) {
28
+ if (tick()) {
29
+ toolkit.fastRaf(() => {
30
+ animateInstantly(tick);
31
+ });
32
+ }
33
+ }
34
+ const timingFunctions = {
35
+ linear: (t) => t,
36
+ easeIn: (t) => t ** 1.675,
37
+ easeOut: (t) => -1 * t ** 1.675,
38
+ easeInOut: (t) => 0.5 * (Math.sin((t - 0.5) * Math.PI) + 1),
39
+ easeInQuad: (t) => t * t,
40
+ easeOutQuad: (t) => t * (2 - t),
41
+ easeInOutQuad: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
42
+ easeInCubic: (t) => t ** 3,
43
+ easeOutCubic: (t) => --t * t * t + 1,
44
+ easeInOutCubic: (t) => t < 0.5 ? 4 * t ** 3 : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
45
+ easeInQuart: (t) => t ** 4,
46
+ easeOutQuart: (t) => 1 - --t * t ** 3,
47
+ easeInOutQuart: (t) => t < 0.5 ? 8 * t ** 4 : 1 - 8 * --t * t ** 3,
48
+ easeInQuint: (t) => t ** 5,
49
+ easeOutQuint: (t) => 1 + --t * t ** 4,
50
+ easeInOutQuint: (t) => t < 0.5 ? 16 * t ** 5 : 1 + 16 * --t * t ** 4
51
+ };
52
+ function animateNumber({
53
+ timing = timingFunctions.linear,
54
+ onUpdate,
55
+ duration,
56
+ onEnd,
57
+ from,
58
+ to
59
+ }) {
60
+ const t0 = Date.now();
61
+ let canceled = false;
62
+ animateInstantly(() => {
63
+ if (canceled) return false;
64
+ const t1 = Date.now();
65
+ let t = (t1 - t0) / duration;
66
+ if (t > 1) t = 1;
67
+ const progress = timing(t);
68
+ if (typeof from === "number" && typeof to === "number") {
69
+ onUpdate(from + (to - from) * progress);
70
+ } else if (Array.isArray(from) && Array.isArray(to)) {
71
+ const result = from.map((f, i) => f + (to[i] - f) * progress);
72
+ onUpdate(result);
73
+ }
74
+ if (t === 1 && onEnd) onEnd();
75
+ return t < 1;
76
+ });
77
+ return () => {
78
+ canceled = true;
79
+ if (onEnd) onEnd();
80
+ };
81
+ }
82
+
83
+ const defaultWindow = globalThis?.window;
84
+ const defaultDocument = globalThis?.document;
85
+ const defaultNavigator = globalThis?.navigator;
86
+
87
+ function copyTextToClipboard(text) {
88
+ if (defaultNavigator?.clipboard) {
89
+ return copyWithNavigator(text);
90
+ } else {
91
+ return copyWithFakeElement(text);
92
+ }
93
+ }
94
+ function copyWithNavigator(text) {
95
+ if (!defaultNavigator) return Promise.resolve(false);
96
+ return navigator.clipboard.writeText(text).then(() => true);
97
+ }
98
+ function copyWithFakeElement(text) {
99
+ return new Promise((resolve, reject) => {
100
+ if (!defaultDocument) return resolve(false);
101
+ if (!defaultWindow) return resolve(false);
102
+ const textareaEl = defaultDocument.createElement("textarea");
103
+ const range = defaultDocument.createRange();
104
+ textareaEl.value = text;
105
+ textareaEl.style.position = "fixed";
106
+ textareaEl.contentEditable = "true";
107
+ defaultDocument.body.appendChild(textareaEl);
108
+ textareaEl.focus();
109
+ textareaEl.select();
110
+ range.selectNodeContents(textareaEl);
111
+ const selection = defaultWindow.getSelection();
112
+ if (selection) {
113
+ selection.removeAllRanges();
114
+ selection.addRange(range);
115
+ }
116
+ textareaEl.setSelectionRange(0, 999999);
117
+ try {
118
+ const successful = defaultDocument.execCommand("copy");
119
+ if (successful) {
120
+ resolve(true);
121
+ } else {
122
+ reject(new Error("copy failed"));
123
+ }
124
+ } catch (error) {
125
+ reject(error);
126
+ }
127
+ if (selection) {
128
+ selection.removeAllRanges();
129
+ }
130
+ defaultDocument.body.removeChild(textareaEl);
131
+ });
132
+ }
133
+
134
+ const FAST_SMOOTH_MAX_DISTANCE = 1500;
135
+ const FAST_SMOOTH_MIN_DURATION = 250;
136
+ const FAST_SMOOTH_MAX_DURATION = 600;
137
+ const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 500;
138
+ var FocusDirection = /* @__PURE__ */ ((FocusDirection2) => {
139
+ FocusDirection2[FocusDirection2["Up"] = 0] = "Up";
140
+ FocusDirection2[FocusDirection2["Down"] = 1] = "Down";
141
+ FocusDirection2[FocusDirection2["Static"] = 2] = "Static";
142
+ return FocusDirection2;
143
+ })(FocusDirection || {});
144
+ function fastSmoothScroll(container, element, position, margin = 0, maxDistance = FAST_SMOOTH_MAX_DISTANCE, forceDirection, forceDuration, forceNormalContainerHeight, onComplete) {
145
+ const scrollFrom = calculateScrollFrom(
146
+ container,
147
+ element,
148
+ maxDistance,
149
+ forceDirection
150
+ );
151
+ if (forceDirection === 2 /* Static */) {
152
+ scrollWithJs(container, element, scrollFrom, position, margin, 0);
153
+ return;
154
+ }
155
+ scrollWithJs(
156
+ container,
157
+ element,
158
+ scrollFrom,
159
+ position,
160
+ margin,
161
+ forceDuration,
162
+ forceNormalContainerHeight,
163
+ onComplete
164
+ );
165
+ }
166
+ function calculateScrollFrom(container, element, maxDistance = FAST_SMOOTH_MAX_DISTANCE, forceDirection) {
167
+ const { offsetTop: elementTop } = element;
168
+ const { scrollTop } = container;
169
+ if (forceDirection === void 0) {
170
+ const offset = elementTop - container.scrollTop;
171
+ if (offset < -maxDistance) {
172
+ return scrollTop + (offset + maxDistance);
173
+ } else if (offset > maxDistance) {
174
+ return scrollTop + (offset - maxDistance);
175
+ }
176
+ } else if (forceDirection === 0 /* Up */) {
177
+ return elementTop + maxDistance;
178
+ } else if (forceDirection === 1 /* Down */) {
179
+ return Math.max(0, elementTop - maxDistance);
180
+ }
181
+ return scrollTop;
182
+ }
183
+ function scrollWithJs(container, element, scrollFrom, position, margin = 0, forceDuration, forceNormalContainerHeight, onComplete) {
184
+ const { offsetTop: elementTop, offsetHeight: elementHeight } = element;
185
+ const {
186
+ scrollTop: currentScrollTop,
187
+ offsetHeight: containerHeight,
188
+ scrollHeight
189
+ } = container;
190
+ const targetContainerHeight = forceNormalContainerHeight && container.dataset.normalHeight ? Number(container.dataset.normalHeight) : containerHeight;
191
+ if (currentScrollTop !== scrollFrom) {
192
+ container.scrollTop = scrollFrom;
193
+ }
194
+ let path;
195
+ switch (position) {
196
+ case "start":
197
+ path = elementTop - margin - scrollFrom;
198
+ break;
199
+ case "end":
200
+ path = elementTop + elementHeight + margin - (scrollFrom + targetContainerHeight);
201
+ break;
202
+ // 'nearest' is not supported yet
203
+ case "nearest":
204
+ case "center":
205
+ case "centerOrTop":
206
+ path = elementHeight < targetContainerHeight ? elementTop + elementHeight / 2 - (scrollFrom + targetContainerHeight / 2) : elementTop - margin - scrollFrom;
207
+ break;
208
+ }
209
+ if (path < 0) {
210
+ const remainingPath = -scrollFrom;
211
+ path = Math.max(path, remainingPath);
212
+ } else if (path > 0) {
213
+ const remainingPath = scrollHeight - (scrollFrom + targetContainerHeight);
214
+ path = Math.min(path, remainingPath);
215
+ }
216
+ if (path === 0) {
217
+ onComplete?.(container);
218
+ return;
219
+ }
220
+ const target = scrollFrom + path;
221
+ if (forceDuration === 0) {
222
+ container.scrollTop = target;
223
+ onComplete?.(container);
224
+ return;
225
+ }
226
+ const absPath = Math.abs(path);
227
+ const transition = absPath < FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition;
228
+ const duration = forceDuration || FAST_SMOOTH_MIN_DURATION + absPath / FAST_SMOOTH_MAX_DISTANCE * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION);
229
+ const startAt = Date.now();
230
+ toolkit.fastRaf(() => {
231
+ animateSingle(() => {
232
+ const t = Math.min((Date.now() - startAt) / duration, 1);
233
+ const currentPath = path * (1 - transition(t));
234
+ container.scrollTop = Math.round(target - currentPath);
235
+ if (t > 0) {
236
+ return true;
237
+ }
238
+ onComplete?.(container);
239
+ return false;
240
+ });
241
+ });
242
+ }
243
+ function longTransition(t) {
244
+ return 1 - (1 - t) ** 5;
245
+ }
246
+ function shortTransition(t) {
247
+ return 1 - (1 - t) ** 3.5;
248
+ }
249
+
250
+ function isScrolledToDown(target, threshold = 10) {
251
+ let scrollBottom;
252
+ if (target === defaultDocument) {
253
+ scrollBottom = (defaultDocument?.body?.offsetHeight || 0) - ((defaultWindow?.innerHeight || 0) + (defaultWindow?.scrollY || 0));
254
+ } else {
255
+ const el = target;
256
+ scrollBottom = el.scrollHeight - (el.offsetHeight + el.scrollTop);
257
+ }
258
+ return scrollBottom <= threshold;
259
+ }
260
+
261
+ function resetScroll(container, scrollTop) {
262
+ if (scrollTop !== void 0) {
263
+ container.scrollTop = scrollTop;
264
+ }
265
+ }
266
+
267
+ exports.FAST_SMOOTH_MAX_DISTANCE = FAST_SMOOTH_MAX_DISTANCE;
268
+ exports.FAST_SMOOTH_MAX_DURATION = FAST_SMOOTH_MAX_DURATION;
269
+ exports.FAST_SMOOTH_MIN_DURATION = FAST_SMOOTH_MIN_DURATION;
270
+ exports.FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE;
271
+ exports.FocusDirection = FocusDirection;
272
+ exports.animate = animate;
273
+ exports.animateInstantly = animateInstantly;
274
+ exports.animateNumber = animateNumber;
275
+ exports.animateSingle = animateSingle;
276
+ exports.copyTextToClipboard = copyTextToClipboard;
277
+ exports.fastSmoothScroll = fastSmoothScroll;
278
+ exports.isScrolledToDown = isScrolledToDown;
279
+ exports.resetScroll = resetScroll;
280
+ exports.timingFunctions = timingFunctions;
281
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/animation.ts","../src/environment.ts","../src/copyTextToClipboard.ts","../src/fastSmoothScroll.ts","../src/isScrolledToDown.ts","../src/resetScroll.ts"],"sourcesContent":["import { fastRaf } from '@andrew_l/toolkit';\n\ninterface AnimationInstance {\n isCancelled: boolean;\n}\n\nlet currentInstance: AnimationInstance | undefined;\n\n/**\n * Animates a single tick of an animation, running repeatedly until cancelled or the tick function returns `false`.\n *\n * If an `instance` is provided, it will control the cancellation state of the animation. Otherwise, a new animation\n * instance is created and assigned as the current instance. This function uses `fastRaf` to run the animation in the\n * next available frame.\n *\n * @param tick A function to be called on each frame, returning `true` to continue animating or `false` to stop.\n * @param instance An optional animation instance to control cancellation state.\n *\n * @example\n * animateSingle(() => {\n * // Your tick logic here (e.g., move an element)\n * return true; // Return `true` to keep animating, `false` to stop.\n * });\n *\n * @group Animation\n */\nexport function animateSingle(tick: Function, instance?: AnimationInstance) {\n if (!instance) {\n if (currentInstance && !currentInstance.isCancelled) {\n currentInstance.isCancelled = true;\n }\n\n instance = { isCancelled: false };\n currentInstance = instance;\n }\n\n if (!instance!.isCancelled && tick()) {\n fastRaf(() => {\n animateSingle(tick, instance);\n });\n }\n}\n\n/**\n * Continuously animates by repeatedly calling the `tick` function until it returns `false`.\n *\n * The function uses `fastRaf` to run the animation and continues until the `tick` function no longer returns `true`.\n *\n * @param tick The tick function to be executed on each frame. If it returns `false`, the animation stops.\n *\n * @example\n * animate(() => {\n * // Your animation logic here\n * return true; // Return `true` to continue animating, `false` to stop.\n * });\n *\n * @group Animation\n */\nexport function animate(tick: Function) {\n fastRaf(() => {\n if (tick()) {\n animate(tick);\n }\n });\n}\n\n/**\n * Instantly animates by calling the `tick` function in rapid succession until it returns `false`.\n *\n * This is intended for scenarios where the animation should run as quickly as possible, without any delay.\n *\n * @param tick The tick function to be executed on each frame. If it returns `false`, the animation stops.\n *\n * @example\n * animateInstantly(() => {\n * // Your instant animation logic here\n * return true; // Return `true` to continue animating, `false` to stop.\n * });\n *\n * @group Animation\n */\nexport function animateInstantly(tick: Function) {\n if (tick()) {\n fastRaf(() => {\n animateInstantly(tick);\n });\n }\n}\n\nexport type TimingFn = (t: number) => number;\n\nexport type AnimateNumberProps = {\n to: number | number[];\n from: number | number[];\n duration: number;\n onUpdate: (value: any) => void;\n timing?: TimingFn;\n onEnd?: () => void;\n};\n\nexport const timingFunctions = {\n linear: (t: number) => t,\n easeIn: (t: number) => t ** 1.675,\n easeOut: (t: number) => -1 * t ** 1.675,\n easeInOut: (t: number) => 0.5 * (Math.sin((t - 0.5) * Math.PI) + 1),\n easeInQuad: (t: number) => t * t,\n easeOutQuad: (t: number) => t * (2 - t),\n easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),\n easeInCubic: (t: number) => t ** 3,\n easeOutCubic: (t: number) => --t * t * t + 1,\n easeInOutCubic: (t: number) =>\n t < 0.5 ? 4 * t ** 3 : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,\n easeInQuart: (t: number) => t ** 4,\n easeOutQuart: (t: number) => 1 - --t * t ** 3,\n easeInOutQuart: (t: number) => (t < 0.5 ? 8 * t ** 4 : 1 - 8 * --t * t ** 3),\n easeInQuint: (t: number) => t ** 5,\n easeOutQuint: (t: number) => 1 + --t * t ** 4,\n easeInOutQuint: (t: number) =>\n t < 0.5 ? 16 * t ** 5 : 1 + 16 * --t * t ** 4,\n};\n\n/**\n * Animates a numeric value from `from` to `to` over a specified duration, applying an optional timing function.\n *\n * The animation will continuously update the value using the `onUpdate` callback until the animation is complete.\n * Once the animation ends, the `onEnd` callback will be called, if provided.\n *\n * @param timing The timing function that controls the progression of the animation. Defaults to `linear`.\n * @param onUpdate A callback that receives the updated value (or values) on each animation frame.\n * @param duration The duration of the animation in milliseconds.\n * @param onEnd An optional callback to be called when the animation completes.\n * @param from The starting value(s) of the animation.\n * @param to The target value(s) of the animation.\n *\n * @returns A function that can be called to cancel the animation.\n *\n * @example\n * animateNumber({\n * from: 0,\n * to: 100,\n * duration: 1000,\n * onUpdate: (value) => {\n * console.log(value); // Updated value on each frame\n * },\n * onEnd: () => {\n * console.log('Animation complete!');\n * },\n * });\n *\n * @group Animation\n */\nexport function animateNumber({\n timing = timingFunctions.linear,\n onUpdate,\n duration,\n onEnd,\n from,\n to,\n}: AnimateNumberProps) {\n const t0 = Date.now();\n let canceled = false;\n\n animateInstantly(() => {\n if (canceled) return false;\n const t1 = Date.now();\n let t = (t1 - t0) / duration;\n if (t > 1) t = 1;\n const progress = timing(t);\n if (typeof from === 'number' && typeof to === 'number') {\n onUpdate(from + (to - from) * progress);\n } else if (Array.isArray(from) && Array.isArray(to)) {\n const result = from.map((f, i) => f + (to[i] - f) * progress);\n onUpdate(result);\n }\n if (t === 1 && onEnd) onEnd();\n return t < 1;\n });\n\n return () => {\n canceled = true;\n if (onEnd) onEnd();\n };\n}\n","export const defaultWindow = /* #__PURE__ */ (globalThis as any)?.window as\n | Window\n | undefined;\n\nexport const defaultDocument = /* #__PURE__ */ (globalThis as any)?.document as\n | Document\n | undefined;\n\nexport const defaultNavigator = /* #__PURE__ */ (globalThis as any)\n ?.navigator as Navigator | undefined;\n\nexport const defaultLocation = /* #__PURE__ */ (globalThis as any)?.window\n ?.location as Location | undefined;\n","import {\n defaultDocument,\n defaultNavigator,\n defaultWindow,\n} from './environment';\n\n/**\n * Copies the provided text to the system clipboard.\n *\n * The function attempts to use the `navigator.clipboard` API if available. If not,\n * it falls back to using a fake element approach to copy the text.\n *\n * @param text The text to be copied to the clipboard.\n * @returns A promise that resolves to `true` if the text was successfully copied,\n * or `false` if the operation failed.\n *\n * @example\n * copyTextToClipboard('Hello, World!')\n * .then(success => {\n * if (success) {\n * console.log('Text copied to clipboard!');\n * } else {\n * console.log('Failed to copy text.');\n * }\n * });\n *\n * @group Clipboard\n */\nexport function copyTextToClipboard(text: string): Promise<boolean> {\n if (defaultNavigator?.clipboard) {\n return copyWithNavigator(text);\n } else {\n return copyWithFakeElement(text);\n }\n}\n\nfunction copyWithNavigator(text: string): Promise<boolean> {\n if (!defaultNavigator) return Promise.resolve(false);\n\n return navigator.clipboard.writeText(text).then(() => true);\n}\n\nfunction copyWithFakeElement(text: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n if (!defaultDocument) return resolve(false);\n if (!defaultWindow) return resolve(false);\n\n const textareaEl = defaultDocument.createElement('textarea');\n const range = defaultDocument.createRange();\n\n textareaEl.value = text;\n textareaEl.style.position = 'fixed'; // Avoid scrolling to bottom\n textareaEl.contentEditable = 'true';\n\n defaultDocument.body.appendChild(textareaEl);\n\n textareaEl.focus();\n textareaEl.select();\n\n range.selectNodeContents(textareaEl);\n\n const selection = defaultWindow.getSelection();\n if (selection) {\n selection.removeAllRanges();\n selection.addRange(range);\n }\n\n textareaEl.setSelectionRange(0, 999999);\n\n try {\n const successful = defaultDocument.execCommand('copy');\n if (successful) {\n resolve(true);\n } else {\n reject(new Error('copy failed'));\n }\n } catch (error) {\n reject(error);\n }\n\n if (selection) {\n selection.removeAllRanges();\n }\n\n defaultDocument.body.removeChild(textareaEl);\n });\n}\n","import { fastRaf } from '@andrew_l/toolkit';\nimport { animateSingle } from './animation';\n\nexport const FAST_SMOOTH_MAX_DISTANCE = 1500;\nexport const FAST_SMOOTH_MIN_DURATION = 250;\nexport const FAST_SMOOTH_MAX_DURATION = 600;\nexport const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 500; // px\n\nexport type ScrollCompleteCallback = (target: HTMLElement) => void;\n\nexport enum FocusDirection {\n Up,\n Down,\n Static,\n}\n\n/**\n * Smoothly scrolls the specified container to bring the given element into view,\n * with optional customization for scroll behavior, direction, and duration.\n *\n * This function calculates the optimal scroll position based on the given parameters\n * and smoothly scrolls the container to that position. It can adjust the scroll\n * behavior for accessibility, animation duration, and other constraints.\n *\n * @param container The container element that will be scrolled.\n * @param element The element inside the container that needs to be brought into view.\n * @param position The scroll position logic (`'centerOrTop'`, `ScrollLogicalPosition`).\n * @param margin A margin in pixels to be applied around the element when scrolling into view (default is `0`).\n * @param maxDistance The maximum distance for smooth scrolling (default is `FAST_SMOOTH_MAX_DISTANCE`).\n * @param forceDirection Optional, the direction to force the scroll (`FocusDirection`). If not provided, the default scroll direction is calculated.\n * @param forceDuration Optional, a custom duration (in milliseconds) for the smooth scroll. If not provided, a default duration is used.\n * @param forceNormalContainerHeight Optional, a flag indicating whether to force the container height calculation for normal behavior.\n * @param onComplete Optional callback function that is triggered when the scroll operation completes.\n *\n * @example\n * fastSmoothScroll(container, element, 'centerOrTop', 10, 500, FocusDirection.Up, 300);\n *\n * @group Scrolling\n */\nexport function fastSmoothScroll(\n container: HTMLElement,\n element: HTMLElement,\n position: ScrollLogicalPosition | 'centerOrTop',\n margin = 0,\n maxDistance = FAST_SMOOTH_MAX_DISTANCE,\n forceDirection?: FocusDirection,\n forceDuration?: number,\n forceNormalContainerHeight?: boolean,\n onComplete?: ScrollCompleteCallback,\n) {\n const scrollFrom = calculateScrollFrom(\n container,\n element,\n maxDistance,\n forceDirection,\n );\n\n if (forceDirection === FocusDirection.Static) {\n scrollWithJs(container, element, scrollFrom, position, margin, 0);\n return;\n }\n\n scrollWithJs(\n container,\n element,\n scrollFrom,\n position,\n margin,\n forceDuration,\n forceNormalContainerHeight,\n onComplete,\n );\n}\n\nfunction calculateScrollFrom(\n container: HTMLElement,\n element: HTMLElement,\n maxDistance = FAST_SMOOTH_MAX_DISTANCE,\n forceDirection?: FocusDirection,\n) {\n const { offsetTop: elementTop } = element;\n const { scrollTop } = container;\n\n if (forceDirection === undefined) {\n const offset = elementTop - container.scrollTop;\n\n if (offset < -maxDistance) {\n return scrollTop + (offset + maxDistance);\n } else if (offset > maxDistance) {\n return scrollTop + (offset - maxDistance);\n }\n } else if (forceDirection === FocusDirection.Up) {\n return elementTop + maxDistance;\n } else if (forceDirection === FocusDirection.Down) {\n return Math.max(0, elementTop - maxDistance);\n }\n\n return scrollTop;\n}\n\nfunction scrollWithJs(\n container: HTMLElement,\n element: HTMLElement,\n scrollFrom: number,\n position: ScrollLogicalPosition | 'centerOrTop',\n margin = 0,\n forceDuration?: number,\n forceNormalContainerHeight?: boolean,\n onComplete?: ScrollCompleteCallback,\n) {\n const { offsetTop: elementTop, offsetHeight: elementHeight } = element;\n const {\n scrollTop: currentScrollTop,\n offsetHeight: containerHeight,\n scrollHeight,\n } = container;\n const targetContainerHeight =\n forceNormalContainerHeight && container.dataset.normalHeight\n ? Number(container.dataset.normalHeight)\n : containerHeight;\n\n if (currentScrollTop !== scrollFrom) {\n container.scrollTop = scrollFrom;\n }\n\n let path!: number;\n\n switch (position) {\n case 'start':\n path = elementTop - margin - scrollFrom;\n break;\n case 'end':\n path =\n elementTop +\n elementHeight +\n margin -\n (scrollFrom + targetContainerHeight);\n break;\n // 'nearest' is not supported yet\n case 'nearest':\n case 'center':\n case 'centerOrTop':\n path =\n elementHeight < targetContainerHeight\n ? elementTop +\n elementHeight / 2 -\n (scrollFrom + targetContainerHeight / 2)\n : elementTop - margin - scrollFrom;\n break;\n }\n\n if (path < 0) {\n const remainingPath = -scrollFrom;\n path = Math.max(path, remainingPath);\n } else if (path > 0) {\n const remainingPath = scrollHeight - (scrollFrom + targetContainerHeight);\n path = Math.min(path, remainingPath);\n }\n\n if (path === 0) {\n onComplete?.(container);\n return;\n }\n\n const target = scrollFrom + path;\n\n if (forceDuration === 0) {\n container.scrollTop = target;\n onComplete?.(container);\n return;\n }\n\n const absPath = Math.abs(path);\n const transition =\n absPath < FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE\n ? shortTransition\n : longTransition;\n const duration =\n forceDuration ||\n FAST_SMOOTH_MIN_DURATION +\n (absPath / FAST_SMOOTH_MAX_DISTANCE) *\n (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION);\n const startAt = Date.now();\n\n fastRaf(() => {\n animateSingle(() => {\n const t = Math.min((Date.now() - startAt) / duration, 1);\n const currentPath = path * (1 - transition(t));\n\n container.scrollTop = Math.round(target - currentPath);\n\n if (t > 0) {\n return true;\n }\n\n onComplete?.(container);\n return false;\n });\n });\n}\n\nfunction longTransition(t: number) {\n return 1 - (1 - t) ** 5;\n}\n\nfunction shortTransition(t: number) {\n return 1 - (1 - t) ** 3.5;\n}\n","import { defaultDocument, defaultWindow } from './environment';\n\n/**\n * Determines if the specified element or the document/window is scrolled near the bottom.\n *\n * This function checks if the scroll position has reached the bottom of the target element or window\n * within a specified threshold. It is commonly used for implementing \"infinite scroll\" or detecting\n * when a user has scrolled to the bottom of a page or container.\n *\n * @param target The target element, document, or window to check the scroll position.\n * Can be an `HTMLElement`, `Document`, or `Window`.\n * @param threshold The threshold (in pixels) below which the target is considered \"scrolled to the bottom.\"\n * Default is `10`, meaning the target is considered scrolled to the bottom if it is within 10px of the bottom.\n *\n * @returns `true` if the target is scrolled near the bottom within the given threshold, otherwise `false`.\n *\n * @example\n * // Check if the window is scrolled near the bottom\n * isScrolledToDown(window, 20); // Returns true if within 20px of the bottom of the window.\n *\n * @example\n * // Check if a specific container is scrolled near the bottom\n * const container = document.getElementById('myContainer');\n * isScrolledToDown(container, 50); // Returns true if within 50px of the bottom of the container.\n *\n * @group Scrolling\n */\nexport function isScrolledToDown(\n target: HTMLElement | Document | Window,\n threshold: number = 10,\n) {\n let scrollBottom: number;\n\n if (target === defaultDocument) {\n scrollBottom =\n (defaultDocument?.body?.offsetHeight || 0) -\n ((defaultWindow?.innerHeight || 0) + (defaultWindow?.scrollY || 0));\n } else {\n const el = target as HTMLElement;\n scrollBottom = el.scrollHeight - (el.offsetHeight + el.scrollTop);\n }\n\n return scrollBottom <= threshold;\n}\n","/**\n * Resets the scroll position of the specified container element.\n *\n * If a `scrollTop` value is provided, the container's scroll position is set\n * to that value. Otherwise, the function will leave the scroll position unchanged.\n * This function is useful for resetting the scroll state in a container, such as\n * when the content changes or after an animation.\n *\n * @param container The container element whose scroll position needs to be reset.\n * @param scrollTop Optional value to set the container's `scrollTop`. If not provided, the scroll position remains unchanged.\n *\n * @example\n * resetScroll(container, 0); // Resets scroll to top of the container.\n * resetScroll(container); // Resets scroll position without changing it.\n *\n * @group Scrolling\n */\nexport function resetScroll(container: HTMLDivElement, scrollTop?: number) {\n // if (IS_IOS) {\n // \tcontainer.style.overflow = 'hidden';\n // }\n\n if (scrollTop !== undefined) {\n container.scrollTop = scrollTop;\n }\n\n // if (IS_IOS) {\n // \tcontainer.style.overflow = '';\n // }\n}\n"],"names":["fastRaf","FocusDirection"],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ interface AnimationInstance {
2
+ isCancelled: boolean;
3
+ }
4
+ /**
5
+ * Animates a single tick of an animation, running repeatedly until cancelled or the tick function returns `false`.
6
+ *
7
+ * If an `instance` is provided, it will control the cancellation state of the animation. Otherwise, a new animation
8
+ * instance is created and assigned as the current instance. This function uses `fastRaf` to run the animation in the
9
+ * next available frame.
10
+ *
11
+ * @param tick A function to be called on each frame, returning `true` to continue animating or `false` to stop.
12
+ * @param instance An optional animation instance to control cancellation state.
13
+ *
14
+ * @example
15
+ * animateSingle(() => {
16
+ * // Your tick logic here (e.g., move an element)
17
+ * return true; // Return `true` to keep animating, `false` to stop.
18
+ * });
19
+ *
20
+ * @group Animation
21
+ */
22
+ declare function animateSingle(tick: Function, instance?: AnimationInstance): void;
23
+ /**
24
+ * Continuously animates by repeatedly calling the `tick` function until it returns `false`.
25
+ *
26
+ * The function uses `fastRaf` to run the animation and continues until the `tick` function no longer returns `true`.
27
+ *
28
+ * @param tick The tick function to be executed on each frame. If it returns `false`, the animation stops.
29
+ *
30
+ * @example
31
+ * animate(() => {
32
+ * // Your animation logic here
33
+ * return true; // Return `true` to continue animating, `false` to stop.
34
+ * });
35
+ *
36
+ * @group Animation
37
+ */
38
+ declare function animate(tick: Function): void;
39
+ /**
40
+ * Instantly animates by calling the `tick` function in rapid succession until it returns `false`.
41
+ *
42
+ * This is intended for scenarios where the animation should run as quickly as possible, without any delay.
43
+ *
44
+ * @param tick The tick function to be executed on each frame. If it returns `false`, the animation stops.
45
+ *
46
+ * @example
47
+ * animateInstantly(() => {
48
+ * // Your instant animation logic here
49
+ * return true; // Return `true` to continue animating, `false` to stop.
50
+ * });
51
+ *
52
+ * @group Animation
53
+ */
54
+ declare function animateInstantly(tick: Function): void;
55
+ type TimingFn = (t: number) => number;
56
+ type AnimateNumberProps = {
57
+ to: number | number[];
58
+ from: number | number[];
59
+ duration: number;
60
+ onUpdate: (value: any) => void;
61
+ timing?: TimingFn;
62
+ onEnd?: () => void;
63
+ };
64
+ declare const timingFunctions: {
65
+ linear: (t: number) => number;
66
+ easeIn: (t: number) => number;
67
+ easeOut: (t: number) => number;
68
+ easeInOut: (t: number) => number;
69
+ easeInQuad: (t: number) => number;
70
+ easeOutQuad: (t: number) => number;
71
+ easeInOutQuad: (t: number) => number;
72
+ easeInCubic: (t: number) => number;
73
+ easeOutCubic: (t: number) => number;
74
+ easeInOutCubic: (t: number) => number;
75
+ easeInQuart: (t: number) => number;
76
+ easeOutQuart: (t: number) => number;
77
+ easeInOutQuart: (t: number) => number;
78
+ easeInQuint: (t: number) => number;
79
+ easeOutQuint: (t: number) => number;
80
+ easeInOutQuint: (t: number) => number;
81
+ };
82
+ /**
83
+ * Animates a numeric value from `from` to `to` over a specified duration, applying an optional timing function.
84
+ *
85
+ * The animation will continuously update the value using the `onUpdate` callback until the animation is complete.
86
+ * Once the animation ends, the `onEnd` callback will be called, if provided.
87
+ *
88
+ * @param timing The timing function that controls the progression of the animation. Defaults to `linear`.
89
+ * @param onUpdate A callback that receives the updated value (or values) on each animation frame.
90
+ * @param duration The duration of the animation in milliseconds.
91
+ * @param onEnd An optional callback to be called when the animation completes.
92
+ * @param from The starting value(s) of the animation.
93
+ * @param to The target value(s) of the animation.
94
+ *
95
+ * @returns A function that can be called to cancel the animation.
96
+ *
97
+ * @example
98
+ * animateNumber({
99
+ * from: 0,
100
+ * to: 100,
101
+ * duration: 1000,
102
+ * onUpdate: (value) => {
103
+ * console.log(value); // Updated value on each frame
104
+ * },
105
+ * onEnd: () => {
106
+ * console.log('Animation complete!');
107
+ * },
108
+ * });
109
+ *
110
+ * @group Animation
111
+ */
112
+ declare function animateNumber({ timing, onUpdate, duration, onEnd, from, to, }: AnimateNumberProps): () => void;
113
+
114
+ /**
115
+ * Copies the provided text to the system clipboard.
116
+ *
117
+ * The function attempts to use the `navigator.clipboard` API if available. If not,
118
+ * it falls back to using a fake element approach to copy the text.
119
+ *
120
+ * @param text The text to be copied to the clipboard.
121
+ * @returns A promise that resolves to `true` if the text was successfully copied,
122
+ * or `false` if the operation failed.
123
+ *
124
+ * @example
125
+ * copyTextToClipboard('Hello, World!')
126
+ * .then(success => {
127
+ * if (success) {
128
+ * console.log('Text copied to clipboard!');
129
+ * } else {
130
+ * console.log('Failed to copy text.');
131
+ * }
132
+ * });
133
+ *
134
+ * @group Clipboard
135
+ */
136
+ declare function copyTextToClipboard(text: string): Promise<boolean>;
137
+
138
+ declare const FAST_SMOOTH_MAX_DISTANCE = 1500;
139
+ declare const FAST_SMOOTH_MIN_DURATION = 250;
140
+ declare const FAST_SMOOTH_MAX_DURATION = 600;
141
+ declare const FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE = 500;
142
+ type ScrollCompleteCallback = (target: HTMLElement) => void;
143
+ declare enum FocusDirection {
144
+ Up = 0,
145
+ Down = 1,
146
+ Static = 2
147
+ }
148
+ /**
149
+ * Smoothly scrolls the specified container to bring the given element into view,
150
+ * with optional customization for scroll behavior, direction, and duration.
151
+ *
152
+ * This function calculates the optimal scroll position based on the given parameters
153
+ * and smoothly scrolls the container to that position. It can adjust the scroll
154
+ * behavior for accessibility, animation duration, and other constraints.
155
+ *
156
+ * @param container The container element that will be scrolled.
157
+ * @param element The element inside the container that needs to be brought into view.
158
+ * @param position The scroll position logic (`'centerOrTop'`, `ScrollLogicalPosition`).
159
+ * @param margin A margin in pixels to be applied around the element when scrolling into view (default is `0`).
160
+ * @param maxDistance The maximum distance for smooth scrolling (default is `FAST_SMOOTH_MAX_DISTANCE`).
161
+ * @param forceDirection Optional, the direction to force the scroll (`FocusDirection`). If not provided, the default scroll direction is calculated.
162
+ * @param forceDuration Optional, a custom duration (in milliseconds) for the smooth scroll. If not provided, a default duration is used.
163
+ * @param forceNormalContainerHeight Optional, a flag indicating whether to force the container height calculation for normal behavior.
164
+ * @param onComplete Optional callback function that is triggered when the scroll operation completes.
165
+ *
166
+ * @example
167
+ * fastSmoothScroll(container, element, 'centerOrTop', 10, 500, FocusDirection.Up, 300);
168
+ *
169
+ * @group Scrolling
170
+ */
171
+ declare function fastSmoothScroll(container: HTMLElement, element: HTMLElement, position: ScrollLogicalPosition | 'centerOrTop', margin?: number, maxDistance?: number, forceDirection?: FocusDirection, forceDuration?: number, forceNormalContainerHeight?: boolean, onComplete?: ScrollCompleteCallback): void;
172
+
173
+ /**
174
+ * Determines if the specified element or the document/window is scrolled near the bottom.
175
+ *
176
+ * This function checks if the scroll position has reached the bottom of the target element or window
177
+ * within a specified threshold. It is commonly used for implementing "infinite scroll" or detecting
178
+ * when a user has scrolled to the bottom of a page or container.
179
+ *
180
+ * @param target The target element, document, or window to check the scroll position.
181
+ * Can be an `HTMLElement`, `Document`, or `Window`.
182
+ * @param threshold The threshold (in pixels) below which the target is considered "scrolled to the bottom."
183
+ * Default is `10`, meaning the target is considered scrolled to the bottom if it is within 10px of the bottom.
184
+ *
185
+ * @returns `true` if the target is scrolled near the bottom within the given threshold, otherwise `false`.
186
+ *
187
+ * @example
188
+ * // Check if the window is scrolled near the bottom
189
+ * isScrolledToDown(window, 20); // Returns true if within 20px of the bottom of the window.
190
+ *
191
+ * @example
192
+ * // Check if a specific container is scrolled near the bottom
193
+ * const container = document.getElementById('myContainer');
194
+ * isScrolledToDown(container, 50); // Returns true if within 50px of the bottom of the container.
195
+ *
196
+ * @group Scrolling
197
+ */
198
+ declare function isScrolledToDown(target: HTMLElement | Document | Window, threshold?: number): boolean;
199
+
200
+ /**
201
+ * Resets the scroll position of the specified container element.
202
+ *
203
+ * If a `scrollTop` value is provided, the container's scroll position is set
204
+ * to that value. Otherwise, the function will leave the scroll position unchanged.
205
+ * This function is useful for resetting the scroll state in a container, such as
206
+ * when the content changes or after an animation.
207
+ *
208
+ * @param container The container element whose scroll position needs to be reset.
209
+ * @param scrollTop Optional value to set the container's `scrollTop`. If not provided, the scroll position remains unchanged.
210
+ *
211
+ * @example
212
+ * resetScroll(container, 0); // Resets scroll to top of the container.
213
+ * resetScroll(container); // Resets scroll position without changing it.
214
+ *
215
+ * @group Scrolling
216
+ */
217
+ declare function resetScroll(container: HTMLDivElement, scrollTop?: number): void;
218
+
219
+ export { type AnimateNumberProps, FAST_SMOOTH_MAX_DISTANCE, FAST_SMOOTH_MAX_DURATION, FAST_SMOOTH_MIN_DURATION, FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE, FocusDirection, type ScrollCompleteCallback, type TimingFn, animate, animateInstantly, animateNumber, animateSingle, copyTextToClipboard, fastSmoothScroll, isScrolledToDown, resetScroll, timingFunctions };