tiny-essentials 1.16.1 → 1.16.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/dist/v1/TinyAfterScrollWatcher.js +49 -26
- package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
- package/dist/v1/TinyEssentials.js +49 -26
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/libs/TinyAfterScrollWatcher.cjs +49 -26
- package/dist/v1/libs/TinyAfterScrollWatcher.d.mts +17 -14
- package/dist/v1/libs/TinyAfterScrollWatcher.mjs +50 -24
- package/docs/v1/libs/TinyAfterScrollWatcher.md +54 -4
- package/package.json +1 -1
|
@@ -19,8 +19,8 @@ class TinyAfterScrollWatcher {
|
|
|
19
19
|
/** @type {Element|Window} */
|
|
20
20
|
#scrollTarget;
|
|
21
21
|
|
|
22
|
-
/** @type {
|
|
23
|
-
lastScrollTime =
|
|
22
|
+
/** @type {null|NodeJS.Timeout} */
|
|
23
|
+
#lastScrollTime = null;
|
|
24
24
|
|
|
25
25
|
/** @type {FnData[]} */
|
|
26
26
|
#afterScrollQueue = [];
|
|
@@ -31,6 +31,9 @@ class TinyAfterScrollWatcher {
|
|
|
31
31
|
/** @type {Set<OnScrollFunc>} */
|
|
32
32
|
#externalScrollListeners = new Set();
|
|
33
33
|
|
|
34
|
+
/** @type {Set<FnData>} */
|
|
35
|
+
#onStopListeners = new Set();
|
|
36
|
+
|
|
34
37
|
/** @type {boolean} */
|
|
35
38
|
#destroyed = false;
|
|
36
39
|
|
|
@@ -44,16 +47,20 @@ class TinyAfterScrollWatcher {
|
|
|
44
47
|
if (!(scrollTarget instanceof Element) && !(scrollTarget instanceof Window))
|
|
45
48
|
throw new TypeError('scrollTarget must be an Element or the Window object.');
|
|
46
49
|
this.#scrollTarget = scrollTarget;
|
|
50
|
+
this._checkTimer = this._checkTimer.bind(this);
|
|
47
51
|
|
|
48
|
-
this.
|
|
49
|
-
this._checkQueue = this._checkQueue.bind(this);
|
|
50
|
-
|
|
51
|
-
this.#scrollTarget.addEventListener('scroll', this._onScroll);
|
|
52
|
+
this.#scrollTarget.addEventListener('scroll', this._checkTimer);
|
|
52
53
|
this.#inactivityTime = inactivityTime;
|
|
53
|
-
|
|
54
|
-
requestAnimationFrame(this._checkQueue);
|
|
55
54
|
}
|
|
56
55
|
|
|
56
|
+
_checkTimer = () => {
|
|
57
|
+
if (this.#lastScrollTime) clearTimeout(this.#lastScrollTime);
|
|
58
|
+
this.#lastScrollTime = setTimeout(() => {
|
|
59
|
+
this.#lastScrollTime = null;
|
|
60
|
+
this.#checkQueue();
|
|
61
|
+
}, this.#inactivityTime);
|
|
62
|
+
};
|
|
63
|
+
|
|
57
64
|
/**
|
|
58
65
|
* Gets the current inactivity time in milliseconds.
|
|
59
66
|
* @returns {number}
|
|
@@ -74,29 +81,21 @@ class TinyAfterScrollWatcher {
|
|
|
74
81
|
this.#inactivityTime = value;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
/**
|
|
78
|
-
* Internal handler for scroll events.
|
|
79
|
-
* Updates the last scroll timestamp.
|
|
80
|
-
* @private
|
|
81
|
-
*/
|
|
82
|
-
_onScroll() {
|
|
83
|
-
this.lastScrollTime = Date.now();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
84
|
/**
|
|
87
85
|
* Continuously checks whether the user has stopped scrolling,
|
|
88
86
|
* and if so, runs all queued functions.
|
|
89
|
-
* @private
|
|
90
87
|
*/
|
|
91
|
-
|
|
88
|
+
#checkQueue() {
|
|
92
89
|
if (this.#destroyed) return;
|
|
93
|
-
|
|
90
|
+
// Runs all onStop first listeners
|
|
91
|
+
for (const fn of this.#onStopListeners) {
|
|
92
|
+
if (typeof fn === 'function') fn();
|
|
93
|
+
}
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
95
|
+
// Then execute the queue afterScrollQueue
|
|
96
|
+
while (this.#afterScrollQueue.length) {
|
|
97
|
+
const fn = this.#afterScrollQueue.pop();
|
|
98
|
+
if (typeof fn === 'function') fn();
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -113,6 +112,29 @@ class TinyAfterScrollWatcher {
|
|
|
113
112
|
this.#afterScrollQueue.push(fn);
|
|
114
113
|
}
|
|
115
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Registers a function to run once after scrolling has stopped,
|
|
117
|
+
* before any afterScrollQueue functions.
|
|
118
|
+
*
|
|
119
|
+
* @param {FnData} fn - A function to execute after scroll stop.
|
|
120
|
+
* @throws {TypeError} If the argument is not a function.
|
|
121
|
+
*/
|
|
122
|
+
onStop(fn) {
|
|
123
|
+
if (typeof fn !== 'function') throw new TypeError('Argument must be a function.');
|
|
124
|
+
this.#onStopListeners.add(fn);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Removes a previously registered onStop function.
|
|
129
|
+
*
|
|
130
|
+
* @param {FnData} fn - The function to remove.
|
|
131
|
+
* @throws {TypeError} If the argument is not a function.
|
|
132
|
+
*/
|
|
133
|
+
offStop(fn) {
|
|
134
|
+
if (typeof fn !== 'function') throw new TypeError('Argument must be a function.');
|
|
135
|
+
this.#onStopListeners.delete(fn);
|
|
136
|
+
}
|
|
137
|
+
|
|
116
138
|
/**
|
|
117
139
|
* Registers an external scroll listener on the tracked element.
|
|
118
140
|
*
|
|
@@ -146,11 +168,12 @@ class TinyAfterScrollWatcher {
|
|
|
146
168
|
if (this.#destroyed) return;
|
|
147
169
|
this.#destroyed = true;
|
|
148
170
|
|
|
149
|
-
this.#scrollTarget.removeEventListener('scroll', this.
|
|
171
|
+
this.#scrollTarget.removeEventListener('scroll', this._checkTimer);
|
|
150
172
|
for (const fn of this.#externalScrollListeners)
|
|
151
173
|
this.#scrollTarget.removeEventListener('scroll', fn);
|
|
152
174
|
|
|
153
175
|
this.#externalScrollListeners.clear();
|
|
176
|
+
this.#onStopListeners.clear();
|
|
154
177
|
}
|
|
155
178
|
}
|
|
156
179
|
|
|
@@ -29,20 +29,7 @@ declare class TinyAfterScrollWatcher {
|
|
|
29
29
|
* @throws {TypeError} If inactivityTime is not a positive number
|
|
30
30
|
*/
|
|
31
31
|
constructor(scrollTarget?: Element | Window, inactivityTime?: number);
|
|
32
|
-
|
|
33
|
-
lastScrollTime: number;
|
|
34
|
-
/**
|
|
35
|
-
* Internal handler for scroll events.
|
|
36
|
-
* Updates the last scroll timestamp.
|
|
37
|
-
* @private
|
|
38
|
-
*/
|
|
39
|
-
private _onScroll;
|
|
40
|
-
/**
|
|
41
|
-
* Continuously checks whether the user has stopped scrolling,
|
|
42
|
-
* and if so, runs all queued functions.
|
|
43
|
-
* @private
|
|
44
|
-
*/
|
|
45
|
-
private _checkQueue;
|
|
32
|
+
_checkTimer: () => void;
|
|
46
33
|
/**
|
|
47
34
|
* Sets a new inactivity time.
|
|
48
35
|
* Must be a positive number (in milliseconds).
|
|
@@ -63,6 +50,22 @@ declare class TinyAfterScrollWatcher {
|
|
|
63
50
|
* @throws {TypeError} If the argument is not a function.
|
|
64
51
|
*/
|
|
65
52
|
doAfterScroll(fn: () => void): void;
|
|
53
|
+
lastScrollTime: number | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Registers a function to run once after scrolling has stopped,
|
|
56
|
+
* before any afterScrollQueue functions.
|
|
57
|
+
*
|
|
58
|
+
* @param {FnData} fn - A function to execute after scroll stop.
|
|
59
|
+
* @throws {TypeError} If the argument is not a function.
|
|
60
|
+
*/
|
|
61
|
+
onStop(fn: FnData): void;
|
|
62
|
+
/**
|
|
63
|
+
* Removes a previously registered onStop function.
|
|
64
|
+
*
|
|
65
|
+
* @param {FnData} fn - The function to remove.
|
|
66
|
+
* @throws {TypeError} If the argument is not a function.
|
|
67
|
+
*/
|
|
68
|
+
offStop(fn: FnData): void;
|
|
66
69
|
/**
|
|
67
70
|
* Registers an external scroll listener on the tracked element.
|
|
68
71
|
*
|
|
@@ -14,14 +14,16 @@
|
|
|
14
14
|
class TinyAfterScrollWatcher {
|
|
15
15
|
/** @type {Element|Window} */
|
|
16
16
|
#scrollTarget;
|
|
17
|
-
/** @type {
|
|
18
|
-
lastScrollTime =
|
|
17
|
+
/** @type {null|NodeJS.Timeout} */
|
|
18
|
+
#lastScrollTime = null;
|
|
19
19
|
/** @type {FnData[]} */
|
|
20
20
|
#afterScrollQueue = [];
|
|
21
21
|
/** @type {number} */
|
|
22
22
|
#inactivityTime = 100;
|
|
23
23
|
/** @type {Set<OnScrollFunc>} */
|
|
24
24
|
#externalScrollListeners = new Set();
|
|
25
|
+
/** @type {Set<FnData>} */
|
|
26
|
+
#onStopListeners = new Set();
|
|
25
27
|
/** @type {boolean} */
|
|
26
28
|
#destroyed = false;
|
|
27
29
|
/**
|
|
@@ -34,12 +36,18 @@ class TinyAfterScrollWatcher {
|
|
|
34
36
|
if (!(scrollTarget instanceof Element) && !(scrollTarget instanceof Window))
|
|
35
37
|
throw new TypeError('scrollTarget must be an Element or the Window object.');
|
|
36
38
|
this.#scrollTarget = scrollTarget;
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.#scrollTarget.addEventListener('scroll', this._onScroll);
|
|
39
|
+
this._checkTimer = this._checkTimer.bind(this);
|
|
40
|
+
this.#scrollTarget.addEventListener('scroll', this._checkTimer);
|
|
40
41
|
this.#inactivityTime = inactivityTime;
|
|
41
|
-
requestAnimationFrame(this._checkQueue);
|
|
42
42
|
}
|
|
43
|
+
_checkTimer = () => {
|
|
44
|
+
if (this.#lastScrollTime)
|
|
45
|
+
clearTimeout(this.#lastScrollTime);
|
|
46
|
+
this.#lastScrollTime = setTimeout(() => {
|
|
47
|
+
this.#lastScrollTime = null;
|
|
48
|
+
this.#checkQueue();
|
|
49
|
+
}, this.#inactivityTime);
|
|
50
|
+
};
|
|
43
51
|
/**
|
|
44
52
|
* Gets the current inactivity time in milliseconds.
|
|
45
53
|
* @returns {number}
|
|
@@ -58,29 +66,23 @@ class TinyAfterScrollWatcher {
|
|
|
58
66
|
throw new Error('inactivityTime must be a positive number in milliseconds.');
|
|
59
67
|
this.#inactivityTime = value;
|
|
60
68
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Internal handler for scroll events.
|
|
63
|
-
* Updates the last scroll timestamp.
|
|
64
|
-
* @private
|
|
65
|
-
*/
|
|
66
|
-
_onScroll() {
|
|
67
|
-
this.lastScrollTime = Date.now();
|
|
68
|
-
}
|
|
69
69
|
/**
|
|
70
70
|
* Continuously checks whether the user has stopped scrolling,
|
|
71
71
|
* and if so, runs all queued functions.
|
|
72
|
-
* @private
|
|
73
72
|
*/
|
|
74
|
-
|
|
73
|
+
#checkQueue() {
|
|
75
74
|
if (this.#destroyed)
|
|
76
75
|
return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
// Runs all onStop first listeners
|
|
77
|
+
for (const fn of this.#onStopListeners) {
|
|
78
|
+
if (typeof fn === 'function')
|
|
79
|
+
fn();
|
|
80
|
+
}
|
|
81
|
+
// Then execute the queue afterScrollQueue
|
|
82
|
+
while (this.#afterScrollQueue.length) {
|
|
83
|
+
const fn = this.#afterScrollQueue.pop();
|
|
84
|
+
if (typeof fn === 'function')
|
|
85
|
+
fn();
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
/**
|
|
@@ -96,6 +98,29 @@ class TinyAfterScrollWatcher {
|
|
|
96
98
|
this.lastScrollTime = Date.now();
|
|
97
99
|
this.#afterScrollQueue.push(fn);
|
|
98
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Registers a function to run once after scrolling has stopped,
|
|
103
|
+
* before any afterScrollQueue functions.
|
|
104
|
+
*
|
|
105
|
+
* @param {FnData} fn - A function to execute after scroll stop.
|
|
106
|
+
* @throws {TypeError} If the argument is not a function.
|
|
107
|
+
*/
|
|
108
|
+
onStop(fn) {
|
|
109
|
+
if (typeof fn !== 'function')
|
|
110
|
+
throw new TypeError('Argument must be a function.');
|
|
111
|
+
this.#onStopListeners.add(fn);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Removes a previously registered onStop function.
|
|
115
|
+
*
|
|
116
|
+
* @param {FnData} fn - The function to remove.
|
|
117
|
+
* @throws {TypeError} If the argument is not a function.
|
|
118
|
+
*/
|
|
119
|
+
offStop(fn) {
|
|
120
|
+
if (typeof fn !== 'function')
|
|
121
|
+
throw new TypeError('Argument must be a function.');
|
|
122
|
+
this.#onStopListeners.delete(fn);
|
|
123
|
+
}
|
|
99
124
|
/**
|
|
100
125
|
* Registers an external scroll listener on the tracked element.
|
|
101
126
|
*
|
|
@@ -129,10 +154,11 @@ class TinyAfterScrollWatcher {
|
|
|
129
154
|
if (this.#destroyed)
|
|
130
155
|
return;
|
|
131
156
|
this.#destroyed = true;
|
|
132
|
-
this.#scrollTarget.removeEventListener('scroll', this.
|
|
157
|
+
this.#scrollTarget.removeEventListener('scroll', this._checkTimer);
|
|
133
158
|
for (const fn of this.#externalScrollListeners)
|
|
134
159
|
this.#scrollTarget.removeEventListener('scroll', fn);
|
|
135
160
|
this.#externalScrollListeners.clear();
|
|
161
|
+
this.#onStopListeners.clear();
|
|
136
162
|
}
|
|
137
163
|
}
|
|
138
164
|
export default TinyAfterScrollWatcher;
|
|
@@ -10,7 +10,8 @@ Perfect for optimizing logic that should only run **after** the user has finishe
|
|
|
10
10
|
* ✅ Works on any scrollable element or the window
|
|
11
11
|
* ⏱️ Customizable "inactivity timeout" (default: `100ms`)
|
|
12
12
|
* 🧠 Executes queued callbacks after scroll ends
|
|
13
|
-
*
|
|
13
|
+
* 🧷 Add/remove scroll listeners easily (`onScroll` / `offScroll`)
|
|
14
|
+
* 🛑 Execute logic right before after-scroll queue (`onStop`)
|
|
14
15
|
* 🧹 Cleanly destroy the instance when no longer needed
|
|
15
16
|
|
|
16
17
|
---
|
|
@@ -36,6 +37,11 @@ watcher.doAfterScroll(() => {
|
|
|
36
37
|
console.log('Scroll has stopped for 300ms!');
|
|
37
38
|
});
|
|
38
39
|
|
|
40
|
+
// Add a function to run immediately when scroll stops
|
|
41
|
+
watcher.onStop(() => {
|
|
42
|
+
console.log('User just stopped scrolling!');
|
|
43
|
+
});
|
|
44
|
+
|
|
39
45
|
// Add a custom scroll listener
|
|
40
46
|
const myScrollHandler = () => console.log('Scrolling...');
|
|
41
47
|
watcher.onScroll(myScrollHandler);
|
|
@@ -43,6 +49,9 @@ watcher.onScroll(myScrollHandler);
|
|
|
43
49
|
// Remove it later
|
|
44
50
|
watcher.offScroll(myScrollHandler);
|
|
45
51
|
|
|
52
|
+
// Also remove onStop listener
|
|
53
|
+
watcher.offStop(myStopFunction);
|
|
54
|
+
|
|
46
55
|
// Fully destroy the watcher
|
|
47
56
|
watcher.destroy();
|
|
48
57
|
```
|
|
@@ -106,6 +115,46 @@ doAfterScroll(fn: FnData): void
|
|
|
106
115
|
|
|
107
116
|
---
|
|
108
117
|
|
|
118
|
+
### 🛑 Method: `onStop(fn)`
|
|
119
|
+
|
|
120
|
+
Registers a function to be called **immediately after scrolling stops**, but **before** the `doAfterScroll` queue is executed.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
onStop(fn: FnData): void
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Parameters:
|
|
127
|
+
|
|
128
|
+
| Name | Type | Description |
|
|
129
|
+
| ---- | -------- | ----------------------------------------------- |
|
|
130
|
+
| `fn` | `FnData` | Function to call as soon as scroll has stopped. |
|
|
131
|
+
|
|
132
|
+
#### Throws:
|
|
133
|
+
|
|
134
|
+
* `TypeError` if the argument is not a function.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### 🔁 Method: `offStop(fn)`
|
|
139
|
+
|
|
140
|
+
Removes a function previously registered with `onStop`.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
offStop(fn: FnData): void
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Parameters:
|
|
147
|
+
|
|
148
|
+
| Name | Type | Description |
|
|
149
|
+
| ---- | -------- | ---------------------------------- |
|
|
150
|
+
| `fn` | `FnData` | Function to remove from onStop set |
|
|
151
|
+
|
|
152
|
+
#### Throws:
|
|
153
|
+
|
|
154
|
+
* `TypeError` if the argument is not a function.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
109
158
|
### 🧷 Method: `onScroll(fn)`
|
|
110
159
|
|
|
111
160
|
Registers a custom scroll listener on the tracked element.
|
|
@@ -176,6 +225,7 @@ type OnScrollFunc = (ev: Event) => void;
|
|
|
176
225
|
|
|
177
226
|
## 📝 Notes
|
|
178
227
|
|
|
179
|
-
*
|
|
180
|
-
* The
|
|
181
|
-
*
|
|
228
|
+
* `onStop` functions are called **before** the `doAfterScroll` queue.
|
|
229
|
+
* The `doAfterScroll` queue runs in **LIFO** order (last-in, first-out).
|
|
230
|
+
* `requestAnimationFrame` is used for efficient polling.
|
|
231
|
+
* Lower `inactivityTime` makes scroll-stop detection more responsive.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiny-essentials",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"description": "Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "npm run test:mjs && npm run test:cjs && npm run test:js",
|